version 0.7.6

This commit is contained in:
Andreas Rumpf 2009-04-22 15:55:27 +02:00
commit e792940f52
106 changed files with 8394 additions and 10083 deletions

242
cogapp.py
View file

@ -5,13 +5,10 @@
""" """
# $Id: cogapp.py 141 2008-05-22 10:56:43Z nedbat $ # $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 os, re, string, sys, traceback, types, imp, copy, getopt, shlex
import imp from pycompab import *
import copy, getopt, shlex
from cStringIO import StringIO
from string import strip, split, join, replace
__all__ = ['Cog', 'CogUsageError'] __all__ = ['Cog', 'CogUsageError']
@ -37,15 +34,14 @@ OPTIONS:
-s STRING Suffix all generated output lines with STRING. -s STRING Suffix all generated output lines with STRING.
-U Write the output with Unix newlines (only LF line-endings). -U Write the output with Unix newlines (only LF line-endings).
-w CMD Use CMD if the output file needs to be made writable. -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. -x Excise all the generated output without running the generators.
-z The [[[end]]] marker can be omitted, and is assumed at eof. -z The [[[end]]] marker can be omitted, and is assumed at eof.
-v Print the version of cog and exit. -v Print the version of cog and exit.
-h Print this help. -h Print this help.
""" """
# Get True and False right even if they aren't already defined. gErrorMsg = "" # this is needed to support Python 1.5.2 till 3.0
True, False = 0==0, 0==1
# Other package modules # Other package modules
from whiteutils import * from whiteutils import *
@ -54,10 +50,12 @@ class CogError(Exception):
""" Any exception raised by Cog. """ Any exception raised by Cog.
""" """
def __init__(self, msg, file='', line=0): def __init__(self, msg, file='', line=0):
global gErrorMsg
if file: if file:
Exception.__init__(self, "%s(%d): %s" % (file, line, msg)) gErrorMsg = Subs("$1($2): $2", file, line, msg)
else: else:
Exception.__init__(self, msg) gErrorMsg = msg
Exception.__init__(self, gErrorMsg)
class CogUsageError(CogError): class CogUsageError(CogError):
""" An error in usage of command-line arguments in cog. """ An error in usage of command-line arguments in cog.
@ -97,15 +95,15 @@ class CogGenerator(Redirectable):
self.markers = [] self.markers = []
self.lines = [] self.lines = []
def parseMarker(self, l): def parseMarker(self, L):
self.markers.append(l) self.markers.append(L)
def parseLine(self, l): def parseLine(self, L):
s = 0 s = 0
e = len(l) e = len(L)
while s < len(l) and l[s] == '\n': s = s + 1 while s < len(L) and L[s] == '\n': s = s + 1
while e >= 1 and l[e-1] == '\n': e = e - 1 while e >= 1 and L[e-1] == '\n': e = e - 1
self.lines.append(l[s:e+1]) self.lines.append(L[s:e+1])
def getCode(self): def getCode(self):
""" Extract the executable Python code from the generator. """ Extract the executable Python code from the generator.
@ -116,10 +114,10 @@ class CogGenerator(Redirectable):
prefIn = commonPrefix(self.markers + self.lines) prefIn = commonPrefix(self.markers + self.lines)
if prefIn: if prefIn:
tmp = [] 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 self.markers = tmp
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.lines = tmp
#self.markers = [ l.replace(prefIn, '', 1) for l in self.markers ] #self.markers = [ l.replace(prefIn, '', 1) for l in self.markers ]
#self.lines = [ l.replace(prefIn, '', 1) for l in self.lines ] #self.lines = [ l.replace(prefIn, '', 1) for l in self.lines ]
@ -163,7 +161,7 @@ class CogGenerator(Redirectable):
def msg(self, s): def msg(self, s):
self.stdout.write("Message: "+s+"\n") 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. """ The cog.out function.
""" """
if trimblanklines and ('\n' in sOut): if trimblanklines and ('\n' in sOut):
@ -177,7 +175,7 @@ class CogGenerator(Redirectable):
sOut = reindentBlock(sOut) sOut = reindentBlock(sOut)
self.outstring = self.outstring + 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. """ The cog.outl function.
""" """
self.out(sOut, dedent, trimblanklines) self.out(sOut, dedent, trimblanklines)
@ -200,10 +198,10 @@ class NumberedFileReader:
self.n = 0 self.n = 0
def readline(self): def readline(self):
l = self.f.readline() L = self.f.readline()
if l: if L:
self.n = self.n + 1 self.n = self.n + 1
return l return L
def linenumber(self): def linenumber(self):
return self.n return self.n
@ -217,17 +215,17 @@ class CogOptions:
self.args = [] self.args = []
self.includePath = [] self.includePath = []
self.defines = {} self.defines = {}
self.bShowVersion = False self.bShowVersion = false
self.sMakeWritableCmd = None self.sMakeWritableCmd = None
self.bReplace = False self.bReplace = false
self.bNoGenerate = False self.bNoGenerate = false
self.sOutputName = None self.sOutputName = None
self.bWarnEmpty = False self.bWarnEmpty = false
self.bHashOutput = False self.bHashOutput = false
self.bDeleteCode = False self.bDeleteCode = false
self.bEofCanBeEnd = False self.bEofCanBeEnd = false
self.sSuffix = None self.sSuffix = None
self.bNewlines = False self.bNewlines = false
def __cmp__(self, other): def __cmp__(self, other):
""" Comparison operator for tests to use. """ Comparison operator for tests to use.
@ -249,44 +247,44 @@ class CogOptions:
# Parse the command line arguments. # Parse the command line arguments.
try: try:
opts, self.args = getopt.getopt(argv, 'cdD:eI:o:rs:Uvw:xz') opts, self.args = getopt.getopt(argv, 'cdD:eI:o:rs:Uvw:xz')
except getopt.error, msg: except getopt.error:
raise CogUsageError(msg) raise CogUsageError("invalid command line")
# Handle the command line arguments. # Handle the command line arguments.
for o, a in opts: for o, a in opts:
if o == '-c': if o == '-c':
self.bHashOutput = True self.bHashOutput = true
elif o == '-d': elif o == '-d':
self.bDeleteCode = True self.bDeleteCode = true
elif o == '-D': elif o == '-D':
if a.count('=') < 1: if a.count('=') < 1:
raise CogUsageError("-D takes a name=value argument") raise CogUsageError("-D takes a name=value argument")
name, value = split(a, '=', 1) name, value = split(a, '=', 1)
self.defines[name] = value self.defines[name] = value
elif o == '-e': elif o == '-e':
self.bWarnEmpty = True self.bWarnEmpty = true
elif o == '-I': elif o == '-I':
self.addToIncludePath(a) self.addToIncludePath(a)
elif o == '-o': elif o == '-o':
self.sOutputName = a self.sOutputName = a
elif o == '-r': elif o == '-r':
self.bReplace = True self.bReplace = true
elif o == '-s': elif o == '-s':
self.sSuffix = a self.sSuffix = a
elif o == '-U': elif o == '-U':
self.bNewlines = True self.bNewlines = true
elif o == '-v': elif o == '-v':
self.bShowVersion = True self.bShowVersion = true
elif o == '-w': elif o == '-w':
self.sMakeWritableCmd = a self.sMakeWritableCmd = a
elif o == '-x': elif o == '-x':
self.bNoGenerate = True self.bNoGenerate = true
elif o == '-z': elif o == '-z':
self.bEofCanBeEnd = True self.bEofCanBeEnd = true
else: else:
# Since getopt.getopt is given a list of possible flags, # Since getopt.getopt is given a list of possible flags,
# this is an internal error. # 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): def validate(self):
""" Does nothing if everything is OK, raises CogError's if it's not. """ 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: if self.bReplace and self.sOutputName:
raise CogUsageError("Can't use -o with -r (they are opposites)") 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): class Cog(Redirectable):
""" The Cog engine. """ The Cog engine.
""" """
@ -315,7 +304,7 @@ class Cog(Redirectable):
self.sEndSpec = ']]]' self.sEndSpec = ']]]'
self.sEndOutput = '[[[end]]]' self.sEndOutput = '[[[end]]]'
self.reEndOutput = re.compile(r'\[\[\[end]]](?P<hashsect> *\(checksum: (?P<hash>[a-f0-9]+)\))') 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.options = CogOptions()
self.sOutputMode = 'w' self.sOutputMode = 'w'
@ -326,14 +315,14 @@ class Cog(Redirectable):
self.stdout.write("Warning: " + msg + "\n") self.stdout.write("Warning: " + msg + "\n")
def isBeginSpecLine(self, s): def isBeginSpecLine(self, s):
return string.find(s, self.sBeginSpec) >= 0 return find(s, self.sBeginSpec) >= 0
def isEndSpecLine(self, s): def isEndSpecLine(self, s):
return string.find(s, self.sEndSpec) >= 0 and \ return find(s, self.sEndSpec) >= 0 and \
not self.isEndOutputLine(s) not self.isEndOutputLine(s)
def isEndOutputLine(self, s): def isEndOutputLine(self, s):
return string.find(s, self.sEndOutput) >= 0 return find(s, self.sEndOutput) >= 0
def installCogModule(self): def installCogModule(self):
""" Magic mumbo-jumbo so that imported Python modules """ Magic mumbo-jumbo so that imported Python modules
@ -362,7 +351,7 @@ class Cog(Redirectable):
fIn = NumberedFileReader(fIn) fIn = NumberedFileReader(fIn)
bSawCog = False bSawCog = false
self.cogmodule.inFile = sFileIn self.cogmodule.inFile = sFileIn
self.cogmodule.outFile = sFileOut self.cogmodule.outFile = sFileOut
@ -375,102 +364,102 @@ class Cog(Redirectable):
globals.update(self.options.defines) globals.update(self.options.defines)
# loop over generator chunks # loop over generator chunks
l = fIn.readline() L = fIn.readline()
while l: while L:
# Find the next spec begin # Find the next spec begin
while l and not self.isBeginSpecLine(l): while L and not self.isBeginSpecLine(L):
if self.isEndSpecLine(l): if self.isEndSpecLine(L):
raise CogError("Unexpected '%s'" % self.sEndSpec, raise CogError(Subs("Unexpected '$1'", self.sEndSpec),
file=sFileIn, line=fIn.linenumber()) file=sFileIn, line=fIn.linenumber())
if self.isEndOutputLine(l): if self.isEndOutputLine(L):
raise CogError("Unexpected '%s'" % self.sEndOutput, raise CogError(Subs("Unexpected '$1'", self.sEndOutput),
file=sFileIn, line=fIn.linenumber()) file=sFileIn, line=fIn.linenumber())
fOut.write(l) fOut.write(L)
l = fIn.readline() L = fIn.readline()
if not l: if not L:
break break
if not self.options.bDeleteCode: if not self.options.bDeleteCode:
fOut.write(l) fOut.write(L)
# l is the begin spec # L is the begin spec
gen = CogGenerator() gen = CogGenerator()
gen.setOutput(stdout=self.stdout) gen.setOutput(stdout=self.stdout)
gen.parseMarker(l) gen.parseMarker(L)
firstLineNum = fIn.linenumber() firstLineNum = fIn.linenumber()
self.cogmodule.firstLineNum = firstLineNum self.cogmodule.firstLineNum = firstLineNum
# If the spec begin is also a spec end, then process the single # If the spec begin is also a spec end, then process the single
# line of code inside. # line of code inside.
if self.isEndSpecLine(l): if self.isEndSpecLine(L):
beg = string.find(l, self.sBeginSpec) beg = find(L, self.sBeginSpec)
end = string.find(l, self.sEndSpec) end = find(L, self.sEndSpec)
if beg > end: if beg > end:
raise CogError("Cog code markers inverted", raise CogError("Cog code markers inverted",
file=sFileIn, line=firstLineNum) file=sFileIn, line=firstLineNum)
else: else:
sCode = strip(l[beg+len(self.sBeginSpec):end]) sCode = strip(L[beg+len(self.sBeginSpec):end])
gen.parseLine(sCode) gen.parseLine(sCode)
else: else:
# Deal with an ordinary code block. # Deal with an ordinary code block.
l = fIn.readline() L = fIn.readline()
# Get all the lines in the spec # Get all the lines in the spec
while l and not self.isEndSpecLine(l): while L and not self.isEndSpecLine(L):
if self.isBeginSpecLine(l): if self.isBeginSpecLine(L):
raise CogError("Unexpected '%s'" % self.sBeginSpec, raise CogError(Subs("Unexpected '$1'", self.sBeginSpec),
file=sFileIn, line=fIn.linenumber()) file=sFileIn, line=fIn.linenumber())
if self.isEndOutputLine(l): if self.isEndOutputLine(L):
raise CogError("Unexpected '%s'" % self.sEndOutput, raise CogError(Subs("Unexpected '$1'", self.sEndOutput),
file=sFileIn, line=fIn.linenumber()) file=sFileIn, line=fIn.linenumber())
if not self.options.bDeleteCode: if not self.options.bDeleteCode:
fOut.write(l) fOut.write(L)
gen.parseLine(l) gen.parseLine(L)
l = fIn.readline() L = fIn.readline()
if not l: if not L:
raise CogError( raise CogError(
"Cog block begun but never ended.", "Cog block begun but never ended.",
file=sFileIn, line=firstLineNum) file=sFileIn, line=firstLineNum)
if not self.options.bDeleteCode: if not self.options.bDeleteCode:
fOut.write(l) fOut.write(L)
gen.parseMarker(l) gen.parseMarker(L)
l = fIn.readline() L = fIn.readline()
# Eat all the lines in the output section. While reading past # Eat all the lines in the output section. While reading past
# them, compute the md5 hash of the old output. # them, compute the md5 hash of the old output.
hasher = md5.new() hasher = newMD5()
while l and not self.isEndOutputLine(l): while L and not self.isEndOutputLine(L):
if self.isBeginSpecLine(l): if self.isBeginSpecLine(L):
raise CogError("Unexpected '%s'" % self.sBeginSpec, raise CogError(Subs("Unexpected '$1'", self.sBeginSpec),
file=sFileIn, line=fIn.linenumber()) file=sFileIn, line=fIn.linenumber())
if self.isEndSpecLine(l): if self.isEndSpecLine(L):
raise CogError("Unexpected '%s'" % self.sEndSpec, raise CogError(Subs("Unexpected '$1'", self.sEndSpec),
file=sFileIn, line=fIn.linenumber()) file=sFileIn, line=fIn.linenumber())
hasher.update(l) MD5update(hasher, L)
l = fIn.readline() L = fIn.readline()
curHash = mydigest(hasher) 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. # 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()) file=sFileIn, line=fIn.linenumber())
# Write the output of the spec to be the new output if we're # Write the output of the spec to be the new output if we're
# supposed to generate code. # supposed to generate code.
hasher = md5.new() hasher = newMD5()
if not self.options.bNoGenerate: 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 = gen.evaluate(cog=self, globals=globals, fname=sFile)
sGen = self.suffixLines(sGen) sGen = self.suffixLines(sGen)
hasher.update(sGen) MD5update(hasher, sGen)
fOut.write(sGen) fOut.write(sGen)
newHash = mydigest(hasher) newHash = mydigest(hasher)
bSawCog = True bSawCog = true
# Write the ending output line # Write the ending output line
hashMatch = self.reEndOutput.search(l) hashMatch = self.reEndOutput.search(L)
if self.options.bHashOutput: if self.options.bHashOutput:
if hashMatch: if hashMatch:
oldHash = hashMatch.groupdict()['hash'] oldHash = hashMatch.groupdict()['hash']
@ -478,23 +467,23 @@ class Cog(Redirectable):
raise CogError("Output has been edited! Delete old checksum to unprotect.", raise CogError("Output has been edited! Delete old checksum to unprotect.",
file=sFileIn, line=fIn.linenumber()) file=sFileIn, line=fIn.linenumber())
# Create a new end line with the correct hash. # Create a new end line with the correct hash.
endpieces = split(l, hashMatch.group(0), 1) endpieces = split(L, hashMatch.group(0), 1)
else: else:
# There was no old hash, but we want a new hash. # There was no old hash, but we want a new hash.
endpieces = split(l, self.sEndOutput, 1) endpieces = split(L, self.sEndOutput, 1)
l = join(endpieces, (self.sEndFormat % newHash)) L = join(endpieces, Subs(self.sEndFormat, newHash))
else: else:
# We don't want hashes output, so if there was one, get rid of # We don't want hashes output, so if there was one, get rid of
# it. # it.
if hashMatch: if hashMatch:
l = replace(l, hashMatch.groupdict()['hashsect'], '', 1) L = replace(L, hashMatch.groupdict()['hashsect'], '', 1)
if not self.options.bDeleteCode: if not self.options.bDeleteCode:
fOut.write(l) fOut.write(L)
l = fIn.readline() L = fIn.readline()
if not bSawCog and self.options.bWarnEmpty: 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. # A regex for non-empty lines, used by suffixLines.
reNonEmptyLines = re.compile("^\s*\S+.*$", re.MULTILINE) reNonEmptyLines = re.compile("^\s*\S+.*$", re.MULTILINE)
@ -525,13 +514,13 @@ class Cog(Redirectable):
# Need to ensure we can write. # Need to ensure we can write.
if self.options.sMakeWritableCmd: if self.options.sMakeWritableCmd:
# Use an external command to make the file writable. # 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()) self.stdout.write(os.popen(cmd).read())
if not os.access(sOldPath, os.W_OK): 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: else:
# Can't write! # Can't write!
raise CogError("Can't overwrite %s" % sOldPath) raise CogError("Can't overwrite " + sOldPath)
f = open(sOldPath, self.sOutputMode) f = open(sOldPath, self.sOutputMode)
f.write(sNewText) f.write(sNewText)
f.close() f.close()
@ -573,8 +562,8 @@ class Cog(Redirectable):
elif self.options.bReplace: elif self.options.bReplace:
# We want to replace the cog file with the output, # We want to replace the cog file with the output,
# but only if they differ. # but only if they differ.
self.stdout.write("Cogging %s" % sFile) self.stdout.write("Cogging " + sFile)
bNeedNewline = True bNeedNewline = true
try: try:
fOldFile = open(sFile) fOldFile = open(sFile)
@ -583,7 +572,7 @@ class Cog(Redirectable):
sNewText = self.processString(sOldText, fname=sFile) sNewText = self.processString(sOldText, fname=sFile)
if sOldText != sNewText: if sOldText != sNewText:
self.stdout.write(" (changed)\n") self.stdout.write(" (changed)\n")
bNeedNewline = False bNeedNewline = false
self.replaceFile(sFile, sNewText) self.replaceFile(sFile, sNewText)
finally: finally:
# The try-finally block is so we can print a partial line # The try-finally block is so we can print a partial line
@ -600,10 +589,10 @@ class Cog(Redirectable):
def processFileList(self, sFileList): def processFileList(self, sFileList):
""" Process the files in a file list. """ 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. # Use shlex to parse the line like a shell.
lex = shlex.shlex(l, posix=True) lex = shlex.shlex(L, posix=true)
lex.whitespace_split = True lex.whitespace_split = true
lex.commenters = '#' lex.commenters = '#'
# No escapes, so that backslash can be part of the path # No escapes, so that backslash can be part of the path
lex.escape = '' lex.escape = ''
@ -645,7 +634,7 @@ class Cog(Redirectable):
self.options.validate() self.options.validate()
if self.options.bShowVersion: if self.options.bShowVersion:
self.stdout.write("Cog version %s\n" % __version__) self.stdout.write(Subs("Cog version $1\n", __version__))
return return
if self.options.args: if self.options.args:
@ -657,19 +646,20 @@ class Cog(Redirectable):
def main(self, argv): def main(self, argv):
""" Handle the command-line execution for cog. """ Handle the command-line execution for cog.
""" """
global gErrorMsg
try: try:
self.callableMain(argv) self.callableMain(argv)
return 0 return 0
except CogUsageError, err: except CogUsageError:
self.stderr.write(err + "\n") self.stderr.write(gErrorMsg + "\n")
self.stderr.write("(for help use -?)\n") self.stderr.write("(for help use -?)\n")
return 2 return 2
except CogGeneratedError, err: except CogGeneratedError:
self.stderr.write("Error: %s\n" % err) self.stderr.write(Subs("Error: $1\n", gErrorMsg))
return 3 return 3
except CogError, err: except CogError:
self.stderr.write(err + "\n") self.stderr.write(gErrorMsg + "\n")
return 1 return 1
except: except:
traceback.print_exc(None, self.stderr) traceback.print_exc(None, self.stderr)

View file

@ -1,5 +1,5 @@
# This is the config file for the documentation generator. # 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. # Feel free to edit the templates as you need.
split.item.toc = "20" split.item.toc = "20"

View file

@ -1,5 +1,5 @@
# Configuration file for the Nimrod Compiler. # Configuration file for the Nimrod Compiler.
# (c) 2008 Andreas Rumpf # (c) 2009 Andreas Rumpf
# Feel free to edit the default values as you need. # Feel free to edit the default values as you need.
@ -7,7 +7,7 @@
# @putenv "key" "val" # @putenv "key" "val"
# Environment variables cannot be used in the options, however! # Environment variables cannot be used in the options, however!
cc = @if windows: llvm_gcc @else: gcc @end cc = gcc
lib="$nimrod/lib" lib="$nimrod/lib"
path="$lib/base" path="$lib/base"
path="$lib/base/gtk" path="$lib/base/gtk"
@ -68,10 +68,8 @@ vcc.options.size = "/O1"
# Configuration for the GNU C/C++ compiler: # Configuration for the GNU C/C++ compiler:
@if windows: @if windows:
gcc.path = r"C:\eigenes\compiler\mingw\bin" gcc.path = r"$nimrod\dist\mingw\bin"
@end @end
#gcc.exe = "gcc-4.1"
#gcc.linkerExe = "gcc-4.1"
gcc.options.debug = "-g" gcc.options.debug = "-g"
@if macosx: @if macosx:
gcc.options.always = "-w -fasm-blocks" gcc.options.always = "-w -fasm-blocks"
@ -83,13 +81,8 @@ gcc.options.size = "-Os"
# Configuration for the Digital Mars C/C++ compiler: # Configuration for the Digital Mars C/C++ compiler:
@if windows: @if windows:
dmc.path = r"C:\eigenes\compiler\d\dm\bin" dmc.path = r"$nimrod\dist\dm\bin"
@end @end
# Configuration for the Tiny C Compiler: # 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" tcc.options.always = "-w"

View file

@ -27,7 +27,6 @@ Advanced options:
--checkpoints:on|off turn on|off checkpoints; for debugging Nimrod --checkpoints:on|off turn on|off checkpoints; for debugging Nimrod
--skip_cfg do not read the general configuration file --skip_cfg do not read the general configuration file
--skip_proj_cfg do not read the project's 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 --index:FILE use FILE to generate a documenation index file
--putenv:key=value set an environment variable --putenv:key=value set an environment variable
--list_cmd list the commands used to execute external programs --list_cmd list the commands used to execute external programs

View file

@ -212,7 +212,6 @@
'nkFromStmt', # a from * import statement 'nkFromStmt', # a from * import statement
'nkImportAs', # an `import xyx as abc` section 'nkImportAs', # an `import xyx as abc` section
'nkIncludeStmt', # an include statement 'nkIncludeStmt', # an include statement
'nkAccessStmt', # used internally for iterators
'nkCommentStmt', # a comment statement 'nkCommentStmt', # a comment statement
'nkStmtListExpr', # a statement list followed by an expr; this is used 'nkStmtListExpr', # a statement list followed by an expr; this is used
# to allow powerful multi-line templates # to allow powerful multi-line templates

View file

@ -19,14 +19,14 @@ Options:
--debugger:on|off turn Embedded Nimrod Debugger ON|OFF --debugger:on|off turn Embedded Nimrod Debugger ON|OFF
-x, --checks:on|off code generation for all runtime checks 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 --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 --range_checks:on|off code generation for range checks ON|OFF
--bound_checks:on|off code generation for bound 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 --overflow_checks:on|off code generation for over-/underflow checks ON|OFF
-a, --assertions:on|off code generation for assertions ON|OFF -a, --assertions:on|off code generation for assertions ON|OFF
--dead_code_elim:on|off whole program dead code elimination 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 --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 -r, --run run the compiled program with given arguments
--advanced show advanced command line switches --advanced show advanced command line switches
-h, --help show this help -h, --help show this help

View file

@ -1,5 +1,5 @@
# This file contains all the messages of the Nimrod compiler # This file contains all the messages of the Nimrod compiler
# (c) 2008 Andreas Rumpf # (c) 2009 Andreas Rumpf
[ [
# fatal errors: # fatal errors:
@ -208,7 +208,7 @@
{'errXNotAllowedHere': '$1 here not allowed'}, {'errXNotAllowedHere': '$1 here not allowed'},
{'errInvalidControlFlowX': 'invalid control flow: $1'}, {'errInvalidControlFlowX': 'invalid control flow: $1'},
{'errATypeHasNoValue': 'a type has no value'}, {'errATypeHasNoValue': 'a type has no value'},
{'errXisNoType': "'$1' is no type"}, {'errXisNoType': "invalid type: '$1'"},
{'errCircumNeedsPointer': "'^' needs a pointer or reference type"}, {'errCircumNeedsPointer': "'^' needs a pointer or reference type"},
{'errInvalidContextForBuiltinX': "invalid context for builtin '$1'"}, {'errInvalidContextForBuiltinX': "invalid context for builtin '$1'"},
{'errInvalidExpression': 'invalid expression'}, {'errInvalidExpression': 'invalid expression'},
@ -254,6 +254,7 @@
{'errXRequiresOneArgument': "converter requires one parameter"}, {'errXRequiresOneArgument': "converter requires one parameter"},
{'errUnhandledExceptionX': "unhandled exception: $1"}, {'errUnhandledExceptionX': "unhandled exception: $1"},
{'errCyclicTree': "macro returned a cyclic abstract syntax tree"}, {'errCyclicTree': "macro returned a cyclic abstract syntax tree"},
{'errXisNoMacroOrTemplate': "'$1' is no macro or template"},
# user error message: # user error message:
{'errUser': '$1'}, {'errUser': '$1'},

View file

@ -1,28 +1,25 @@
"Incorrect documentation is often worse than no documentation." Incorrect documentation is often worse than no documentation.
-- Bertrand Meyer -- Bertrand Meyer
The documentation consists of several documents: 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. | 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. | The Nimrod tutorial part two deals with the advanced language constructs.
- | `Nimrod manual <manual.html>`_ - | `Library documentation <lib.html>`_
| The Nimrod manual is a draft that will evolve into a proper specification. | 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 | The user guide lists command line arguments, special features of the
compiler, etc. compiler, etc.
- | `User guide for the Embedded Nimrod Debugger <endb.html>`_ - | `Manual <manual.html>`_
| This document describes how to use the Embedded Debugger. | The Nimrod manual is a draft that will evolve into a proper specification.
- | `Nimrod library documentation <lib.html>`_ - | `Internal documentation <intern.html>`_
| This document describes Nimrod's standard library.
- | `Nimrod internal documentation <intern.html>`_
| The internal documentation describes how the compiler is implemented. Read | The internal documentation describes how the compiler is implemented. Read
this if you want to hack the compiler. this if you want to hack the compiler.

View file

@ -5,7 +5,7 @@ Short description of Nimrod's modules
Module Description Module Description
============== ========================================================== ============== ==========================================================
nimrod main module: parses the command line and calls nimrod main module: parses the command line and calls
```main.MainCommand`` ``main.MainCommand``
main implements the top-level command dispatching main implements the top-level command dispatching
lexbase buffer handling of the lexical analyser lexbase buffer handling of the lexical analyser
scanner 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 semexprs contains the semantic checking phase for expressions
semstmts contains the semantic checking phase for statements semstmts contains the semantic checking phase for statements
semtypes contains the semantic checking phase for types semtypes contains the semantic checking phase for types
seminst instantiation of generic procs and types
semfold contains code to deal with constant folding semfold contains code to deal with constant folding
evals contains an AST interpreter for compile time evaluation evals contains an AST interpreter for compile time evaluation
pragmas semantic checking of pragmas pragmas semantic checking of pragmas

View file

@ -118,7 +118,7 @@ ifStmt ::= 'if' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
whenStmt ::= 'when' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt] whenStmt ::= 'when' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
caseStmt ::= 'case' expr [':'] ('of' sliceExprList ':' stmt)* caseStmt ::= 'case' expr [':'] ('of' sliceExprList ':' stmt)*
('elif' expr ':' stmt)* ('elif' expr ':' stmt)*
['else' ':' stmt] ['else' ':' stmt]
whileStmt ::= 'while' expr ':' stmt whileStmt ::= 'while' expr ':' stmt
forStmt ::= 'for' symbol (comma symbol)* 'in' expr ['..' expr] ':' stmt forStmt ::= 'for' symbol (comma symbol)* 'in' expr ['..' expr] ':' stmt
exceptList ::= [qualifiedIdent (comma qualifiedIdent)*] exceptList ::= [qualifiedIdent (comma qualifiedIdent)*]

View file

@ -6,12 +6,15 @@ Nimrod Standard Library
:Version: |nimrodversion| :Version: |nimrodversion|
Though the Nimrod Standard Library is still evolving, it is already quite Though the Nimrod Standard Library is still evolving, it is already quite
usable. It is divided into basic libraries that contains modules that virtually usable. It is divided into *pure libraries*, *impure libraries* and *wrappers*.
every program will need and advanced libraries which are more heavy weight.
Advanced libraries are in the ``lib/base`` directory.
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>`_ * `system <system.html>`_
Basic procs and operators that every program needs. It also provides IO 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 literals, raw string literals and triple quote string literals are supported
as in the Nimrod programming language. 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>`_ * `strtabs <strtabs.html>`_
The ``strtabs`` module implements an efficient hash table that is a mapping The ``strtabs`` module implements an efficient hash table that is a mapping
from strings to strings. Supports a case-sensitive, case-insensitive and from strings to strings. Supports a case-sensitive, case-insensitive and
@ -68,17 +77,31 @@ Basic libraries
Nimrod types. Nimrod types.
* `lexbase <lexbase.html>`_ * `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. 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>`_ * `regexprs <regexprs.html>`_
This module contains procedures and operators for handling regular This module contains procedures and operators for handling regular
expressions. expressions.
* `md5 <md5.html>`_
This module implements the MD5 checksum algorithm.
Impure libraries
================
* `dialogs <dialogs.html>`_ * `dialogs <dialogs.html>`_
This module implements portable dialogs for Nimrod; the implementation This module implements portable dialogs for Nimrod; the implementation
builds on the GTK interface. On Windows, native dialogs are shown if builds on the GTK interface. On Windows, native dialogs are shown if
@ -87,6 +110,11 @@ Advanced libaries
* `zipfiles <zipfiles.html>`_ * `zipfiles <zipfiles.html>`_
This module implements a zip archive creator/reader/modifier. 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 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. Contains a wrapper for the POSIX standard.
* `windows <windows.html>`_ * `windows <windows.html>`_
Contains a wrapper for the Win32 API. 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>`_ * `shellapi <shellapi.html>`_
Contains a wrapper for the ``shellapi.h`` header. Contains a wrapper for the ``shellapi.h`` header.
* `shfolder <shfolder.html>`_ * `shfolder <shfolder.html>`_

View file

@ -1011,8 +1011,8 @@ char '\0'
bool false bool false
ref or pointer type nil ref or pointer type nil
procedural type nil procedural type nil
sequence nil (**not** ``@[]``) sequence nil (*not* ``@[]``)
string nil (**not** "") string nil (*not* "")
tuple[x: A, y: B, ...] (default(A), default(B), ...) tuple[x: A, y: B, ...] (default(A), default(B), ...)
(analogous for objects) (analogous for objects)
array[0..., T] [default(T), ...] array[0..., T] [default(T), ...]
@ -1080,9 +1080,9 @@ Case statement
Syntax:: Syntax::
caseStmt ::= 'case' expr ('of' sliceExprList ':' stmt)* caseStmt ::= 'case' expr [':'] ('of' sliceExprList ':' stmt)*
('elif' expr ':' stmt)* ('elif' expr ':' stmt)*
['else' ':' stmt] ['else' ':' stmt]
Example: Example:
@ -1241,11 +1241,11 @@ sugar for:
``return`` without an expression is a short notation for ``return result`` if ``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 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 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 .. code-block:: nimrod
proc returnZero(): int = proc returnZero(): int =
# implicitely returns 0 # implicitely returns 0
Yield statement Yield statement
@ -1649,7 +1649,7 @@ possible within a single ``type`` section.
Generics 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: Example:

View file

@ -208,8 +208,9 @@ Acyclic Pragma
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
The `acyclic`:idx: pragma can be used for object types to mark them as acyclic 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 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 type
PNode = ref TNode PNode = ref TNode
TNode {.acyclic, final.} = object TNode {.acyclic, final.} = object

File diff suppressed because it is too large Load diff

View file

@ -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 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 syntax: Statements which are not indented are executed when the program
starts. Indentation is Nimrod's way of grouping statements. String literals starts. Indentation is Nimrod's way of grouping statements. Indentation is
are enclosed in double quotes. The ``var`` statement declares a new variable done with spaces only, tabulators are not allowed.
named ``name`` of type ``string`` with the value that is returned by the
``readline`` procedure. Since the compiler knows that ``readline`` returns String literals are enclosed in double quotes. The ``var`` statement declares
a string, you can leave out the type in the declaration (this is called a new variable named ``name`` of type ``string`` with the value that is
`local type inference`:idx:). So this will work too: 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 .. code-block:: Nimrod
var name = readline(stdin) 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: *insignificant* in Nimrod and even underscores are ignored:
``This_is_an_identifier`` and this is the same identifier ``This_is_an_identifier`` and this is the same identifier
``ThisIsAnIdentifier``. This feature enables you to use other ``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 yours. It also frees you from remembering the exact spelling of an identifier
(was it ``parseURL`` or ``parseUrl`` or ``parse_URL``?). (was it ``parseURL`` or ``parseUrl`` or ``parse_URL``?).
@ -129,6 +131,9 @@ the syntax, watch their indentation:
Echo("Hi!") Echo("Hi!")
# comment has not the right indentation -> syntax error! # 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 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). 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: 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``, ``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. alone does not produce an octal.
@ -428,6 +433,10 @@ the ``#ifdef`` construct in the C programming language.
**Note**: The documentation generator currently always follows the first branch **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 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 Assignment, procedure calls or the ``return`` statement belong to the simple
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
contain other statements. To avoid ambiguities, complex statements always have 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 .. code-block:: nimrod
# no indentation needed for single assignment statement: # 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 .. code-block:: nimrod
return 42 return 42
.. code-block:: nimrod
result = 42 result = 42
return return
.. code-block:: nimrod
result = 42 result = 42
return result return result
@ -880,7 +891,7 @@ In Nimrod new types can be defined within a ``type`` statement:
.. code-block:: nimrod .. code-block:: nimrod
type type
biggestInt = int64 # biggest integer type that is available 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 Enumeration and object types cannot be defined on the fly, but only within a
``type`` statement. ``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. as high performance bit vectors.
Sets can be constructed via the set constructor: ``{}`` is the empty set. The 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): can also be used to include elements (and ranges of elements):
.. code-block:: nimrod .. 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 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. 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. 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
--------- ---------
`Sequences`:idx: are similar to arrays but of dynamic length which may change `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. 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 Tuples
------ ------
@ -1271,8 +1284,19 @@ with an asterisk (``*``) are exported:
# multiply two int sequences: # multiply two int sequences:
for i in 0..len(a)-1: result[i] = a[i] * b[i] 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 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, Modules that depend on each other are possible, but strongly discouraged,
because then one module cannot be reused without the other. because then one module cannot be reused without the other.

View file

@ -33,7 +33,8 @@ Object Oriented Programming
While Nimrod's support for object oriented programming (OOP) is minimalistic, 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 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 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 Objects
@ -196,8 +197,8 @@ for any type:
stdout.writeln("Hallo") # the same as write(stdout, "Hallo") stdout.writeln("Hallo") # the same as write(stdout, "Hallo")
If it gives you warm fuzzy feelings, you can even write ``1.`+`(2)`` instead of 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 ``1 + 2`` and claim that Nimrod is a pure object oriented language. (But
would not even be lying: *pure OO* has no meaning anyway. :-) that's not true. :-)
Properties Properties
@ -258,20 +259,24 @@ The example is silly, since a vector is better modelled by a tuple which
already provides ``v[]`` access. already provides ``v[]`` access.
Dynamic binding Dynamic dispatch
--------------- ----------------
In Nimrod procedural types are used to implement dynamic binding. The following In Nimrod procedural types are used to implement dynamic dispatch. The
example also shows some more conventions: The ``self`` or ``this`` object following example also shows some more conventions: The ``self`` or ``this``
is named ``my`` (because it is shorter than the alternatives), each class object is named ``my`` (because it is shorter than the alternatives), each
provides a constructor, etc. class provides a constructor, etc.
.. code-block:: nimrod .. code-block:: nimrod
type type
TFigure = object of TObject # abstract base class: TFigure = object of TObject # abstract base class:
draw: proc (my: var TFigure) # concrete classes implement this proc fDraw: proc (my: var TFigure) # concrete classes implement this proc
proc init(f: var TFigure) = proc init(f: var TFigure) =
f.draw = nil f.fDraw = nil
proc draw(f: var TFigure) =
# ``draw`` dispatches dynamically:
f.fDraw(f)
type type
TCircle = object of TFigure TCircle = object of TFigure
@ -282,7 +287,7 @@ provides a constructor, etc.
proc init(my: var TCircle) = proc init(my: var TCircle) =
init(TFigure(my)) # call base constructor init(TFigure(my)) # call base constructor
my.radius = 5 my.radius = 5
my.draw = drawCircle my.fdraw = drawCircle
type type
TRectangle = object of TFigure TRectangle = object of TFigure
@ -294,50 +299,7 @@ provides a constructor, etc.
init(TFigure(my)) # call base constructor init(TFigure(my)) # call base constructor
my.width = 5 my.width = 5
my.height = 10 my.height = 10
my.draw = drawRectangle my.fdraw = 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("[]")
# now use these classes: # now use these classes:
var var
@ -348,10 +310,12 @@ is not necessarily bad. But if you want to eliminate the somewhat redundant
r.draw() r.draw()
c.draw() c.draw()
The example also introduces `lambda`:idx: expressions: A ``lambda`` expression The code uses a ``draw`` procedure that is bound statically, but inside it
defines a new proc with the ``closure`` calling convention on the fly. 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 Exceptions
@ -432,7 +396,7 @@ is not executed (if an exception occurs).
Generics 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 `Generics`:idx: are Nimrod's means to parametrize procs, iterators or types
with `type parameters`:idx:. They are most useful for efficient type safe with `type parameters`:idx:. They are most useful for efficient type safe
@ -458,8 +422,8 @@ containers:
else: else:
var it = root var it = root
while it != nil: while it != nil:
# compare the data items; uses the generic ``cmd`` proc that works for # compare the data items; uses the generic ``cmd`` proc
# any type that has a ``==`` and ``<`` operator # that works for any type that has a ``==`` and ``<`` operator
var c = cmp(it.data, n.data) var c = cmp(it.data, n.data)
if c < 0: if c < 0:
if it.le == nil: if it.le == nil:
@ -491,7 +455,7 @@ containers:
var var
root: PBinaryTree[string] # instantiate a PBinaryTree with ``string`` 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 add(root, "world") # instantiates the second ``add`` proc
for str in preorder(root): for str in preorder(root):
stdout.writeln(str) 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*), The "types" of templates can be the symbols ``expr`` (stands for *expression*),
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type ``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
description*). These are no real types, they just help the compiler parsing. 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 The template body does not open a new scope. To open a new scope
use a ``block`` statement: use a ``block`` statement:
@ -587,6 +552,35 @@ use a ``block`` statement:
b = 42 # does not work, `b` is unknown 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 Macros
====== ======
@ -658,36 +652,6 @@ The macro call expands to:
writeln(stdout, x) 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 Statement Macros
---------------- ----------------

View file

@ -22,6 +22,8 @@ type
menu: PGtkMenuBar menu: PGtkMenuBar
notebook: PGtkNotebook notebook: PGtkNotebook
tabname: int # used for generating tab names tabname: int # used for generating tab names
tabs: seq[PTab]
currTab: int
PEditor = ptr TEditor PEditor = ptr TEditor
proc on_window_destroy(obj: PGtkObject, event: PGdkEvent, 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 if tab.hbox == v: return i
inc(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.} = proc OnCloseTab(button: PGtkButton, tab: PTab) {.cdecl.} =
var idx = getTabIndex(tab.e, tab) var idx = -1
if idx >= 0: gtk_notebook_remove_page(tab.e.notebook, idx) 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) = proc createTab(e: PEditor, filename: string, untitled: bool) =
var t = cast[PTab](alloc0(sizeof(TTab))) var t = cast[PTab](alloc0(sizeof(TTab)))
@ -78,8 +121,22 @@ proc createTab(e: PEditor, filename: string, untitled: bool) =
gtk_widget_show(lab) gtk_widget_show(lab)
var idx = gtk_notebook_append_page(e.notebook, scroll, t.hbox) 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) 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.} = proc on_new_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
inc(e.tabname) inc(e.tabname)
createTab(e, "untitled-" & $e.tabname, true) createTab(e, "untitled-" & $e.tabname, true)
@ -91,6 +148,8 @@ proc main(e: PEditor) =
e.window = GTK_WINDOW(glade_xml_get_widget(builder, "window")) e.window = GTK_WINDOW(glade_xml_get_widget(builder, "window"))
e.statusbar = GTK_STATUSBAR(glade_xml_get_widget(builder, "statusbar")) e.statusbar = GTK_STATUSBAR(glade_xml_get_widget(builder, "statusbar"))
e.notebook = GTK_NOTEBOOK(glade_xml_get_widget(builder, "notebook")) e.notebook = GTK_NOTEBOOK(glade_xml_get_widget(builder, "notebook"))
e.tabs = @[]
e.currTab = -1
setHomogeneous(e.notebook^, 1) setHomogeneous(e.notebook^, 1)
# connect the signal handlers: # connect the signal handlers:
@ -108,6 +167,18 @@ proc main(e: PEditor) =
discard g_signal_connect(quitItem, "activate", discard g_signal_connect(quitItem, "activate",
G_CALLBACK(on_window_destroy), e) 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_window_set_default_icon_name(GTK_STOCK_EDIT)
gtk_widget_show(e.window) gtk_widget_show(e.window)
gtk_main() gtk_main()

219
koch.py
View file

@ -3,26 +3,19 @@
########################################################################## ##########################################################################
## ## ## ##
## Build script of the Nimrod Compiler ## ## 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 import sys, os, os.path, re, shutil, time, getopt, glob, zlib, pickle
from string import split, replace, lower, join, find, strip from pycompab import *
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
# --------------------- constants ---------------------------------------- # --------------------- constants ----------------------------------------
NIMROD_VERSION = '0.7.4' NIMROD_VERSION = '0.7.6'
# This string contains Nimrod's version. It is the only place # This string contains Nimrod's version. It is the only place
# where the version needs to be updated. The rest is done by # where the version needs to be updated. The rest is done by
# the build process automatically. It is replaced **everywhere** # the build process automatically. It is replaced **everywhere**!
# automatically!
# Format is: Major.Minor.Patch # Format is: Major.Minor.Patch
# Major part: plan is to use number 1 for the first version that is stable; # Major part: plan is to use number 1 for the first version that is stable;
# higher versions may be incompatible with previous versions # higher versions may be incompatible with previous versions
@ -30,39 +23,39 @@ NIMROD_VERSION = '0.7.4'
# backwards-compatible) # backwards-compatible)
# Patch level: is increased for every patch # Patch level: is increased for every patch
EXPLAIN = True EXPLAIN = true
force = False force = false
GENERATE_DIFF = False GENERATE_DIFF = false
# if set, a diff.log file is generated when bootstrapping # 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 # the command used for bootstrapping
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
def Error(msg): sys.exit("[Koch] *** ERROR: " + msg) def Error(msg): sys.exit("[Koch] *** ERROR: " + msg)
def Warn(msg): print "[Koch] *** WARNING: " + msg def Warn(msg): print("[Koch] *** WARNING: " + msg)
def Echo(msg): print "[Koch] " + msg def Echo(msg): print("[Koch] " + msg)
def _Info(msg): print "[Koch] " + msg def _Info(msg): print("[Koch] " + msg)
_FINGERPRINTS_FILE = "koch.dat" _FINGERPRINTS_FILE = "koch.dat"
# in this file all the fingerprints are kept to allow recognizing when a file # 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 # has changed. This works reliably, which cannot be said from just taking
# filetime-stamps. # filetime-stamps.
def FileCmp(filenameA, filenameB): def SameFileContent(filenameA, filenameB):
SIZE = 4096*2 SIZE = 4096*2
result = True result = true
a = open(filenameA, "rb") a = open(filenameA, "rb")
b = open(filenameB, "rb") b = open(filenameB, "rb")
while True: while true:
x = a.read(SIZE) x = a.read(SIZE)
y = b.read(SIZE) y = b.read(SIZE)
if x != y: if x != y:
result = False result = false
break break
elif len(x) < SIZE: # EOF? elif len(x) < SIZE: # EOF?
break break
@ -70,41 +63,11 @@ def FileCmp(filenameA, filenameB):
b.close() b.close()
return result 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): def SplitArg(s):
if ':' in s: c = ':' if ':' in s: c = ':'
elif '=' in s: c = '=' elif '=' in s: c = '='
else: return (s, '') else: return (s, '')
i = s.find(c) i = find(s, c)
return (s[:i], s[i+1:]) return (s[:i], s[i+1:])
_baseDir = os.getcwd() _baseDir = os.getcwd()
@ -150,7 +113,7 @@ def Remove(f):
try: try:
os.remove(Path(f)) os.remove(Path(f))
except OSError: except OSError:
Warn("could not remove: %s" % f) Warn("could not remove: " + f)
def Move(src, dest): def Move(src, dest):
try: try:
@ -164,22 +127,26 @@ def Move(src, dest):
d = Path(dest) d = Path(dest)
try: try:
m(s, d) m(s, d)
except IOError, OSError: except IOError:
Warn("could not move %s to %s" % (s, d)) 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): def Copy(src, dest):
s = Path(src) s = Path(src)
d = Path(dest) d = Path(dest)
try: try:
shutil.copyfile(s, d) shutil.copyfile(s, d)
except IOError, OSError: except IOError:
Warn("could not copy %s to %s" % (s, d)) 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): def RemoveDir(f):
try: try:
shutil.rmtree(Path(f)) shutil.rmtree(Path(f))
except OSError: except OSError:
Warn("could not remove: %s" % f) Warn("could not remove: " + f)
def Exists(f): return os.path.exists(Path(f)) def Exists(f): return os.path.exists(Path(f))
@ -198,7 +165,7 @@ def Mkdir(dest):
Warn("could not create directory: " + d) Warn("could not create directory: " + d)
def Glob(pattern): # needed because glob.glob() is buggy on Windows 95: 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 global _baseDir
(head, tail) = os.path.split(Path(pattern)) (head, tail) = os.path.split(Path(pattern))
result = [] result = []
@ -250,14 +217,14 @@ class Changed:
# a success: # a success:
c.success() c.success()
""" """
def __init__(self, id, files, explain=False, def __init__(self, id, files, explain=false,
fingerprintsfile=_FINGERPRINTS_FILE): fingerprintsfile=_FINGERPRINTS_FILE):
# load the fingerprints file: # load the fingerprints file:
# fingerprints is a dict[target, files] where files is a dict[filename, hash] # fingerprints is a dict[target, files] where files is a dict[filename, hash]
self.fingers = {} # default value self.fingers = {} # default value
if Exists(fingerprintsfile): if Exists(fingerprintsfile):
try: try:
self.fingers = cPickle.load(open(fingerprintsfile)) self.fingers = pickle.load(open(fingerprintsfile, "rb"))
except OSError: except OSError:
Error("Cannot read from " + fingerprintsfile) Error("Cannot read from " + fingerprintsfile)
self.filename = fingerprintsfile self.filename = fingerprintsfile
@ -267,7 +234,7 @@ class Changed:
self.explain = explain self.explain = explain
def _hashFile(self, f): def _hashFile(self, f):
x = open(f) x = open(f, "rb")
result = self._hashStr(x.read()) result = self._hashStr(x.read())
x.close() # for other Python implementations x.close() # for other Python implementations
return result return result
@ -275,29 +242,29 @@ class Changed:
def check(self): def check(self):
if type(self.files) == type(""): if type(self.files) == type(""):
self.files = split(self.files) self.files = split(self.files)
result = False result = false
target = self.id target = self.id
if not self.fingers.has_key(target): if not has_key(self.fingers, target):
self.fingers[target] = {} self.fingers[target] = {}
if self.explain: _Info("no entries for target '%s'" % target) if self.explain: _Info(Subs("no entries for target '$1'", target))
result = True result = true
for d in self.files: for d in self.files:
if Exists(d): if Exists(d):
n = self._hashFile(d) n = self._hashFile(d)
if not self.fingers[target].has_key(d) or n != self.fingers[target][d]: if not has_key(self.fingers[target], d) or n != self.fingers[target][d]:
result = True result = true
if self.explain: _Info("'%s' modified since last build" % d) if self.explain: _Info(Subs("'$1' modified since last build", d))
self.fingers[target][d] = n self.fingers[target][d] = n
else: else:
Warn("'%s' does not exist!" % d) Warn(Subs("'$1' does not exist!", d))
result = True result = true
return result return result
def update(self, filename): def update(self, filename):
self.fingers[self.id][filename] = self._hashFile(filename) self.fingers[self.id][filename] = self._hashFile(filename)
def success(self): 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")) _nim_exe = os.path.join(os.getcwd(), "bin", ExeExt("nim"))
_output_obj = os.path.join(os.getcwd(), "obj") _output_obj = os.path.join(os.getcwd(), "obj")
FPC_CMD = (r"fpc -Cs16777216 -gl -bl -Crtoi -Sgidh -vw -Se1 -o%s " FPC_CMD = Subs(r"fpc -Cs16777216 -gl -bl -Crtoi -Sgidh -vw -Se1 -o$1 "
r"-FU%s %s") % (_nim_exe, _output_obj, r"-FU$2 $3", _nim_exe, _output_obj,
os.path.join(os.getcwd(), "nim", "nimrod.pas")) os.path.join(os.getcwd(), "nim", "nimrod.pas"))
def buildRod(options): 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")) Move(ExeExt("rod/nimrod"), ExeExt("bin/nimrod"))
def cmd_nim(): def cmd_nim():
@ -341,8 +308,8 @@ def cmd_nim():
Exec(FPC_CMD) Exec(FPC_CMD)
if Exists(ExeExt("bin/nim")): if Exists(ExeExt("bin/nim")):
c.success() c.success()
return True return true
return False return false
def cmd_rod(options): def cmd_rod(options):
prereqs = Glob("lib/*.nim") + Glob("rod/*.nim") + [ prereqs = Glob("lib/*.nim") + Glob("rod/*.nim") + [
@ -356,13 +323,13 @@ def cmd_rod(options):
# ------------------- constants ----------------------------------------------- # ------------------- constants -----------------------------------------------
HELP = """\ HELP = Subs("""\
+-----------------------------------------------------------------+ +-----------------------------------------------------------------+
| Maintenance script for Nimrod | | Maintenance script for Nimrod |
| Version %s| | Version $1|
| (c) 2008 Andreas Rumpf | | (c) 2009 Andreas Rumpf |
+-----------------------------------------------------------------+ +-----------------------------------------------------------------+
Your Python version: %s Your Python version: $2
Usage: Usage:
koch.py [options] command [options for command] koch.py [options] command [options for command]
@ -382,27 +349,27 @@ Possible Commands:
csource build the C sources for installation csource build the C sources for installation
zip build the installation ZIP package zip build the installation ZIP package
inno build the Inno Setup installer 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): def main(args):
if len(args) == 0: if len(args) == 0:
print HELP print(HELP)
else: else:
i = 0 i = 0
while args[i][:1] == "-": while args[i][:1] == "-":
a = args[i] a = args[i]
if a in ("--force", "-f", "-B", "-b"): if a in ("--force", "-f", "-B", "-b"):
global force global force
force = True force = true
elif a in ("-h", "--help", "-?"): elif a in ("-h", "--help", "-?"):
print HELP print(HELP)
return return
elif a == "--diff": elif a == "--diff":
global GENERATE_DIFF global GENERATE_DIFF
GENERATE_DIFF = True GENERATE_DIFF = true
elif a == "--no_fpc": elif a == "--no_fpc":
global USE_FPC global USE_FPC
USE_FPC = False USE_FPC = false
else: else:
Error("illegal option: " + a) Error("illegal option: " + a)
i = i + 1 i = i + 1
@ -417,19 +384,23 @@ def main(args):
elif cmd == "zip": cmd_zip() elif cmd == "zip": cmd_zip()
elif cmd == "inno": cmd_inno() elif cmd == "inno": cmd_inno()
elif cmd == "csource": cmd_csource() elif cmd == "csource": cmd_csource()
elif cmd == "install": cmd_install() # for backwards compability
else: Error("illegal command: " + cmd) else: Error("illegal command: " + cmd)
def cmd_csource(): def cmd_csource():
Exec("nimrod cc -r tools/niminst --var:version=%s csource rod/nimrod" % Exec(Subs("nimrod cc -r tools/niminst --var:version=$1 csource rod/nimrod",
NIMROD_VERSION) NIMROD_VERSION))
def cmd_zip(): def cmd_zip():
Exec("nimrod cc -r tools/niminst --var:version=%s zip rod/nimrod" % Exec(Subs("nimrod cc -r tools/niminst --var:version=$1 zip rod/nimrod",
NIMROD_VERSION) NIMROD_VERSION))
def cmd_inno(): def cmd_inno():
Exec("nimrod cc -r tools/niminst --var:version=%s inno rod/nimrod" % Exec(Subs("nimrod cc -r tools/niminst --var:version=$1 inno rod/nimrod",
NIMROD_VERSION) NIMROD_VERSION))
def cmd_install():
Exec("sh ./build.sh")
# -------------------------- bootstrap ---------------------------------------- # -------------------------- bootstrap ----------------------------------------
@ -448,13 +419,13 @@ def genBootDiff(genA, genB):
return a != b return a != b
BOOTLOG = "bootdiff.log" BOOTLOG = "bootdiff.log"
result = False result = false
for f in Glob("diff/*.c"): Remove(f) for f in Glob("diff/*.c"): Remove(f)
if Exists(BOOTLOG): Remove(BOOTLOG) if Exists(BOOTLOG): Remove(BOOTLOG)
if GENERATE_DIFF: if GENERATE_DIFF:
lines = [] # lines of the generated logfile lines = [] # lines of the generated logfile
if len(genA) != len(genB): Warn("number of generated files differ!") 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] bcontent = genB[filename]
if bcontent != acontent: if bcontent != acontent:
lines.append("------------------------------------------------------") lines.append("------------------------------------------------------")
@ -464,8 +435,8 @@ def genBootDiff(genA, genB):
la = acontent[i][:-1] # without newline! la = acontent[i][:-1] # without newline!
lb = bcontent[i][:-1] lb = bcontent[i][:-1]
if interestingDiff(la, lb): if interestingDiff(la, lb):
lines.append("%6d - %s" % (i, la)) lines.append(Subs("$1 - $2", i, la))
lines.append("%6d + %s" % (i, lb)) lines.append(Subs("$1 + $2", i, lb))
if len(acontent) > len(bcontent): if len(acontent) > len(bcontent):
cont = acontent cont = acontent
marker = "-" marker = "-"
@ -473,10 +444,10 @@ def genBootDiff(genA, genB):
cont = bcontent cont = bcontent
marker = "+" marker = "+"
for i in range(min(len(acontent), len(bcontent)), len(cont)): 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", "a_"+filename), "w+").write(join(acontent, ""))
open(os.path.join("diff", "b_"+filename), "w+").write(join(bcontent, "")) 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")) open(BOOTLOG, "w+").write(join(lines, "\n"))
return result return result
@ -488,17 +459,17 @@ def cmd_rodsrc():
compiler = "nim" compiler = "nim"
else: else:
compiler = "nimrod" compiler = "nimrod"
CMD = "%s boot --skip_proj_cfg -o:rod/%s.nim nim/%s" CMD = "$1 boot --skip_proj_cfg -o:rod/$2.nim nim/$3"
result = False result = false
for fi in Glob("nim/*.pas"): for fi in Glob("nim/*.pas"):
f = FilenameNoExt(fi) f = FilenameNoExt(fi)
if f in PAS_FILES_BLACKLIST: continue if f in PAS_FILES_BLACKLIST: continue
c = Changed(f+"__rodsrc", fi, EXPLAIN) c = Changed(f+"__rodsrc", fi, EXPLAIN)
if c.check() or force: if c.check() or force:
Exec(CMD % (compiler, f, f+".pas")) Exec(Subs(CMD, compiler, f, f+".pas"))
Exec("%s parse rod/%s.nim" % (compiler, f)) Exec(Subs("$1 parse rod/$2.nim", compiler, f))
c.success() c.success()
result = True result = true
return result return result
def moveExes(): def moveExes():
@ -506,7 +477,7 @@ def moveExes():
def cmd_boot(args): def cmd_boot(args):
def myExec(compiler, args=args): def myExec(compiler, args=args):
Exec(BOOTCMD % (compiler, args)) Exec(Subs(BOOTCMD, compiler, args))
# some C compilers (PellesC) output the executable to the # some C compilers (PellesC) output the executable to the
# wrong directory. We work around this bug here: # wrong directory. We work around this bug here:
if Exists(ExeExt("rod/nimcache/nimrod")): if Exists(ExeExt("rod/nimcache/nimrod")):
@ -515,7 +486,7 @@ def cmd_boot(args):
writePlatdefC(getNimrodPath()) writePlatdefC(getNimrodPath())
d = detect("fpc -h") d = detect("fpc -h")
if USE_FPC and d: if USE_FPC and d:
Echo("'%s' detected" % d) Echo(Subs("'$1' detected", d))
cmd_nim() cmd_nim()
compiler = "nim" compiler = "nim"
else: else:
@ -536,34 +507,34 @@ def cmd_boot(args):
if diff: if diff:
Warn("generated C files are not equal: cycle once again...") Warn("generated C files are not equal: cycle once again...")
# check if the executables are the same (they should!): # check if the executables are the same (they should!):
if FileCmp(Path(ExeExt("rod/nimrod")), if SameFileContent(Path(ExeExt("rod/nimrod")),
Path(ExeExt("bin/nimrod"))): Path(ExeExt("bin/nimrod"))):
Echo("executables are equal: SUCCESS!") Echo("executables are equal: SUCCESS!")
else: else:
Echo("executables are not equal: cycle once again...") Echo("executables are not equal: cycle once again...")
diff = True diff = true
if diff: if diff:
# move the new executable to bin directory: # move the new executable to bin directory:
moveExes() moveExes()
# use the new executable to compile Nimrod: # use the new executable to compile Nimrod:
myExec("nimrod") myExec("nimrod")
if FileCmp(Path(ExeExt("rod/nimrod")), if SameFileContent(Path(ExeExt("rod/nimrod")),
Path(ExeExt("bin/nimrod"))): Path(ExeExt("bin/nimrod"))):
Echo("executables are equal: SUCCESS!") Echo("executables are equal: SUCCESS!")
else: else:
Warn("executables are still not equal") Warn("executables are still not equal")
# ------------------ profile -------------------------------------------------- # ------------------ profile --------------------------------------------------
def cmd_profile(): def cmd_profile():
Exec(BOOTCMD % ("nimrod", "-d:release --profiler:on")) Exec(Subs(BOOTCMD, "nimrod", "-d:release --profiler:on"))
moveExes() moveExes()
Exec(BOOTCMD % ("nimrod", "--compile_only")) Exec(Subs(BOOTCMD, "nimrod", "--compile_only"))
# ------------------ web ------------------------------------------------------ # ------------------ web ------------------------------------------------------
def cmd_web(): def cmd_web():
Exec("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=%s" Exec(Subs("nimrod cc -r tools/nimweb.nim web/nimrod "
% NIMROD_VERSION) "--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 for f in Glob("rod/*.nim"): Remove(f) # remove generated source code
def visit(extRegEx, dirname, names): def visit(extRegEx, dirname, names):
if os.path.split(dirname)[1] == "nimcache": if os.path.split(dirname)[1] == "nimcache":
shutil.rmtree(path=dirname, ignore_errors=True) shutil.rmtree(path=dirname, ignore_errors=true)
del names del names
else: else:
for name in names: for name in names:
@ -637,10 +608,10 @@ def writePlatdefC(nimrodpath):
import os import os
host, processor = getOSandProcessor() host, processor = getOSandProcessor()
f = open(os.path.join(nimrodpath, "build/platdef.c"), "w+") f = open(os.path.join(nimrodpath, "build/platdef.c"), "w+")
f.write('/* Generated by koch.py */\n' f.write(Subs('/* Generated by koch.py */\n'
'char* nimOS(void) { return "%s"; }\n' 'char* nimOS(void) { return "$1"; }\n'
'char* nimCPU(void) { return "%s"; }\n' 'char* nimCPU(void) { return "$2"; }\n'
'\n' % (host, processor)) '\n', host, processor))
f.close() f.close()
def detect(cmd, lookFor="version"): def detect(cmd, lookFor="version"):

View file

@ -1,13 +1,20 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf # (c) Copyright 2009 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
# Low level allocator for Nimrod. # 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 ----------------------- # ------------ platform specific chunk allocation code -----------------------
@ -15,20 +22,14 @@ when defined(posix):
const # XXX: make these variables for portability? const # XXX: make these variables for portability?
PROT_READ = 1 # page can be read PROT_READ = 1 # page can be read
PROT_WRITE = 2 # page can be written PROT_WRITE = 2 # page can be written
PROT_EXEC = 4 # page can be executed MAP_PRIVATE = 2 # Changes are private
PROT_NONE = 0 # page can not be accessed
MAP_SHARED = 1 # Share changes when defined(linux):
MAP_PRIVATE = 2 # Changes are private const MAP_ANONYMOUS = 0x20 # don't use a file
MAP_TYPE = 0xf # Mask for type of mapping elif defined(macosx):
MAP_FIXED = 0x10 # Interpret addr exactly const MAP_ANONYMOUS = 0x1000
MAP_ANONYMOUS = 0x20 # don't use a file else:
const MAP_ANONYMOUS = 0 # other operating systems may not know about this
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
proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
off: int): pointer {.header: "<sys/mman.h>".} off: int): pointer {.header: "<sys/mman.h>".}
@ -42,7 +43,7 @@ when defined(posix):
raiseOutOfMem() raiseOutOfMem()
proc osDeallocPages(p: pointer, size: int) {.inline} = proc osDeallocPages(p: pointer, size: int) {.inline} =
munmap(p, len) munmap(p, size)
elif defined(windows): elif defined(windows):
const const
@ -51,20 +52,27 @@ elif defined(windows):
MEM_TOP_DOWN = 0x100000 MEM_TOP_DOWN = 0x100000
PAGE_READWRITE = 0x04 PAGE_READWRITE = 0x04
MEM_DECOMMIT = 0x4000
MEM_RELEASE = 0x8000
proc VirtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType, proc VirtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType,
flProtect: int32): pointer {. flProtect: int32): pointer {.
header: "<windows.h>", stdcall.} header: "<windows.h>", stdcall.}
proc VirtualFree(lpAddress: pointer, dwSize: int,
dwFreeType: int32) {.header: "<windows.h>", stdcall.}
proc osAllocPages(size: int): pointer {.inline.} = proc osAllocPages(size: int): pointer {.inline.} =
result = VirtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT, result = VirtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT,
PAGE_READWRITE) PAGE_READWRITE)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
proc osDeallocPages(p: pointer, size: int) {.inline.} = proc osDeallocPages(p: pointer, size: int) {.inline.} =
nil # according to Microsoft, 0 is the only correct value here:
VirtualFree(p, 0, MEM_RELEASE)
else: else:
{.error: "Port GC to your platform".} {.error: "Port memory manager to your platform".}
# --------------------- end of non-portable code ----------------------------- # --------------------- end of non-portable code -----------------------------
@ -78,59 +86,79 @@ else:
# Guess the page size of the system; if it is the # Guess the page size of the system; if it is the
# wrong value, performance may be worse (this is not # wrong value, performance may be worse (this is not
# for sure though), but GC still works; must be a power of two! # 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 const
PageShift = if sizeof(pointer) == 4: 12 else: 13 PageMask = PageSize-1
PageSize = 1 shl PageShift # on 32 bit systems 4096
MemAlignment = sizeof(pointer)*2 # minimal memory block that can be allocated SmallChunkSize = PageSize # * 4
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 MemAlign = 8 # minimal memory block that can be allocated
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 (!) # on 32 bit systems this is only 16 (!)
smallRequest = PageSize div 4 ChunkOsReturn = 64 * PageSize
ChunkOsReturn = 1024 # in pages
InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn! InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn!
debugMemMan = true # we wish to debug the memory manager...
# 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 type
PChunkDesc = ptr TChunkDesc PTrunk = ptr TTrunk
TChunkDesc {.final, pure.} = object TTrunk {.final.} = object
key: TAddress # address at bit 0 next: PTrunk # all nodes are connected with this pointer
next: PChunkDesc key: int # start address at bit 0
bits: array[0..127, int] # a bit vector bits: array[0..IntsPerTrunk-1, int] # a bit vector
PChunkDescArray = ptr array[0..1000_000, PChunkDesc] TTrunkBuckets = array[0..1023, PTrunk]
TChunkSet {.final, pure.} = object TIntSet {.final.} = object
counter, max: int data: TTrunkBuckets
head: PChunkDesc
data: PChunkDescArray
when sizeof(int) == 4:
type THalfWord = int16
else:
type THalfWord = int32
type type
TAlignType = float
TFreeCell {.final, pure.} = object TFreeCell {.final, pure.} = object
zeroField: pointer # type info nil means cell is not used next: ptr TFreeCell # next free cell in chunk (overlaid with refcount)
next: ptr TFreeCell # next free cell in chunk zeroField: pointer # nil means cell is not used (overlaid with typ field)
PChunk = ptr TChunk PChunk = ptr TBaseChunk
TChunk {.final, pure.} = object PBigChunk = ptr TBigChunk
size: int # lowest two bits are used for merging: PSmallChunk = ptr TSmallChunk
# bit 0: chunk to the left is accessible and free TBaseChunk {.pure.} = object
# bit 1: chunk to the right is accessible and free prevSize: int # size of previous chunk; for coalescing
len: int # for small object allocation size: int # if < PageSize it is a small chunk
prev, next: PChunk # chunks of the same (or bigger) size used: bool # later will be optimized into prevSize...
#len, used: THalfWord # index of next to allocate cell
TSmallChunk = object of TBaseChunk
next, prev: PSmallChunk # chunks of the same size
freeList: ptr TFreeCell 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 proc roundup(x, v: int): int {.inline.} = return ((-x) and (v-1)) +% x
@ -147,240 +175,325 @@ assert(roundup(15, 8) == 16)
type type
PLLChunk = ptr TLLChunk PLLChunk = ptr TLLChunk
TLLChunk {.pure.} = object ## *low-level* chunk TLLChunk {.pure.} = object ## *low-level* chunk
size: int size: int # remaining size
when sizeof(int) == 4: acc: int # accumulator
align: int
TAllocator {.final, pure.} = object TAllocator {.final, pure.} = object
llmem: PLLChunk llmem: PLLChunk
UsedPagesCount, FreePagesCount, maxPagesCount: int currMem, maxMem: int # currently and maximum used memory size (allocated from OS)
freeSmallChunks: array[0..smallRequest div MemAlign-1, PChunk] freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
freeBigChunks: array[0..ChunkOsReturn-1, PChunk] 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 = proc llAlloc(a: var TAllocator, size: int): pointer =
# *low-level* alloc for the memory managers data structures. Deallocation # *low-level* alloc for the memory managers data structures. Deallocation
# is never done. # is never done.
assert(size <= PageSize-8) if a.llmem == nil or size > a.llmem.size:
if a.llmem.size + size > PageSize: var request = roundup(size+sizeof(TLLChunk), PageSize)
a.llmem = osGetPages(PageSize) a.llmem = cast[PLLChunk](osAllocPages(request))
inc(a.gUsedPages) incCurrMem(a, request)
a.llmem.size = 8 a.llmem.size = request - sizeof(TLLChunk)
result = cast[pointer](cast[TAddress](a.llmem) + a.llmem.size) a.llmem.acc = sizeof(TLLChunk)
inc(llmem.size, size) result = cast[pointer](cast[TAddress](a.llmem) + a.llmem.acc)
dec(a.llmem.size, size)
inc(a.llmem.acc, size)
zeroMem(result, size) zeroMem(result, size)
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
const proc IntSetPut(t: var TIntSet, key: int): PTrunk =
InitChunkSetSize = 1024 # must be a power of two! 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 ChunkSetInit(s: var TChunkSet) = proc Contains(s: TIntSet, key: int): bool =
s.data = cast[PChunkDescArray](llAlloc(InitChunkSetSize * sizeof(PChunkDesc))) var t = IntSetGet(s, key shr TrunkShift)
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: if t != nil:
u = (u %% PageSize) /% MemAlignment var u = key and TrunkMask
result = (t.bits[u /% BitsPerUnit] and (1 shl (u %% BitsPerUnit))) != 0 result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
else: else:
result = false result = false
proc incl(s: var TCellSet, cell: PCell) = proc Incl(s: var TIntSet, key: int) =
var u = cast[TAddress](cell) var t = IntSetPut(s, key shr TrunkShift)
var t = ChunkSetPut(s, u shr PageShift) var u = key and TrunkMask
u = (u %% PageSize) /% MemAlignment t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
t.bits[u /% BitsPerUnit] = t.bits[u /% BitsPerUnit] or
(1 shl (u %% BitsPerUnit))
proc excl(s: var TCellSet, cell: PCell) = proc Excl(s: var TIntSet, key: int) =
var u = cast[TAddress](cell) var t = IntSetGet(s, key shr TrunkShift)
var t = ChunkSetGet(s, u shr PageShift)
if t != nil: if t != nil:
u = (u %% PageSize) /% MemAlignment var u = key and TrunkMask
t.bits[u /% BitsPerUnit] = (t.bits[u /% BitsPerUnit] and t.bits[u shr IntShift] = t.bits[u shr IntShift] and not
not (1 shl (u %% BitsPerUnit))) (1 shl (u and IntMask))
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
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 ---------------------------------------------- # ------------- chunk management ----------------------------------------------
proc removeChunk(a: var TAllocator, c: PChunk) {.inline.} = proc pageIndex(c: PChunk): int {.inline.} =
if c.prev != nil: c.prev.next = c.next result = cast[TAddress](c) shr PageShift
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.} = proc pageIndex(p: pointer): int {.inline.} =
var s = abs(c.size) div PageSize 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.prev = nil
c.next = a.freeChunks[s]
a.freeChunks[s] = c
proc freeChunk(a: var TAllocator, c: PChunk) = proc isSmallChunk(c: PChunk): bool {.inline.} =
assert(c.size > 0) return c.size <= SmallChunkSize-smallChunkOverhead()
if c.size < PageSize: c.size = PageSize #return c.size < SmallChunkSize
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)
proc splitChunk(a: var TAllocator, c: PChunk, size: int) = proc chunkUnused(c: PChunk): bool {.inline.} =
var rest = cast[PChunk](cast[TAddress](p) + size) result = not c.used
rest.size = size - c.size # results in negative number, because rest is free
addChunk(a, rest) proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
# mark pages as accessible: var c = c
ChunkTablePut(a, rest, bitAccessible) 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)
if c.size < ChunkOsReturn:
ListAdd(a.freeChunksList, c)
c.used = false
else:
freeOsChunks(a, c, c.size)
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 c.size = size
incl(a.chunkStarts, pageIndex(rest))
ListAdd(a.freeChunksList, rest)
proc getChunkOfSize(a: var TAllocator, size: int): PChunk = proc getBigChunk(a: var TAllocator, size: int): PBigChunk =
for i in size..high(a.freeChunks): # use first fit for now:
result = a.freeChunks[i] assert((size and PageMask) == 0)
if result != nil: result = a.freeChunksList
if i != size: splitChunk(a, result, size) block search:
else: removeChunk(a, result) while result != nil:
result.prev = nil assert chunkUnused(result)
result.next = nil if result.size == size:
break 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.} = proc getCellSize(p: pointer): int {.inline.} =
var c = getChunk(p) var c = pageAddr(p)
result = abs(c.size) result = c.size
proc alloc(a: var TAllocator, size: int): pointer = proc alloc(a: var TAllocator, requestedSize: int): pointer =
if size <= smallRequest: var size = roundup(max(requestedSize, sizeof(TFreeCell)), MemAlign)
# allocate a small block if size <= SmallChunkSize-smallChunkOverhead():
# allocate a small block: for small chunks, we use only its next pointer
var s = size div MemAlign var s = size div MemAlign
var c = a.freeSmallChunks[s] var c = a.freeSmallChunks[s]
if c == nil: if c == nil:
c = getChunkOfSize(0) c = getSmallChunk(a)
c.freeList = nil c.freeList = nil
assert c.size == PageSize
c.size = size c.size = size
a.freeSmallChunks[s] = c c.acc = size
c.len = 1 c.free = SmallChunkSize - smallChunkOverhead() - size
c.used = 1 c.next = nil
c.chunkStart = 0 c.prev = nil
result = addr(c.data[0]) ListAdd(a.freeSmallChunks[s], c)
elif c.freeList != nil: result = addr(c.data)
result = c.freeList
assert(c.freeList.zeroField == nil)
c.freeList = c.freeList.next
inc(c.used)
if c.freeList == nil: removeChunk(a, c)
else: else:
assert(c.len*size <= high(c.data)) assert c.next != c
result = addr(c.data[c.len*size]) assert c.size == size
inc(c.len) if c.freeList == nil:
inc(c.used) assert(c.acc + smallChunkOverhead() + size <= SmallChunkSize)
if c.len*size > high(c.data): removeChunk(a, c) 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: else:
size = roundup(requestedSize+bigChunkOverhead(), PageSize)
# allocate a large block # allocate a large block
var c = getChunkOfSize(size shr PageShift) var c = getBigChunk(a, size)
result = addr(c.data[0]) assert c.prev == nil
c.freeList = nil assert c.next == nil
c.size = size assert c.size == size
c.len = 0 result = addr(c.data)
c.used = 0 cast[ptr TFreeCell](result).zeroField = cast[ptr TFreeCell](1) # make it != nil
c.chunkStart = 0 #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) = proc dealloc(a: var TAllocator, p: pointer) =
var c = getChunk(p) var c = pageAddr(p)
if c.size <= smallRequest: if isSmallChunk(c):
# free small block: # `p` is within a small chunk:
var c = cast[PSmallChunk](c)
var s = c.size
var f = cast[ptr TFreeCell](p) var f = cast[ptr TFreeCell](p)
#echo("setting to nil: ", $cast[TAddress](addr(f.zeroField)))
assert(f.zeroField != nil)
f.zeroField = nil f.zeroField = nil
f.next = c.freeList f.next = c.freeList
c.freeList = p c.freeList = f
dec(c.used) # check if it is not in the freeSmallChunks[s] list:
if c.used == 0: freeChunk(c) 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: else:
# free big chunk # free big chunk
freeChunk(c) freeBigChunk(a, cast[PBigChunk](c))
proc realloc(a: var TAllocator, p: pointer, size: int): pointer = 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 # 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) dealloc(a, p)
proc isAllocatedPtr(a: TAllocator, p: pointer): bool = proc isAllocatedPtr(a: TAllocator, p: pointer): bool =
var c = getChunk(p) if isAccessible(p):
if c in a.accessibleChunks and c.size > 0: var c = pageAddr(p)
result = cast[ptr TFreeCell](p).zeroField != nil 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))

View file

@ -5,7 +5,6 @@
; that is the only convention any C compiler supports. ; that is the only convention any C compiler supports.
\python{ \python{
# as usual I use my own preprocessor :-)
import os import os
def c(name): def c(name):

View file

@ -43,8 +43,12 @@ proc genericAssign(dest, src: Pointer, mt: PNimType) =
x^ = nil x^ = nil
return return
assert(dest != nil) assert(dest != nil)
unsureAsgnRef(cast[ppointer](dest), when defined(boehmGC):
newObj(mt, seq.len * mt.base.size + GenericSeqSize)) 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)^) var dst = cast[taddress](cast[ppointer](dest)^)
for i in 0..seq.len-1: for i in 0..seq.len-1:
genericAssign( genericAssign(

View file

@ -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">&quot;name&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;Klaus&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;password&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;123456&quot;</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 &quot;name&quot; or &quot;passwort&quot;</span>
<span class="Identifier">validateData</span><span class="Punctation">(</span><span class="Identifier">myData</span><span class="Punctation">,</span> <span class="StringLit">&quot;name&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;password&quot;</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">&quot;&lt;!DOCTYPE HTML PUBLIC </span><span class="EscapeSequence">\&quot;</span>-//W3C//DTD HTML 4.01//EN<span class="EscapeSequence">\&quot;</span><span class="EscapeSequence">&gt;\n</span><span class="StringLit">&quot;</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">&quot;&lt;html&gt;&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt;&lt;body&gt;</span><span class="EscapeSequence">\n</span><span class="StringLit">&quot;</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">&quot;your name: &quot;</span> <span class="Operator">&amp;</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">&quot;name&quot;</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">&quot;your password: &quot;</span> <span class="Operator">&amp;</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">&quot;password&quot;</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">&quot;&lt;/body&gt;&lt;/html&gt;&quot;</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">&quot;name&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;Hanz&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;password&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;12345&quot;</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, &quot;Content-type: text/htmlnn&quot;)</blockquote>
</dd>
</dl></div>
</div>
<small>Generated: 2008-12-08 20:29:49 UTC</small>
</div>
</body>
</html>

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf # (c) Copyright 2009 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -71,20 +71,40 @@ proc URLdecode*(s: string): string =
else: add(result, s[i]) else: add(result, s[i])
inc(i) inc(i)
proc addXmlChar(dest: var string, c: Char) {.inline.} =
case c
of '&': add(dest, "&amp;")
of '<': add(dest, "&lt;")
of '>': add(dest, "&gt;")
of '\"': add(dest, "&quot;")
else: add(dest, c)
proc XMLencode*(s: string): string =
## Encodes a value to be XML safe:
## * ``"`` is replaced by ``&quot;``
## * ``<`` is replaced by ``&lt;``
## * ``>`` is replaced by ``&gt;``
## * ``&`` is replaced by ``&amp;``
## * every other character is carried over.
result = ""
for i in 0..len(s)-1: addXmlChar(result, s[i])
type type
ECgi* = object of EIO ## the exception that is raised, if a CGI error occurs ECgi* = object of EIO ## the exception that is raised, if a CGI error occurs
TRequestMethod* = enum ## the used request method TRequestMethod* = enum ## the used request method
methodNone, ## no REQUEST_METHOD environment variable
methodPost, ## query uses the POST method methodPost, ## query uses the POST method
methodGet ## query uses the GET 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 var e: ref ECgi
new(e) new(e)
e.msg = msg e.msg = msg
raise e raise e
proc readData*(allowedMethods: set[TRequestMethod] = 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 ## Read CGI data. If the client does not use a method listed in the
## `allowedMethods` set, an `ECgi` exception is raised. ## `allowedMethods` set, an `ECgi` exception is raised.
result = newStringTable() result = newStringTable()
@ -103,7 +123,11 @@ proc readData*(allowedMethods: set[TRequestMethod] =
cgiError("'REQUEST_METHOD' 'GET' is not supported") cgiError("'REQUEST_METHOD' 'GET' is not supported")
# read from the QUERY_STRING environment variable: # read from the QUERY_STRING environment variable:
enc = getenv("QUERY_STRING") 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: # decode everything in one pass:
var i = 0 var i = 0
@ -129,7 +153,6 @@ proc readData*(allowedMethods: set[TRequestMethod] =
setLen(value, 0) # reuse memory setLen(value, 0) # reuse memory
while true: while true:
case enc[i] case enc[i]
of '\0': return
of '%': of '%':
var x = 0 var x = 0
handleHexChar(enc[i+1], x) handleHexChar(enc[i+1], x)
@ -137,12 +160,13 @@ proc readData*(allowedMethods: set[TRequestMethod] =
inc(i, 2) inc(i, 2)
add(value, chr(x)) add(value, chr(x))
of '+': add(value, ' ') of '+': add(value, ' ')
of '=', '&': break of '&', '\0': break
else: add(value, enc[i]) else: add(value, enc[i])
inc(i) inc(i)
if enc[i] != '&': cgiError("'&' expected")
inc(i) # skip '='
result[name] = value result[name] = value
if enc[i] == '&': inc(i)
elif enc[i] == '\0': break
else: cgiError("'&' expected")
proc validateData*(data: PStringTable, validKeys: openarray[string]) = proc validateData*(data: PStringTable, validKeys: openarray[string]) =
## validates data; raises `ECgi` if this fails. This checks that each variable ## validates data; raises `ECgi` if this fails. This checks that each variable

View file

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

View file

@ -17267,5 +17267,5 @@ proc gtk_file_chooser_set_do_overwrite_confirmation*(chooser: PGtkFileChooser,
proc gtk_nimrod_init*() = proc gtk_nimrod_init*() =
var var
cmdLine {.importc: "cmdLine".}: array [0..255, cstring] cmdLine {.importc: "cmdLine".}: array [0..255, cstring]
cmdCount {.importc: "cmdCount".}: int cmdCount {.importc: "cmdCount".}: cint
gtk_init(addr(cmdLine), addr(cmdCount)) gtk_init(addr(cmdLine), addr(cmdCount))

View file

@ -226,8 +226,8 @@ const
SQL_DRIVER_NOPROMPT* = 0 SQL_DRIVER_NOPROMPT* = 0
SQL_DRIVER_COMPLETE* = 1 SQL_DRIVER_COMPLETE* = 1
SQL_DRIVER_PROMPT* = 2 SQL_DRIVER_PROMPT* = 2
SQL_DRIVER_COMPLETE_REQUIRED* = 3 # whether an attribute is a pointer or not SQL_DRIVER_COMPLETE_REQUIRED* = 3
SQL_IS_POINTER* = (- 4) SQL_IS_POINTER* = (- 4) # whether an attribute is a pointer or not
SQL_IS_UINTEGER* = (- 5) SQL_IS_UINTEGER* = (- 5)
SQL_IS_INTEGER* = (- 6) SQL_IS_INTEGER* = (- 6)
SQL_IS_USMALLINT* = (- 7) SQL_IS_USMALLINT* = (- 7)
@ -737,7 +737,8 @@ proc SQLFreeStmt*(StatementHandle: TSqlHStmt, Option: TSqlUSmallInt): TSqlSmallI
proc SQLColAttribute*(StatementHandle: TSqlHStmt, ColumnNumber: TSqlUSmallInt, proc SQLColAttribute*(StatementHandle: TSqlHStmt, ColumnNumber: TSqlUSmallInt,
FieldIdentifier: TSqlUSmallInt, FieldIdentifier: TSqlUSmallInt,
CharacterAttribute: PSQLCHAR, BufferLength: TSqlSmallInt, CharacterAttribute: PSQLCHAR, BufferLength: TSqlSmallInt,
StringLength: PSQLSMALLINT, NumericAttribute: TSqlPointer): TSqlSmallInt{. StringLength: PSQLSMALLINT,
NumericAttribute: TSqlPointer): TSqlSmallInt{.
dynlib: odbclib, importc.} dynlib: odbclib, importc.}
proc SQLEndTran*(HandleType: TSqlSmallInt, Handle: TSqlHandle, proc SQLEndTran*(HandleType: TSqlSmallInt, Handle: TSqlHandle,
CompletionType: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, CompletionType: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
@ -756,7 +757,8 @@ proc SQLSpecialColumns*(StatementHandle: TSqlHStmt, IdentifierType: TSqlUSmallIn
CatalogName: PSQLCHAR, NameLength1: TSqlSmallInt, CatalogName: PSQLCHAR, NameLength1: TSqlSmallInt,
SchemaName: PSQLCHAR, NameLength2: TSqlSmallInt, SchemaName: PSQLCHAR, NameLength2: TSqlSmallInt,
TableName: PSQLCHAR, NameLength3: TSqlSmallInt, TableName: PSQLCHAR, NameLength3: TSqlSmallInt,
Scope: TSqlUSmallInt, Nullable: TSqlUSmallInt): TSqlSmallInt{. Scope: TSqlUSmallInt,
Nullable: TSqlUSmallInt): TSqlSmallInt{.
dynlib: odbclib, importc.} dynlib: odbclib, importc.}
proc SQLProcedures*(hstmt: TSqlHStmt, szTableQualifier: PSQLCHAR, proc SQLProcedures*(hstmt: TSqlHStmt, szTableQualifier: PSQLCHAR,
cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR, cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR,
@ -778,6 +780,7 @@ proc SQLStatistics*(hstmt: TSqlHStmt, CatalogName: PSQLCHAR,
NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR, NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR,
NameLength2: TSqlSmallInt, TableName: PSQLCHAR, NameLength2: TSqlSmallInt, TableName: PSQLCHAR,
NameLength3: TSqlSmallInt, Unique: TSqlUSmallInt, NameLength3: TSqlSmallInt, Unique: TSqlUSmallInt,
Reserved: TSqlUSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.} Reserved: TSqlUSmallInt): TSqlSmallInt {.
dynlib: odbclib, importc.}
{.pop.} {.pop.}

View file

@ -1397,34 +1397,34 @@ proc glVertexPointer*(size: TGLint, atype: TGLenum, stride: TGLsizei,
pointer: Pointer){.dynlib: dllname, importc.} pointer: Pointer){.dynlib: dllname, importc.}
proc glViewport*(x, y: TGLint, width, height: TGLsizei){.dynlib: dllname, importc.} proc glViewport*(x, y: TGLint, width, height: TGLsizei){.dynlib: dllname, importc.}
type type
PFNGLARRAYELEMENTEXTPROC* = proc (i: TGLint) PFN_GLARRAY_ELEMENT_EXTPROC* = proc (i: TGLint)
PFNGLDRAWARRAYSEXTPROC* = proc (mode: TGLenum, first: TGLint, count: TGLsizei) PFN_GLDRAW_ARRAYS_EXTPROC* = proc (mode: TGLenum, first: TGLint, count: TGLsizei)
PFNGLVERTEXPOINTEREXTPROC* = proc (size: TGLint, atype: TGLenum, PFN_GLVERTEX_POINTER_EXTPROC* = proc (size: TGLint, atype: TGLenum,
stride, count: TGLsizei, pointer: Pointer) stride, count: TGLsizei, pointer: Pointer)
PFNGLNORMALPOINTEREXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei, PFN_GLNORMAL_POINTER_EXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei,
pointer: Pointer) pointer: Pointer)
PFNGLCOLORPOINTEREXTPROC* = proc (size: TGLint, atype: TGLenum, PFN_GLCOLOR_POINTER_EXTPROC* = proc (size: TGLint, atype: TGLenum,
stride, count: TGLsizei, pointer: Pointer) stride, count: TGLsizei, pointer: Pointer)
PFNGLINDEXPOINTEREXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei, PFN_GLINDEX_POINTER_EXTPROC* = proc (atype: TGLenum, stride, count: TGLsizei,
pointer: Pointer) pointer: Pointer)
PFNGLTEXCOORDPOINTEREXTPROC* = proc (size: TGLint, atype: TGLenum, PFN_GLTEXCOORD_POINTER_EXTPROC* = proc (size: TGLint, atype: TGLenum,
stride, count: TGLsizei, pointer: Pointer) stride, count: TGLsizei, pointer: Pointer)
PFNGLEDGEFLAGPOINTEREXTPROC* = proc (stride, count: TGLsizei, PFN_GLEDGEFLAG_POINTER_EXTPROC* = proc (stride, count: TGLsizei,
pointer: PGLboolean) pointer: PGLboolean)
PFNGLGETPOINTERVEXTPROC* = proc (pname: TGLenum, params: Pointer) PFN_GLGET_POINTER_VEXT_PROC* = proc (pname: TGLenum, params: Pointer)
PFNGLARRAYELEMENTARRAYEXTPROC* = proc (mode: TGLenum, count: TGLsizei, PFN_GLARRAY_ELEMENT_ARRAY_EXTPROC* = proc (mode: TGLenum, count: TGLsizei,
pi: Pointer) # WIN_swap_hint pi: Pointer) # WIN_swap_hint
PFNGLADDSWAPHINTRECTWINPROC* = proc (x, y: TGLint, width, height: TGLsizei) PFN_GLADDSWAPHINT_RECT_WINPROC* = proc (x, y: TGLint, width, height: TGLsizei)
PFNGLCOLORTABLEEXTPROC* = proc (target, internalFormat: TGLenum, PFN_GLCOLOR_TABLE_EXTPROC* = proc (target, internalFormat: TGLenum,
width: TGLsizei, format, atype: TGLenum, width: TGLsizei, format, atype: TGLenum,
data: Pointer) data: Pointer)
PFNGLCOLORSUBTABLEEXTPROC* = proc (target: TGLenum, start, count: TGLsizei, PFN_GLCOLOR_SUBTABLE_EXTPROC* = proc (target: TGLenum, start, count: TGLsizei,
format, atype: TGLenum, data: Pointer) format, atype: TGLenum, data: Pointer)
PFNGLGETCOLORTABLEEXTPROC* = proc (target, format, atype: TGLenum, PFN_GLGETCOLOR_TABLE_EXTPROC* = proc (target, format, atype: TGLenum,
data: Pointer) data: Pointer)
PFNGLGETCOLORTABLEPARAMETERIVEXTPROC* = proc (target, pname: TGLenum, PFN_GLGETCOLOR_TABLE_PARAMETER_IVEXTPROC* = proc (target, pname: TGLenum,
params: PGLint) params: PGLint)
PFNGLGETCOLORTABLEPARAMETERFVEXTPROC* = proc (target, pname: TGLenum, PFN_GLGETCOLOR_TABLE_PARAMETER_FVEXTPROC* = proc (target, pname: TGLenum,
params: PGLfloat) params: PGLfloat)
{.pop.} {.pop.}

View file

@ -7,18 +7,6 @@
# distribution, for details about the copyright. # 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" .} {.compile: "pcre_all.c" .}
type type

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2006 Andreas Rumpf # (c) Copyright 2009 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -35,7 +35,7 @@ const
proc match*(s, pattern: string, matches: var openarray[string], proc match*(s, pattern: string, matches: var openarray[string],
start: int = 0): bool 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 ## the captured substrings in the array ``matches``. If it does not
## match, nothing is written into ``matches`` and ``false`` is ## match, nothing is written into ``matches`` and ``false`` is
## returned. ## returned.
@ -113,3 +113,42 @@ proc match(s, pattern: string, start: int = 0): bool =
proc find(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 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

View file

@ -1,6 +1,6 @@
======================================================= =======================================================
The Nimrod Runtime Library 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 This is the file copying.txt, it applies to the Nimrod Run-Time Library

View file

@ -348,9 +348,9 @@ proc dbgStackFrame(s: string, start: int, currFrame: PExtendedFrame) =
proc CommandPrompt() = proc CommandPrompt() =
# if we return from this routine, user code executes again # if we return from this routine, user code executes again
var var
again: bool = True again = True
dbgFramePtr = framePtr # for going down and up the stack 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: while again:
write(stdout, "*** endb| >>") write(stdout, "*** endb| >>")
@ -478,10 +478,10 @@ proc dbgRegisterGlobal(name: cstring, address: pointer,
inc(dbgGlobalData.f.len) inc(dbgGlobalData.f.len)
proc endb(line: int) {.compilerproc.} = 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 # Thus, it must have as few parameters as possible to keep the
# code size small! # 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! framePtr.line = line # this is done here for smaller code size!
if dbgLineHook != nil: dbgLineHook() if dbgLineHook != nil: dbgLineHook()
case dbgState case dbgState

View file

@ -58,7 +58,10 @@ when defined(posix):
proc nimLoadLibrary(path: string): TLibHandle = proc nimLoadLibrary(path: string): TLibHandle =
result = dlopen(path, RTLD_NOW) result = dlopen(path, RTLD_NOW)
if result == nil: 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 = proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
result = dlsym(lib, name) result = dlsym(lib, name)
@ -84,7 +87,10 @@ elif defined(windows) or defined(dos):
proc nimLoadLibrary(path: string): TLibHandle = proc nimLoadLibrary(path: string): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(path)) result = cast[TLibHandle](winLoadLibrary(path))
if result == nil: 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 = proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
result = GetProcAddress(cast[THINSTANCE](lib), name) result = GetProcAddress(cast[THINSTANCE](lib), name)
@ -118,7 +124,10 @@ elif defined(mac):
NSDestroyObjectFileImage(img) NSDestroyObjectFileImage(img)
result = TLibHandle(modul) result = TLibHandle(modul)
if result == nil: 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 = proc nimGetProcAddr(lib: TLibHandle, cname: string): TProcAddr =
var var

View file

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

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf # (c) Copyright 2009 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -15,36 +15,9 @@
# * generational # * generational
# Future Improvements: # 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 # * Support for multi-threading. However, locks for the reference counting
# might turn out to be too slow. # 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() proc getOccupiedMem(): int = return tlsfUsed()
@ -54,38 +27,13 @@ proc getTotalMem(): int = return tlsfMax()
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
const 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 CycleIncrease = 2 # is a multiplicative increase
InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
ZctThreshold = 256 # we collect garbage if the ZCT's size ZctThreshold = 256 # we collect garbage if the ZCT's size
# reaches this threshold # reaches this threshold
# this seems to be a good value # this seems to be a good value
const 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 rcIncrement = 0b1000 # so that lowest 3 bits are not touched
# NOTE: Most colors are currently unused # NOTE: Most colors are currently unused
rcBlack = 0b000 # cell is colored black; in use or free rcBlack = 0b000 # cell is colored black; in use or free
@ -101,40 +49,9 @@ type
TWalkOp = enum TWalkOp = enum
waZctDecRef, waPush, waCycleDecRef 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) TFinalizer {.compilerproc.} = proc (self: pointer)
# A ref type can have a finalizer that is called before the object's # A ref type can have a finalizer that is called before the object's
# storage is freed. # 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 TGcHeap {.final, pure.} = object # this contains the zero count and
# non-zero count table # non-zero count table
mask: TAddress # mask for fast pointer detection mask: TAddress # mask for fast pointer detection
@ -152,7 +69,6 @@ type
tempStack: TCellSeq # temporary stack for recursion elimination tempStack: TCellSeq # temporary stack for recursion elimination
var var
gOutOfMem: ref EOutOfMemory
stackBottom: pointer stackBottom: pointer
gch: TGcHeap gch: TGcHeap
cycleThreshold: int = InitialCycleThreshold cycleThreshold: int = InitialCycleThreshold
@ -171,12 +87,10 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc.}
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} proc newObj(typ: PNimType, size: int): pointer {.compilerproc.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.}
proc raiseOutOfMem() {.noreturn.} = proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
if gOutOfMem == nil: if (c.refcount and colorMask) != rcZct:
writeToStdErr("out of memory; cannot even throw an exception") c.refcount = c.refcount and not colorMask or rcZct
quit(1) add(s, c)
gOutOfMem.msg = "out of memory"
raise gOutOfMem
proc cellToUsr(cell: PCell): pointer {.inline.} = proc cellToUsr(cell: PCell): pointer {.inline.} =
# convert object (=pointer to refcount) to pointer to userdata # 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 if result > 0: result = result shr rcShift
else: result = 0 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_disable() = inc(recGcLock)
proc GC_enable() = proc GC_enable() =
if recGcLock > 0: dec(recGcLock) if recGcLock > 0: dec(recGcLock)
@ -221,160 +130,10 @@ proc GC_disableMarkAndSweep() =
cycleThreshold = high(cycleThreshold)-1 cycleThreshold = high(cycleThreshold)-1
# set to the max value to suppress the cycle detector # 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: # this that has to equals zero, otherwise we have to round up UnitsPerPage:
when BitsPerPage mod BitsPerUnit != 0: when BitsPerPage mod BitsPerUnit != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".} {.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: when debugGC:
proc writeCell(msg: CString, c: PCell) = proc writeCell(msg: CString, c: PCell) =
var kind = -1 var kind = -1
@ -450,7 +209,6 @@ proc IsOnStack(p: pointer): bool {.noinline.}
proc forAllChildren(cell: PCell, op: TWalkOp) proc forAllChildren(cell: PCell, op: TWalkOp)
proc doOperation(p: pointer, op: TWalkOp) proc doOperation(p: pointer, op: TWalkOp)
proc forAllChildrenAux(dest: Pointer, mt: PNimType, 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 # we need the prototype here for debugging purposes
proc prepareDealloc(cell: PCell) = proc prepareDealloc(cell: PCell) =
@ -479,13 +237,13 @@ proc decRef(c: PCell) {.inline.} =
if c.refcount <% rcIncrement: if c.refcount <% rcIncrement:
addZCT(gch.zct, c) addZCT(gch.zct, c)
elif canBeCycleRoot(c): elif canBeCycleRoot(c):
possibleRoot(gch, c) incl(gch.cycleRoots, c)
proc incRef(c: PCell) {.inline.} = proc incRef(c: PCell) {.inline.} =
c.refcount = c.refcount +% rcIncrement c.refcount = c.refcount +% rcIncrement
if canBeCycleRoot(c): if canBeCycleRoot(c):
# OPT: the code generator should special case this # 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 nimGCref(p: pointer) {.compilerproc, inline.} = incRef(usrToCell(p))
proc nimGCunref(p: pointer) {.compilerproc, inline.} = decRef(usrToCell(p)) proc nimGCunref(p: pointer) {.compilerproc, inline.} = decRef(usrToCell(p))
@ -534,26 +292,6 @@ proc initGC() =
gch.mask = 0 gch.mask = 0
new(gOutOfMem) # reserve space for the EOutOfMemory exception here! 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) = proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
var d = cast[TAddress](dest) var d = cast[TAddress](dest)
case n.kind case n.kind
@ -749,7 +487,7 @@ proc collectCycles(gch: var TGcHeap) =
CellSetDeinit(gch.cycleRoots) CellSetDeinit(gch.cycleRoots)
gch.cycleRoots = newRoots 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: # the addresses are not as objects on the stack, so turn them to objects:
var cell = usrToCell(p) var cell = usrToCell(p)
var c = cast[TAddress](cell) var c = cast[TAddress](cell)
@ -759,7 +497,7 @@ proc gcMark(p: pointer) = # {.fastcall.} =
incl(gch.stackCells, cell) # yes: mark it incl(gch.stackCells, cell) # yes: mark it
# ----------------- stack management -------------------------------------- # ----------------- stack management --------------------------------------
# inspired from Smart Eiffel (c) # inspired from Smart Eiffel
proc stackSize(): int {.noinline.} = proc stackSize(): int {.noinline.} =
var stackTop: array[0..1, pointer] var stackTop: array[0..1, pointer]
@ -885,7 +623,7 @@ else:
# in a platform independant way # in a platform independant way
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
when true: when false:
# new version: several C compilers are too smart here # new version: several C compilers are too smart here
var var
max = cast[TAddress](stackBottom) max = cast[TAddress](stackBottom)
@ -910,7 +648,7 @@ else:
max = stackBottom max = stackBottom
registers: C_JmpBuf # The jmp_buf buffer is in the C stack. registers: C_JmpBuf # The jmp_buf buffer is in the C stack.
sp: PPointer # Used to traverse the stack and registers assuming 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. if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
sp = cast[ppointer](addr(registers)) sp = cast[ppointer](addr(registers))
while sp <= max: while sp <= max:
@ -937,7 +675,7 @@ proc updateZCT() =
dec(L) dec(L)
d[j] = d[L] d[j] = d[L]
c.refcount = c.refcount and not colorMask 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: else:
inc(j) inc(j)
gch.zct.len = L gch.zct.len = L

View file

@ -58,12 +58,12 @@ type
nnkTypeDef, nnkYieldStmt, nnkTryStmt, nnkFinally, nnkTypeDef, nnkYieldStmt, nnkTryStmt, nnkFinally,
nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt,
nnkBlockStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkBlockStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt,
nnkFromStmt, nnkImportAs, nnkIncludeStmt, nnkAccessStmt, nnkFromStmt, nnkImportAs, nnkIncludeStmt, nnkCommentStmt,
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkStmtListType, nnkStmtListExpr, nnkBlockExpr, nnkStmtListType, nnkBlockType,
nnkBlockType, nnkVm, nnkTypeOfExpr, nnkObjectTy, nnkVm, nnkTypeOfExpr, nnkObjectTy, nnkTupleTy,
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkRecList, nnkRecCase, nnkRecWhen, nnkRefTy,
nnkRefTy, nnkPtrTy, nnkVarTy, nnkProcTy, nnkPtrTy, nnkVarTy, nnkProcTy, nnkEnumTy,
nnkEnumTy, nnkEnumFieldDef, nnkReturnToken nnkEnumFieldDef, nnkReturnToken
TNimNodeKinds* = set[TNimrodNodeKind] TNimNodeKinds* = set[TNimrodNodeKind]
TNimrodTypeKind* = enum TNimrodTypeKind* = enum
ntyNone, ntyBool, ntyChar, ntyEmpty, ntyNone, ntyBool, ntyChar, ntyEmpty,
@ -86,12 +86,24 @@ type
#[[[end]]] #[[[end]]]
type type
TNimrodIdent = object of TObject
## represents a Nimrod identifier in the AST
TNimrodNode {.final.} = object # hidden TNimrodNode {.final.} = object # hidden
TNimrodSymbol {.final.} = object # hidden TNimrodSymbol {.final.} = object # hidden
TNimrodType {.final.} = object # hidden TNimrodType {.final.} = object # hidden
PNimrodType* {.compilerproc.} = ref TNimrodType 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 PNimrodSymbol* {.compilerproc.} = ref TNimrodSymbol
## represents a Nimrod *symbol* in the compiler; a *symbol* is a looked-up
## *ident*.
PNimrodNode* {.compilerproc.} = ref TNimrodNode PNimrodNode* {.compilerproc.} = ref TNimrodNode
## represents a Nimrod AST node. Macros operate on this type.
expr* = PNimrodNode expr* = PNimrodNode
stmt* = PNimrodNode stmt* = PNimrodNode
@ -100,23 +112,35 @@ type
# its father. How to do this without back references? # its father. How to do this without back references?
proc `[]`* (n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".} proc `[]`* (n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
## get `n`'s `i`'th child.
proc `[]=`* (n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".} proc `[]=`* (n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
## provide access to `n`'s children ## set `n`'s `i`'th child to `child`.
type proc `!` *(s: string): TNimrodIdent {.magic: "StrToIdent".}
TNimrodIdent = object of TObject ## constructs an identifier from the string `s`
converter StrToIdent*(s: string): TNimrodIdent {.magic: "StrToIdent".}
proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".} proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
## converts a Nimrod identifier to a string
proc `==`* (a, b: TNimrodIdent): bool {.magic: "EqIdent".} proc `==`* (a, b: TNimrodIdent): bool {.magic: "EqIdent".}
## compares two Nimrod identifiers
proc len*(n: PNimrodNode): int {.magic: "NLen".} 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, 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".} 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".} proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}
## returns the `kind` of the node `n`.
proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".} proc intVal*(n: PNimrodNode): biggestInt {.magic: "NIntVal".}
proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".} proc floatVal*(n: PNimrodNode): biggestFloat {.magic: "NFloatVal".}
@ -134,38 +158,67 @@ proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
proc newNimNode*(kind: TNimrodNodeKind, proc newNimNode*(kind: TNimrodNodeKind,
n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".} n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".} proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}
proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".} proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}
proc error*(msg: string) {.magic: "NError".} proc error*(msg: string) {.magic: "NError".}
## writes an error message at compile time
proc warning*(msg: string) {.magic: "NWarning".} proc warning*(msg: string) {.magic: "NWarning".}
## writes a warning message at compile time
proc hint*(msg: string) {.magic: "NHint".} proc hint*(msg: string) {.magic: "NHint".}
## writes a hint message at compile time
proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} = proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} =
## creates a string literal node from `s`
result = newNimNode(nnkStrLit) result = newNimNode(nnkStrLit)
result.strVal = s result.strVal = s
proc newIntLitNode*(i: biggestInt): PNimrodNode {.compileTime.} = proc newIntLitNode*(i: biggestInt): PNimrodNode {.compileTime.} =
## creates a int literal node from `i`
result = newNimNode(nnkIntLit) result = newNimNode(nnkIntLit)
result.intVal = i 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 = newNimNode(nnkFloatLit)
result.floatVal = f result.floatVal = f
proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} = proc newIdentNode*(i: TNimrodIdent): PNimrodNode {.compileTime.} =
## creates an identifier node from `i`
result = newNimNode(nnkIdent) result = newNimNode(nnkIdent)
result.ident = i 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.} = 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)) return newStrLitNode(repr(n))
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} = 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)) if n.kind != k: error("macro expects a node of kind: " & repr(k))
proc expectMinLen*(n: PNimrodNode, min: int) {.compileTime.} = 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") 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, proc newCall*(theProc: TNimrodIdent,
args: openArray[PNimrodNode]): PNimrodNode {.compileTime.} = args: openArray[PNimrodNode]): PNimrodNode {.compileTime.} =
## produces a new call node. `theProc` is the proc that is called with ## produces a new call node. `theProc` is the proc that is called with
@ -173,3 +226,12 @@ proc newCall*(theProc: TNimrodIdent,
result = newNimNode(nnkCall) result = newNimNode(nnkCall)
result.add(newIdentNode(theProc)) result.add(newIdentNode(theProc))
result.add(args) 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)

View file

@ -51,6 +51,20 @@ proc classify*(x: float): TFloatClass =
# XXX: fcSubnormal is not detected! # 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.} = proc isPowerOfTwo*(x: int): bool {.noSideEffect.} =
## returns true, if x is a power of two, false otherwise. ## returns true, if x is a power of two, false otherwise.
## Negative numbers are not a power of two. ## Negative numbers are not a power of two.
@ -74,6 +88,25 @@ proc countBits*(n: int32): int {.noSideEffect.}
include cntbits 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): when not defined(ECMAScript):
proc random*(max: int): int proc random*(max: int): int

View file

@ -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.} = proc extractBlockHdr(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
t.matrix[fl][sl] = b.freePtr.next 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 t.matrix[fl][sl].freePtr.prev = nil
else: else:
clear_bit(sl, t.slBitmap[fl]) clear_bit(sl, t.slBitmap[fl])

View file

@ -1,12 +1,24 @@
/* /*
Nimrod's Runtime Library Nimrod's Runtime Library
(c) Copyright 2008 Andreas Rumpf (c) Copyright 2009 Andreas Rumpf
See the file "copying.txt", included in this See the file "copying.txt", included in this
distribution, for details about the copyright. distribution, for details about the copyright.
*/ */
/* compiler symbols:
__BORLANDC__
_MSC_VER
__WATCOMC__
__LCC__
__GNUC__
__DMC__
__POCC__
__TINYC__
*/
#ifndef NIMBASE_H #ifndef NIMBASE_H
#define NIMBASE_H #define NIMBASE_H
@ -239,17 +251,6 @@ static unsigned long nimInf[2]={0xffffffff, 0x7fffffff};
# define INF (*(double*) nimInf) # define INF (*(double*) nimInf)
#endif */ #endif */
/* compiler symbols:
__BORLANDC__
_MSC_VER
__WATCOMC__
__LCC__
__GNUC__
__DMC__
__POCC__
__TINYC__
*/
/* C99 compiler? */ /* C99 compiler? */
#if (defined(__STD_VERSION__) && (__STD_VERSION__ >= 199901)) #if (defined(__STD_VERSION__) && (__STD_VERSION__ >= 199901))
# define HAVE_STDINT_H # define HAVE_STDINT_H

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf # (c) Copyright 2009 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -178,12 +178,14 @@ proc ExistsFile*(filename: string): bool
proc JoinPath*(head, tail: string): string {.noSideEffect.} proc JoinPath*(head, tail: string): string {.noSideEffect.}
## Joins two directory names to one. ## Joins two directory names to one.
## ##
## For example on Unix:: ## For example on Unix:
## ##
## ..code-block:: nimrod
## JoinPath("usr", "lib") ## JoinPath("usr", "lib")
## ##
## results in:: ## results in:
## ##
## ..code-block:: nimrod
## "usr/lib" ## "usr/lib"
## ##
## If head is the empty string, tail is returned. ## If head is the empty string, tail is returned.
@ -312,7 +314,7 @@ proc createDir*(dir: string)
## ##
## The directory may contain several ## The directory may contain several
## subdirectories that do not exist yet. The full path is created. If this ## 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. ## because for most usages this does not indicate an error.
proc existsDir*(dir: string): bool proc existsDir*(dir: string): bool
@ -650,8 +652,7 @@ proc searchExtPos(s: string): int =
break # do not skip over path break # do not skip over path
proc SplitFilename(filename: string, name, extension: var string) = proc SplitFilename(filename: string, name, extension: var string) =
var var extPos = searchExtPos(filename)
extPos = searchExtPos(filename)
if extPos >= 0: if extPos >= 0:
name = copy(filename, 0, extPos-1) name = copy(filename, 0, extPos-1)
extension = copy(filename, extPos) extension = copy(filename, extPos)
@ -664,14 +665,12 @@ proc normExt(ext: string): string =
else: result = extSep & ext else: result = extSep & ext
proc ChangeFileExt(filename, ext: string): string = proc ChangeFileExt(filename, ext: string): string =
var var extPos = searchExtPos(filename)
extPos = searchExtPos(filename)
if extPos < 0: result = filename & normExt(ext) if extPos < 0: result = filename & normExt(ext)
else: result = copy(filename, 0, extPos-1) & normExt(ext) else: result = copy(filename, 0, extPos-1) & normExt(ext)
proc AppendFileExt(filename, ext: string): string = proc AppendFileExt(filename, ext: string): string =
var var extPos = searchExtPos(filename)
extPos = searchExtPos(filename)
if extPos < 0: result = filename & normExt(ext) if extPos < 0: result = filename & normExt(ext)
else: result = filename #make a string copy here else: result = filename #make a string copy here
@ -850,12 +849,12 @@ proc rawCreateDir(dir: string) =
OSError() OSError()
proc createDir(dir: string) = 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)) if dir[i] in {dirsep, altsep}: rawCreateDir(copy(dir, 0, i-1))
rawCreateDir(dir) rawCreateDir(dir)
proc executeShellCommand(command: string): int = proc executeShellCommand(command: string): int =
return csystem(command) result = csystem(command)
var var
envComputed: bool = false envComputed: bool = false
@ -892,8 +891,7 @@ else:
proc getEnvVarsC() = proc getEnvVarsC() =
# retrieves the variables of char** env of C's main proc # retrieves the variables of char** env of C's main proc
if not envComputed: if not envComputed:
var var i = 0
i: int = 0
while True: while True:
if gEnv[i] == nil: break if gEnv[i] == nil: break
add environment, $gEnv[i] add environment, $gEnv[i]
@ -904,13 +902,13 @@ proc findEnvVar(key: string): int =
getEnvVarsC() getEnvVarsC()
var temp = key & '=' var temp = key & '='
for i in 0..high(environment): for i in 0..high(environment):
if findSubStr(temp, environment[i]) == 0: return i if startsWith(environment[i], temp): return i
return -1 return -1
proc getEnv(key: string): string = proc getEnv(key: string): string =
var i = findEnvVar(key) var i = findEnvVar(key)
if i >= 0: if i >= 0:
return copy(environment[i], findSubStr("=", environment[i])+1) return copy(environment[i], find(environment[i], '=')+1)
else: else:
var env = cgetenv(key) var env = cgetenv(key)
if env == nil: return "" 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. ## tuple is the name of the current variable stored, in the second its value.
getEnvVarsC() getEnvVarsC()
for i in 0..high(environment): 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)) yield (copy(environment[i], 0, p-1), copy(environment[i], p+1))
proc putEnv(key, val: string) = proc putEnv(key, val: string) =
@ -1140,7 +1138,7 @@ else:
proc GetConfigDir(): string = return getEnv("HOME") & "/.config/" proc GetConfigDir(): string = return getEnv("HOME") & "/.config/"
var var
cmdCount {.importc: "cmdCount".}: int cmdCount {.importc: "cmdCount".}: cint
cmdLine {.importc: "cmdLine".}: cstringArray cmdLine {.importc: "cmdLine".}: cstringArray
proc paramStr(i: int): string = proc paramStr(i: int): string =

View file

@ -10,7 +10,7 @@
## The ``parsecfg`` module implements a high performance configuration file ## The ``parsecfg`` module implements a high performance configuration file
## parser. The configuration file's syntax is similar to the Windows ``.ini`` ## 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 ## 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. ## as in the Nimrod programming language.
## This is an example of how a configuration file may look like: ## 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) inc(c.bufpos)
proc getEscapedChar(c: var TCfgParser, tok: var TToken) = proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
var xi: int
inc(c.bufpos) # skip '\' inc(c.bufpos) # skip '\'
case c.buf[c.bufpos] case c.buf[c.bufpos]
of 'n', 'N': of 'n', 'N':
@ -173,12 +172,12 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
Inc(c.bufpos) Inc(c.bufpos)
of 'x', 'X': of 'x', 'X':
inc(c.bufpos) inc(c.bufpos)
xi = 0 var xi = 0
handleHexChar(c, xi) handleHexChar(c, xi)
handleHexChar(c, xi) handleHexChar(c, xi)
add(tok.literal, Chr(xi)) add(tok.literal, Chr(xi))
of '0'..'9': of '0'..'9':
xi = 0 var xi = 0
handleDecChars(c, xi) handleDecChars(c, xi)
if (xi <= 255): add(tok.literal, Chr(xi)) if (xi <= 255): add(tok.literal, Chr(xi))
else: tok.kind = tkInvalid else: tok.kind = tkInvalid
@ -191,12 +190,8 @@ proc HandleCRLF(c: var TCfgParser, pos: int): int =
else: result = pos else: result = pos
proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) = proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
var var pos = c.bufPos + 1 # skip "
pos: int var buf = c.buf # put `buf` in a register
ch: Char
buf: cstring
pos = c.bufPos + 1 # skip "
buf = c.buf # put `buf` in a register
tok.kind = tkSymbol tok.kind = tkSymbol
if (buf[pos] == '\"') and (buf[pos + 1] == '\"'): if (buf[pos] == '\"') and (buf[pos + 1] == '\"'):
# long string literal: # long string literal:
@ -211,19 +206,18 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
Inc(pos) Inc(pos)
of '\c', '\L': of '\c', '\L':
pos = HandleCRLF(c, pos) pos = HandleCRLF(c, pos)
tok.literal = tok.literal & nl add(tok.literal, nl)
of lexbase.EndOfFile: of lexbase.EndOfFile:
tok.kind = tkInvalid tok.kind = tkInvalid
break break
else: else:
add(tok.literal, buf[pos]) add(tok.literal, buf[pos])
Inc(pos) Inc(pos)
c.bufpos = pos + c.bufpos = pos + 3 # skip the three """
3 # skip the three """
else: else:
# ordinary string literal # ordinary string literal
while true: while true:
ch = buf[pos] var ch = buf[pos]
if ch == '\"': if ch == '\"':
inc(pos) # skip '"' inc(pos) # skip '"'
break break
@ -240,11 +234,8 @@ proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) =
c.bufpos = pos c.bufpos = pos
proc getSymbol(c: var TCfgParser, tok: var TToken) = proc getSymbol(c: var TCfgParser, tok: var TToken) =
var var pos = c.bufpos
pos: int var buf = c.buf
buf: cstring
pos = c.bufpos
buf = c.buf
while true: while true:
add(tok.literal, buf[pos]) add(tok.literal, buf[pos])
Inc(pos) Inc(pos)
@ -253,11 +244,8 @@ proc getSymbol(c: var TCfgParser, tok: var TToken) =
tok.kind = tkSymbol tok.kind = tkSymbol
proc skip(c: var TCfgParser) = proc skip(c: var TCfgParser) =
var var pos = c.bufpos
buf: cstring var buf = c.buf
pos: int
pos = c.bufpos
buf = c.buf
while true: while true:
case buf[pos] case buf[pos]
of ' ', '\t': of ' ', '\t':

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf # (c) Copyright 2009 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -73,8 +73,7 @@ proc parseWord(s: string, i: int, w: var string,
inc(result) inc(result)
proc handleShortOption(p: var TOptParser) = proc handleShortOption(p: var TOptParser) =
var i: int var i = p.pos
i = p.pos
p.kind = cmdShortOption p.kind = cmdShortOption
add(p.key, p.cmd[i]) add(p.key, p.cmd[i])
inc(i) inc(i)
@ -91,8 +90,7 @@ proc handleShortOption(p: var TOptParser) =
p.pos = i p.pos = i
proc next(p: var TOptParser) = proc next(p: var TOptParser) =
var i: int var i = p.pos
i = p.pos
while p.cmd[i] in {'\x09', ' '}: inc(i) while p.cmd[i] in {'\x09', ' '}: inc(i)
p.pos = i p.pos = i
setlen(p.key, 0) setlen(p.key, 0)

File diff suppressed because it is too large Load diff

View file

@ -104,17 +104,17 @@ proc reprSet(p: pointer, typ: PNimType): string {.compilerproc.} =
type type
TReprClosure {.final.} = object # we cannot use a global variable here TReprClosure {.final.} = object # we cannot use a global variable here
# as this wouldn't be thread-safe # as this wouldn't be thread-safe
marked: TCellSet marked: TCellSeq
recdepth: int # do not recurse endless recdepth: int # do not recurse endless
indent: int # indentation indent: int # indentation
proc initReprClosure(cl: var TReprClosure) = proc initReprClosure(cl: var TReprClosure) =
CellSetInit(cl.marked) Init(cl.marked)
cl.recdepth = -1 # default is to display everything! cl.recdepth = -1 # default is to display everything!
cl.indent = 0 cl.indent = 0
proc deinitReprClosure(cl: var TReprClosure) = proc deinitReprClosure(cl: var TReprClosure) =
CellSetDeinit(cl.marked) Deinit(cl.marked)
proc reprBreak(result: var string, cl: TReprClosure) = proc reprBreak(result: var string, cl: TReprClosure) =
add result, "\n" add result, "\n"
@ -145,7 +145,6 @@ proc reprSequence(result: var string, p: pointer, typ: PNimType,
typ.Base, cl) typ.Base, cl)
add result, "]" add result, "]"
proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode, proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode,
cl: var TReprClosure) = cl: var TReprClosure) =
case n.kind 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, proc reprRef(result: var string, p: pointer, typ: PNimType,
cl: var TReprClosure) = cl: var TReprClosure) =
# we know that p is not nil here: # 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) add result, "ref " & reprPointer(p)
if cell notin cl.marked: if cell notin cl.marked:
# only the address is shown: # only the address is shown:
incl(cl.marked, cell) add(cl.marked, cell)
add result, " --> " add result, " --> "
reprAux(result, p, typ.base, cl) reprAux(result, p, typ.base, cl)

View file

@ -100,7 +100,7 @@ proc readLine*(s: PStream): string =
type type
PStringStream* = ref TStringStream ## a stream that encapsulates a string PStringStream* = ref TStringStream ## a stream that encapsulates a string
TStringStream* = object of TStream TStringStream* = object of TStream
data: string data*: string
pos: int pos: int
proc ssAtEnd(s: PStringStream): bool = proc ssAtEnd(s: PStringStream): bool =

View file

@ -16,10 +16,10 @@ import
os, hashes, strutils os, hashes, strutils
type type
TStringTableMode* = enum # describes the tables operation mode TStringTableMode* = enum ## describes the tables operation mode
modeCaseSensitive, # the table is case sensitive modeCaseSensitive, ## the table is case sensitive
modeCaseInsensitive, # the table is case insensitive modeCaseInsensitive, ## the table is case insensitive
modeStyleInsensitive # the table is style insensitive modeStyleInsensitive ## the table is style insensitive
TKeyValuePair = tuple[key, val: string] TKeyValuePair = tuple[key, val: string]
TKeyValuePairSeq = seq[TKeyValuePair] TKeyValuePairSeq = seq[TKeyValuePair]
TStringTable* = object of TObject 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) yield (t.data[h].key, t.data[h].val)
type type
TFormatFlag* = enum # flags for the `%` operator TFormatFlag* = enum ## flags for the `%` operator
useEnvironment, # use environment variable if the ``$key`` useEnvironment, ## use environment variable if the ``$key``
# is not found in the table ## is not found in the table
useEmpty, # use the empty string as a default, thus it useEmpty, ## use the empty string as a default, thus it
# won't throw an exception if ``$key`` is not ## won't throw an exception if ``$key`` is not
# in the table ## in the table
useKey # do not replace ``$key`` if it is not found useKey ## do not replace ``$key`` if it is not found
# in the table (or in the environment) ## in the table (or in the environment)
proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string
## The `%` operator for string tables. ## The `%` operator for string tables.
@ -110,9 +110,7 @@ proc mustRehash(length, counter: int): bool =
result = (length * 2 < counter * 3) or (length - counter < 4) result = (length * 2 < counter * 3) or (length - counter < 4)
proc nextTry(h, maxHash: THash): THash = proc nextTry(h, maxHash: THash): THash =
result = ((5 * h) + 1) and maxHash # For any initial h in range(maxHash), repeating that maxHash times result = ((5 * h) + 1) and maxHash
# generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof).
proc RawGet(t: PStringTable, key: string): int = proc RawGet(t: PStringTable, key: string): int =
var h: THash 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 hasKey(t, key): return t[key]
if useEnvironment in flags: result = os.getEnv(key) if useEnvironment in flags: result = os.getEnv(key)
else: result = "" else: result = ""
if (result == ""): if result.len == 0:
if useKey in flags: result = '$' & key if useKey in flags: result = '$' & key
elif not (useEmpty in flags): raiseFormatException(key) elif not (useEmpty in flags): raiseFormatException(key)
proc `%`(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string = proc `%`(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string =
const const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'} PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
var
i, j: int
key: string
result = "" result = ""
i = strStart var i = 0
while i <= len(f) + strStart - 1: while i < len(f):
if f[i] == '$': if f[i] == '$':
case f[i + 1] case f[i+1]
of '$': of '$':
add(result, '$') add(result, '$')
inc(i, 2) inc(i, 2)
of '{': of '{':
j = i + 1 var j = i + 1
while (j <= len(f) + strStart - 1) and (f[j] != '}'): inc(j) while j < f.len and f[j] != '}': inc(j)
key = copy(f, i + 2, j - 1) add(result, getValue(t, flags, copy(f, i+2, j-1)))
result = result & getValue(t, flags, key)
i = j + 1 i = j + 1
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_': of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '_':
j = i + 1 var j = i + 1
while (j <= len(f) + strStart - 1) and (f[j] in PatternChars): inc(j) while j < f.len and f[j] in PatternChars: inc(j)
key = copy(f, i+1, j - 1) add(result, getValue(t, flags, copy(f, i+1, j-1)))
result = result & getValue(t, flags, key)
i = j i = j
else: else:
add(result, f[i]) add(result, f[i])

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2006 Andreas Rumpf # (c) Copyright 2009 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -9,8 +9,7 @@
## This module contains various string utility routines. ## This module contains various string utility routines.
## See the module `regexprs` for regular expression support. ## See the module `regexprs` for regular expression support.
## All the routines here are avaiable for the EMCAScript target ## All the routines here are avaiable for the EMCAScript target too!
## too!
{.deadCodeElim: on.} {.deadCodeElim: on.}
@ -34,12 +33,65 @@ const
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'} Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
## All the characters that count as whitespace. ## 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 strStart* = 0 # this is only for bootstraping
# XXX: remove this someday # XXX: remove this someday
nl* = "\n" # this is only for bootstraping XXX: remove this somehow nl* = "\n" # this is only for bootstraping XXX: remove this somehow
proc strip*(s: string): string {.noSideEffect.} proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
## Strips leading and trailing whitespace from `s`. ## 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.} proc toLower*(s: string): string {.noSideEffect.}
## Converts `s` into lower case. This works only for the letters A-Z. ## 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 ## Normalizes the string `s`. That means to convert it to lower case and
## remove any '_'. This is needed for Nimrod identifiers for example. ## 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 ## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned. ## 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 ## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned. ## 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 ## Searches for `chars` in `s` starting at position `start`. If `s` contains
## none of the characters in `chars`, -1 is returned. ## 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. ## Splits the string `s` into substrings.
## ##
## Substrings are separated by a substring containing only `seps`. ## Substrings are separated by a substring containing only `seps`.
## The seperator substrings are not returned in `sub`, nor are they part ## Examples:
## of `sub`.
## Examples::
## ##
## .. code-block:: nimrod
## for word in split(" this is an example "): ## for word in split(" this is an example "):
## writeln(stdout, word) ## writeln(stdout, word)
## ##
## Results in:: ## Results in:
## ##
## .. code-block:: nimrod
## "this" ## "this"
## "is" ## "is"
## "an" ## "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! while last < len(s) and s[last] not_in seps: inc(last) # BUGFIX!
yield copy(s, first, last-1) 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 = iterator splitLines*(s: string): string =
## Splits the string `s` into its containing lines. Each newline ## Splits the string `s` into its containing lines. Each newline
## combination (CR, LF, CR-LF) is supported. The result strings contain ## combination (CR, LF, CR-LF) is supported. The result strings contain
## no trailing ``\n``. ## no trailing ``\n``.
## ##
## Example:: ## Example:
## ##
## .. code-block:: nimrod
## for line in lines("\nthis\nis\nan\n\nexample\n"): ## for line in lines("\nthis\nis\nan\n\nexample\n"):
## writeln(stdout, line) ## writeln(stdout, line)
## ##
## Results in:: ## Results in:
## ##
## .. code-block:: nimrod
## "" ## ""
## "this" ## "this"
## "is" ## "is"
@ -164,6 +273,11 @@ proc splitSeq*(s: string, seps: set[char] = Whitespace): seq[string] {.
noSideEffect.} noSideEffect.}
## The same as `split`, but is a proc that returns a sequence of substrings. ## 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.} proc cmpIgnoreCase*(a, b: string): int {.noSideEffect.}
## Compares two strings in a case insensitive manner. Returns: ## 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 ## Parses a decimal integer value contained in `s`. If `s` is not
## a valid integer, `EInvalidValue` is raised. ## 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 ## Parses a decimal floating point value contained in `s`. If `s` is not
## a valid floating point number, `EInvalidValue` is raised. ``NAN``, ## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
## ``INF``, ``-INF`` are also supported (case insensitive comparison). ## ``INF``, ``-INF`` are also supported (case insensitive comparison).
@ -217,37 +331,6 @@ proc ParseFloat*(s: string): float {.noSideEffect.}
proc toString*[Ty](x: Ty): string proc toString*[Ty](x: Ty): string
## This generic proc is the same as the stringify operator `$`. ## 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 proc repeatChar*(count: int, c: Char = ' '): string
## Returns a string of length `count` consisting only of ## Returns a string of length `count` consisting only of
## the character `c`. ## the character `c`.
@ -260,7 +343,25 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect.}
## Returns true iff ``s`` ends with ``suffix``. ## Returns true iff ``s`` ends with ``suffix``.
## If ``suffix == ""`` true is returned. ## 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 = proc allCharsInSet*(s: string, theSet: TCharSet): bool =
## returns true iff each character of `s` is in the set `theSet`. ## 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 = proc quoteIfContainsWhite*(s: string): string =
## returns ``'"' & s & '"'`` if `s` contains a space and does not ## returns ``'"' & s & '"'`` if `s` contains a space and does not
## start with a quote, else returns `s` ## 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 & '"' result = '"' & s & '"'
else: else:
result = s result = s
@ -307,10 +408,8 @@ proc intToStr(x: int, minchars: int = 1): string =
proc toString[Ty](x: Ty): string = return $x proc toString[Ty](x: Ty): string = return $x
proc toOctal(c: char): string = proc toOctal(c: char): string =
var
val: int
result = newString(3) result = newString(3)
val = ord(c) var val = ord(c)
for i in countdown(2, 0): for i in countdown(2, 0):
result[i] = Chr(val mod 8 + ord('0')) result[i] = Chr(val mod 8 + ord('0'))
val = val div 8 val = val div 8
@ -326,18 +425,15 @@ proc findNormalized(x: string, inArray: openarray[string]): int =
# security whole ... # security whole ...
return -1 return -1
proc `%`(formatstr: string, a: openarray[string]): string = proc addf(s: var string, formatstr: string, a: openarray[string]) =
# the format operator const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
result = ""
var i = 0 var i = 0
while i < len(formatstr): while i < len(formatstr):
if formatstr[i] == '$': if formatstr[i] == '$':
case formatstr[i+1] # again we use the fact that strings case formatstr[i+1] # again we use the fact that strings
# are zero-terminated here # are zero-terminated here
of '$': of '$':
add result, '$' add s, '$'
inc(i, 2) inc(i, 2)
of '1'..'9': of '1'..'9':
var j = 0 var j = 0
@ -345,26 +441,30 @@ proc `%`(formatstr: string, a: openarray[string]): string =
while formatstr[i] in {'0'..'9'}: while formatstr[i] in {'0'..'9'}:
j = j * 10 + ord(formatstr[i]) - ord('0') j = j * 10 + ord(formatstr[i]) - ord('0')
inc(i) inc(i)
add result, a[j - 1] add s, a[j - 1]
of '{': of '{':
var j = i+1 var j = i+1
while formatstr[j] notin {'\0', '}'}: inc(j) while formatstr[j] notin {'\0', '}'}: inc(j)
var x = findNormalized(copy(formatstr, i+2, j-1), a) 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") else: raise newException(EInvalidValue, "invalid format string")
i = j+1 i = j+1
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_': of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
var j = i+1 var j = i+1
while formatstr[j] in PatternChars: inc(j) while formatstr[j] in PatternChars: inc(j)
var x = findNormalized(copy(formatstr, i+1, j-1), a) 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") else: raise newException(EInvalidValue, "invalid format string")
i = j i = j
else: raise newException(EInvalidValue, "invalid format string") else: raise newException(EInvalidValue, "invalid format string")
else: else:
add result, formatstr[i] add s, formatstr[i]
inc(i) inc(i)
proc `%`(formatstr: string, a: openarray[string]): string =
result = ""
addf(result, formatstr, a)
proc cmpIgnoreCase(a, b: string): int = proc cmpIgnoreCase(a, b: string): int =
# makes usage of the fact that strings are zero-terminated # makes usage of the fact that strings are zero-terminated
for i in 0..len(a)-1: for i in 0..len(a)-1:
@ -377,9 +477,8 @@ proc cmpIgnoreCase(a, b: string): int =
# thus we compile without checks here # thus we compile without checks here
proc cmpIgnoreStyle(a, b: string): int = proc cmpIgnoreStyle(a, b: string): int =
var var i = 0
i = 0 var j = 0
j = 0
while True: while True:
while a[i] == '_': inc(i) while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo 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 const
chars: set[Char] = Whitespace chars: set[Char] = Whitespace
var var
first = 0 first = 0
last = len(s)-1 last = len(s)-1
while s[first] in chars: inc(first) if leading:
while last >= 0 and s[last] in chars: dec(last) while s[first] in chars: inc(first)
if trailing:
while last >= 0 and s[last] in chars: dec(last)
result = copy(s, first, last) result = copy(s, first, last)
proc toLower(c: Char): Char = 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..0xff: a[chr(i)] = m+1
for i in 0..m-1: a[sub[i]] = m-i 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: # fast "quick search" algorithm:
var var
m = len(sub) 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 = proc findSubStr(sub, s: string, start: int = 0): int =
var a: TSkipTable var a: TSkipTable
preprocessSub(sub, a) preprocessSub(sub, a)
result = findSubStrAux(sub, s, start, a) result = findSubStrAux(s, sub, start, a)
# slow linear search: # slow linear search:
#var #var
# i, j, M, N: int # i, j, M, N: int
@ -492,6 +593,20 @@ proc findSubStr(sub, s: string, start: int = 0): int =
# elif (i >= N): # elif (i >= N):
# return -1 # 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 = proc findSubStr(sub: char, s: string, start: int = 0): int =
for i in start..len(s)-1: for i in start..len(s)-1:
@ -504,23 +619,21 @@ proc findChars(chars: set[char], s: string, start: int = 0): int =
return -1 return -1
proc contains(s: string, chars: set[char]): bool = 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 = proc contains(s: string, c: char): bool =
return findSubStr(c, s) >= 0 return find(s, c) >= 0
proc contains(s, sub: string): bool = proc contains(s, sub: string): bool =
return findSubStr(sub, s) >= 0 return find(s, sub) >= 0
proc replaceStr(s, sub, by: string): string = proc replaceStr(s, sub, by: string): string =
var var a: TSkipTable
i, j: int
a: TSkipTable
result = "" result = ""
preprocessSub(sub, a) preprocessSub(sub, a)
i = 0 var i = 0
while true: while true:
j = findSubStrAux(sub, s, i, a) var j = findSubStrAux(s, sub, i, a)
if j < 0: break if j < 0: break
add result, copy(s, i, j - 1) add result, copy(s, i, j - 1)
add result, by add result, by
@ -583,7 +696,10 @@ proc rawParseInt(s: string, index: var int): BiggestInt =
while s[i] == '_': while s[i] == '_':
inc(i) # underscores are allowed and ignored inc(i) # underscores are allowed and ignored
result = result * sign result = result * sign
index = i # store index back if s[i] == '\0':
index = i # store index back
else:
index = -1 # BUGFIX: error!
else: else:
index = -1 index = -1
@ -602,17 +718,17 @@ proc parseInt(s: string): int =
result = int(res) # convert to smaller integer type result = int(res) # convert to smaller integer type
proc ParseBiggestInt(s: string): biggestInt = proc ParseBiggestInt(s: string): biggestInt =
var var index = 0
index: int = 0
result = rawParseInt(s, index) result = rawParseInt(s, index)
if index == -1: if index == -1:
raise newException(EInvalidValue, "invalid integer: " & s) raise newException(EInvalidValue, "invalid integer: " & s)
proc ParseFloat(s: string): float = proc ParseFloat(s: string, start = 0): float =
var var
esign = 1.0 esign = 1.0
sign = 1.0 sign = 1.0
exponent, i: int i = start
exponent: int
flags: int flags: int
result = 0.0 result = 0.0
if s[i] == '+': inc(i) if s[i] == '+': inc(i)
@ -677,7 +793,7 @@ proc ParseFloat(s: string): float =
proc toOct*(x: BiggestInt, len: int): string = proc toOct*(x: BiggestInt, len: int): string =
## converts `x` into its octal representation. The resulting string is ## 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 var
mask: BiggestInt = 7 mask: BiggestInt = 7
shift: BiggestInt = 0 shift: BiggestInt = 0
@ -701,7 +817,7 @@ proc toBin*(x: BiggestInt, len: int): string =
shift = shift + 1 shift = shift + 1
mask = mask shl 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): ## Escapes a string `s`. This does these operations (at the same time):
## * replaces any ``\`` by ``\\`` ## * replaces any ``\`` by ``\\``
## * replaces any ``'`` by ``\'`` ## * replaces any ``'`` by ``\'``
@ -723,8 +839,34 @@ proc escape*(s: string, prefix, suffix = "\""): string =
else: add(result, c) else: add(result, c)
add(result, suffix) 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 = 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 ## distance algorithm with only a linear memory overhead. This implementation
## is highly optimized! ## is highly optimized!
var len1 = a.len var len1 = a.len

View file

@ -37,16 +37,21 @@ proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
proc rawNewString(space: int): NimString {.compilerProc.} = proc rawNewString(space: int): NimString {.compilerProc.} =
var s = space var s = space
if s < 8: s = 7 if s < 8: s = 7
result = cast[NimString](newObj(addr(strDesc), sizeof(TGenericSeq) + when defined(boehmGC):
(s+1) * sizeof(char))) result = cast[NimString](boehmAllocAtomic(
#result.len = 0 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.space = s
#result.data[0] = '\0'
proc mnewString(len: int): NimString {.exportc.} = proc mnewString(len: int): NimString {.exportc.} =
result = rawNewString(len) result = rawNewString(len)
result.len = len result.len = len
#result.data[len] = '\0' when defined(boehmGC):
result.data[len] = '\0'
proc toNimStr(str: CString, len: int): NimString {.compilerProc.} = proc toNimStr(str: CString, len: int): NimString {.compilerProc.} =
result = rawNewString(len) result = rawNewString(len)
@ -224,10 +229,11 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
GenericSeqSize)) GenericSeqSize))
elif newLen < result.len: elif newLen < result.len:
# we need to decref here, otherwise the GC leaks! # we need to decref here, otherwise the GC leaks!
for i in newLen..result.len-1: when not defined(boehmGC):
forAllChildrenAux(cast[pointer](cast[TAddress](result) +% for i in newLen..result.len-1:
GenericSeqSize +% (i*%elemSize)), forAllChildrenAux(cast[pointer](cast[TAddress](result) +%
extGetCellType(result).base, waZctDecRef) GenericSeqSize +% (i*%elemSize)),
extGetCellType(result).base, waZctDecRef)
# and set the memory to nil: # and set the memory to nil:
zeroMem(cast[pointer](cast[TAddress](result) +% GenericSeqSize +% zeroMem(cast[pointer](cast[TAddress](result) +% GenericSeqSize +%
(newLen*%elemSize)), (result.len-%newLen) *% elemSize) (newLen*%elemSize)), (result.len-%newLen) *% elemSize)

View file

@ -14,8 +14,8 @@
## explicitly. Because of this there can not be a user-defined module named ## explicitly. Because of this there can not be a user-defined module named
## ``system``. ## ``system``.
## ##
## *"The good thing about reinventing the wheel is that you can get a ## *The good thing about reinventing the wheel is that you can get a
## round one."* ## round one.*
{.push hints: off.} {.push hints: off.}
@ -1044,8 +1044,9 @@ proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
# Fixup some magic symbols here: # Fixup some magic symbols here:
{.fixup_system.} # This is an undocumented pragma that can only be used #{.fixup_system.}
# once in the system module. # This is an undocumented pragma that can only be used
# once in the system module.
proc `&` *[T](x, y: seq[T]): seq[T] {.noSideEffect.} = proc `&` *[T](x, y: seq[T]): seq[T] {.noSideEffect.} =
newSeq(result, x.len + y.len) newSeq(result, x.len + y.len)
@ -1099,6 +1100,13 @@ proc find*[T, S](a: T, item: S): int {.inline.} =
inc(result) inc(result)
result = -1 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 ------------------------------------------------------ # ----------------- FPU ------------------------------------------------------
#proc disableFPUExceptions*() #proc disableFPUExceptions*()
@ -1401,16 +1409,95 @@ when not defined(EcmaScript) and not defined(NimrodVM):
# as it would recurse endlessly! # as it would recurse endlessly!
include arithm include arithm
{.pop.} # stack trace {.pop.} # stack trace
include dyncalls
const const
GenericSeqSize = (2 * sizeof(int)) GenericSeqSize = (2 * sizeof(int))
when not defined(boehmgc) and not defined(nogc): proc reprAny(p: pointer, typ: PNimType): string {.compilerproc.}
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 gc
include sysstr include sysstr
include assign include assign
include dyncalls
include repr include repr
# we have to implement it here after gentostr for the cstrToNimStrDummy proc # we have to implement it here after gentostr for the cstrToNimStrDummy proc

View file

@ -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)

View file

@ -9,6 +9,8 @@
## This module provides support to handle the Unicode UTF-8 encoding. ## This module provides support to handle the Unicode UTF-8 encoding.
{.deadCodeElim: on.}
type type
TRune* = int ## type that can hold any Unicode character TRune* = int ## type that can hold any Unicode character
TRune16* = int16 ## 16 bit Unicode character TRune16* = int16 ## 16 bit Unicode character

View file

@ -93,12 +93,12 @@ type
nkTypeDef, nkYieldStmt, nkTryStmt, nkFinally, nkTypeDef, nkYieldStmt, nkTryStmt, nkFinally,
nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt, nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt,
nkBlockStmt, nkDiscardStmt, nkStmtList, nkImportStmt, nkBlockStmt, nkDiscardStmt, nkStmtList, nkImportStmt,
nkFromStmt, nkImportAs, nkIncludeStmt, nkAccessStmt, nkFromStmt, nkImportAs, nkIncludeStmt, nkCommentStmt,
nkCommentStmt, nkStmtListExpr, nkBlockExpr, nkStmtListType, nkStmtListExpr, nkBlockExpr, nkStmtListType, nkBlockType,
nkBlockType, nkVm, nkTypeOfExpr, nkObjectTy, nkVm, nkTypeOfExpr, nkObjectTy, nkTupleTy,
nkTupleTy, nkRecList, nkRecCase, nkRecWhen, nkRecList, nkRecCase, nkRecWhen, nkRefTy,
nkRefTy, nkPtrTy, nkVarTy, nkProcTy, nkPtrTy, nkVarTy, nkProcTy, nkEnumTy,
nkEnumTy, nkEnumFieldDef, nkReturnToken); nkEnumFieldDef, nkReturnToken);
TNodeKinds = set of TNodeKind; TNodeKinds = set of TNodeKind;
const const
NodeKindToStr: array [TNodeKind] of string = ( NodeKindToStr: array [TNodeKind] of string = (
@ -128,12 +128,12 @@ const
'nkTypeDef', 'nkYieldStmt', 'nkTryStmt', 'nkFinally', 'nkTypeDef', 'nkYieldStmt', 'nkTryStmt', 'nkFinally',
'nkRaiseStmt', 'nkReturnStmt', 'nkBreakStmt', 'nkContinueStmt', 'nkRaiseStmt', 'nkReturnStmt', 'nkBreakStmt', 'nkContinueStmt',
'nkBlockStmt', 'nkDiscardStmt', 'nkStmtList', 'nkImportStmt', 'nkBlockStmt', 'nkDiscardStmt', 'nkStmtList', 'nkImportStmt',
'nkFromStmt', 'nkImportAs', 'nkIncludeStmt', 'nkAccessStmt', 'nkFromStmt', 'nkImportAs', 'nkIncludeStmt', 'nkCommentStmt',
'nkCommentStmt', 'nkStmtListExpr', 'nkBlockExpr', 'nkStmtListType', 'nkStmtListExpr', 'nkBlockExpr', 'nkStmtListType', 'nkBlockType',
'nkBlockType', 'nkVm', 'nkTypeOfExpr', 'nkObjectTy', 'nkVm', 'nkTypeOfExpr', 'nkObjectTy', 'nkTupleTy',
'nkTupleTy', 'nkRecList', 'nkRecCase', 'nkRecWhen', 'nkRecList', 'nkRecCase', 'nkRecWhen', 'nkRefTy',
'nkRefTy', 'nkPtrTy', 'nkVarTy', 'nkProcTy', 'nkPtrTy', 'nkVarTy', 'nkProcTy', 'nkEnumTy',
'nkEnumTy', 'nkEnumFieldDef', 'nkReturnToken'); 'nkEnumFieldDef', 'nkReturnToken');
type type
TSymFlag = ( TSymFlag = (
sfUsed, sfStar, sfMinus, sfInInterface, sfUsed, sfStar, sfMinus, sfInInterface,
@ -328,6 +328,7 @@ type
TLocFlag = ( 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 lfNoDeepCopy, // no need for a deep copy
lfNoDecl, // do not declare it in C lfNoDecl, // do not declare it in C
lfDynamicLib, // link symbol to dynamic library lfDynamicLib, // link symbol to dynamic library

View file

@ -1233,16 +1233,14 @@ begin
app(r, '.Sup'); app(r, '.Sup');
s := skipGeneric(s.sons[0]); s := skipGeneric(s.sons[0]);
end; end;
appf(p.s[cpsStmts], '$1.m_type = $2;$n', appf(p.s[cpsStmts], '$1.m_type = $2;$n', [r, genTypeInfo(p.module, t)])
[r, genTypeInfo(p.module, t)])
end; end;
frEmbedded: begin frEmbedded: begin
// worst case for performance: // worst case for performance:
useMagic(p.module, 'objectInit'); useMagic(p.module, 'objectInit');
if takeAddr then r := addrLoc(a) if takeAddr then r := addrLoc(a)
else r := rdLoc(a); else r := rdLoc(a);
appf(p.s[cpsStmts], 'objectInit($1, $2);$n', appf(p.s[cpsStmts], 'objectInit($1, $2);$n', [r, genTypeInfo(p.module, t)])
[r, genTypeInfo(p.module, t)])
end end
end end
end; end;
@ -1256,9 +1254,15 @@ begin
refType := skipVarGenericRange(e.sons[1].typ); refType := skipVarGenericRange(e.sons[1].typ);
InitLocExpr(p, e.sons[1], a); InitLocExpr(p, e.sons[1], a);
initLoc(b, locExpr, a.t, OnHeap); initLoc(b, locExpr, a.t, OnHeap);
b.r := ropef('($1) newObj($2, sizeof($3))',
[getTypeDesc(p.module, reftype), genTypeInfo(p.module, refType), if optBoehmGC in gGlobalOptions then
getTypeDesc(p.module, skipGenericRange(reftype.sons[0]))]); 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}[]); genAssignment(p, a, b, {@set}[]);
// set the object type: // set the object type:
bt := skipGenericRange(refType.sons[0]); bt := skipGenericRange(refType.sons[0]);
@ -1275,10 +1279,16 @@ begin
InitLocExpr(p, e.sons[1], a); InitLocExpr(p, e.sons[1], a);
InitLocExpr(p, e.sons[2], b); InitLocExpr(p, e.sons[2], b);
initLoc(c, locExpr, a.t, OnHeap); initLoc(c, locExpr, a.t, OnHeap);
c.r := ropef('($1) newSeq($2, $3)', if optBoehmGC in gGlobalOptions then
[getTypeDesc(p.module, seqtype), c.r := ropef('($1) newSeq(sizeof($2), $3)',
genTypeInfo(p.module, seqType), [getTypeDesc(p.module, seqtype),
rdLoc(b)]); 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}[]); genAssignment(p, a, c, {@set}[]);
end; end;
@ -1325,6 +1335,7 @@ begin
InitLocExpr(p, e.sons[1], a); 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; oldModule := p.module;
p.module := gmti; p.module := gmti;
InitLocExpr(p, e.sons[2], f); InitLocExpr(p, e.sons[2], f);
@ -2290,7 +2301,7 @@ begin
len := sonsLen(n); len := sonsLen(n);
result := toRope('{'+''); result := toRope('{'+'');
for i := 0 to len - 2 do 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])); if len > 0 then app(result, genConstExpr(p, n.sons[len-1]));
app(result, '}' + tnl) app(result, '}' + tnl)
end; end;
@ -2299,6 +2310,7 @@ function genConstExpr(p: BProc; n: PNode): PRope;
var var
trans: PNode; trans: PNode;
cs: TBitSet; cs: TBitSet;
d: TLoc;
begin begin
case n.Kind of case n.Kind of
nkHiddenStdConv, nkHiddenSubConv: result := genConstExpr(p, n.sons[1]); nkHiddenStdConv, nkHiddenSubConv: result := genConstExpr(p, n.sons[1]);
@ -2317,7 +2329,10 @@ begin
trans := n; trans := n;
result := genConstSimpleList(p, trans); result := genConstSimpleList(p, trans);
end end
else else begin
result := genLiteral(p, n) // result := genLiteral(p, n)
initLocExpr(p, n, d);
result := rdLoc(d)
end
end end
end; end;

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -18,25 +18,41 @@ begin
line := toLinenumber(t.info); // BUGFIX line := toLinenumber(t.info); // BUGFIX
if line < 0 then line := 0; // negative numbers are not allowed in #line if line < 0 then line := 0; // negative numbers are not allowed in #line
if optLineDir in p.Options then 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)]); [toRope(toFilename(t.info)), toRope(line)]);
if ([optStackTrace, optEndb] * p.Options = [optStackTrace, optEndb]) and if ([optStackTrace, optEndb] * p.Options = [optStackTrace, optEndb]) and
((p.prc = nil) or not (sfPure in p.prc.flags)) then begin ((p.prc = nil) or not (sfPure in p.prc.flags)) then begin
useMagic(p.module, 'endb'); // new: endb support 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 end
else if ([optLineTrace, optStackTrace] * p.Options = else if ([optLineTrace, optStackTrace] * p.Options =
[optLineTrace, optStackTrace]) and ((p.prc = nil) or [optLineTrace, optStackTrace]) and ((p.prc = nil) or
not (sfPure in p.prc.flags)) then not (sfPure in p.prc.flags)) then begin
appf(p.s[cpsStmts], 'F.line = $1;$n', [toRope(line)]) 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; end;
procedure finishTryStmt(p: BProc; howMany: int); procedure finishTryStmt(p: BProc; howMany: int);
var var
i: int; i: int;
begin begin
for i := 1 to howMany do for i := 1 to howMany do begin
app(p.s[cpsStmts], 'excHandler = excHandler->prev;' + tnl); 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; end;
procedure genReturnStmt(p: BProc; t: PNode); procedure genReturnStmt(p: BProc; t: PNode);
@ -45,7 +61,7 @@ begin
genLineDir(p, t); genLineDir(p, t);
if (t.sons[0] <> nil) then genStmts(p, t.sons[0]); if (t.sons[0] <> nil) then genStmts(p, t.sons[0]);
finishTryStmt(p, p.nestedTryStmts); finishTryStmt(p, p.nestedTryStmts);
app(p.s[cpsStmts], 'goto BeforeRet;' + tnl) appff(p.s[cpsStmts], 'goto BeforeRet;$n', 'br label %BeforeRet$n', [])
end; end;
procedure initVariable(p: BProc; v: PSym); procedure initVariable(p: BProc; v: PSym);
@ -53,11 +69,29 @@ begin
if containsGarbageCollectedRef(v.typ) or (v.ast = nil) then if containsGarbageCollectedRef(v.typ) or (v.ast = nil) then
// Language change: always initialize variables if v.ast == nil! // Language change: always initialize variables if v.ast == nil!
if not (skipVarGenericRange(v.typ).Kind in [tyArray, tyArrayConstr, tySet, if not (skipVarGenericRange(v.typ).Kind in [tyArray, tyArrayConstr, tySet,
tyTuple, tyObject]) then tyTuple, tyObject]) then begin
appf(p.s[cpsStmts], '$1 = 0;$n', [rdLoc(v.loc)]) if gCmd = cmdCompileToLLVM then
else appf(p.s[cpsStmts], 'store $2 0, $2* $1$n',
appf(p.s[cpsStmts], 'memset((void*)$1, 0, sizeof($2));$n', [addrLoc(v.loc), getTypeDesc(p.module, v.loc.t)])
[addrLoc(v.loc), rdLoc(v.loc)]) 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; end;
procedure genVarStmt(p: BProc; n: PNode); procedure genVarStmt(p: BProc; n: PNode);
@ -73,7 +107,7 @@ begin
assert(a.sons[0].kind = nkSym); assert(a.sons[0].kind = nkSym);
v := a.sons[0].sym; v := a.sons[0].sym;
if sfGlobal in v.flags then if sfGlobal in v.flags then
assignGlobalVar(p.module, v) assignGlobalVar(p, v)
else begin else begin
assignLocalVar(p, v); assignLocalVar(p, v);
initVariable(p, v) // XXX: this is not required if a.sons[2] != nil, initVariable(p, v) // XXX: this is not required if a.sons[2] != nil,
@ -140,10 +174,14 @@ begin
nkElifBranch: begin nkElifBranch: begin
initLocExpr(p, it.sons[0], a); initLocExpr(p, it.sons[0], a);
Lelse := getLabel(p); 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]); genStmts(p, it.sons[1]);
if sonsLen(n) > 1 then 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); fixLabel(p, Lelse);
end; end;
nkElse: begin nkElse: begin
@ -857,7 +895,7 @@ begin
nkCaseStmt: genCaseStmt(p, t); nkCaseStmt: genCaseStmt(p, t);
nkReturnStmt: genReturnStmt(p, t); nkReturnStmt: genReturnStmt(p, t);
nkBreakStmt: genBreakStmt(p, t); nkBreakStmt: genBreakStmt(p, t);
nkCall: begin nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand: begin
genLineDir(p, t); genLineDir(p, t);
initLocExpr(p, t, a); initLocExpr(p, t, a);
end; end;

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -45,7 +45,19 @@ function mangleName(s: PSym): PRope;
begin begin
result := s.loc.r; result := s.loc.r;
if result = nil then begin 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, '_'+'');
app(result, toRope(s.id)); app(result, toRope(s.id));
if optGenMapping in gGlobalOptions then if optGenMapping in gGlobalOptions then
@ -58,15 +70,33 @@ end;
function getTypeName(typ: PType): PRope; function getTypeName(typ: PType): PRope;
begin 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 result := typ.sym.loc.r
else begin 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 result := typ.loc.r
end; end;
if result = nil then InternalError('getTypeName: ' + typeKindToStr[typ.kind]); if result = nil then InternalError('getTypeName: ' + typeKindToStr[typ.kind]);
end; 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 ------------------------ // ------------------------------ C type generator ------------------------
function mapType(typ: PType): TCTypeKind; function mapType(typ: PType): TCTypeKind;
@ -104,10 +134,6 @@ begin
tyPtr, tyVar, tyRef: begin tyPtr, tyVar, tyRef: begin
case typ.sons[0].kind of case typ.sons[0].kind of
tyOpenArray, tyArrayConstr, tyArray: result := ctArray; tyOpenArray, tyArrayConstr, tyArray: result := ctArray;
(*tySet: begin
if mapType(typ.sons[0]) = ctArray then result := ctArray
else result := ctPtr
end*)
else result := ctPtr else result := ctPtr
end end
end; end;
@ -155,6 +181,10 @@ const
// but one can //define it to what you want so there will no problem // but one can //define it to what you want so there will no problem
'N_INLINE', 'N_NOINLINE', 'N_FASTCALL', 'N_CLOSURE', 'N_NOCONV'); '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; function CacheGetType(const tab: TIdTable; key: PType): PRope;
begin begin
// returns nil if we need to declare this type // returns nil if we need to declare this type
@ -165,7 +195,13 @@ end;
function getTempName(): PRope; function getTempName(): PRope;
begin 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); inc(gId);
end; end;
@ -194,7 +230,8 @@ end;
procedure fillResult(param: PSym); procedure fillResult(param: PSym);
begin 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 if (mapType(param.typ) <> ctArray) and IsInvalidReturnType(param.typ) then
begin begin
include(param.loc.flags, lfIndirect); include(param.loc.flags, lfIndirect);
@ -232,7 +269,7 @@ begin
if arr.kind = tyVar then arr := arr.sons[0]; if arr.kind = tyVar then arr := arr.sons[0];
j := 0; j := 0;
while arr.Kind = tyOpenArray do begin // need to pass hidden parameter: 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); inc(j);
arr := arr.sons[0] arr := arr.sons[0]
end; end;
@ -241,8 +278,9 @@ begin
if (t.sons[0] <> nil) and isInvalidReturnType(t.sons[0]) then begin if (t.sons[0] <> nil) and isInvalidReturnType(t.sons[0]) then begin
if params <> nil then app(params, ', '); if params <> nil then app(params, ', ');
app(params, getTypeDescAux(m, t.sons[0], check)); app(params, getTypeDescAux(m, t.sons[0], check));
if mapType(t.sons[0]) <> ctArray then app(params, '*'+''); if (mapType(t.sons[0]) <> ctArray) or (gCmd = cmdCompileToLLVM) then
app(params, ' Result'); app(params, '*'+'');
appff(params, ' Result', ' @Result', []);
end; end;
if t.callConv = ccClosure then begin if t.callConv = ccClosure then begin
if params <> nil then app(params, ', '); if params <> nil then app(params, ', ');
@ -252,7 +290,7 @@ begin
if params <> nil then app(params, ', '); if params <> nil then app(params, ', ');
app(params, '...') app(params, '...')
end; end;
if params = nil then if (params = nil) and (gCmd <> cmdCompileToLLVM) then
app(params, 'void)') app(params, 'void)')
else else
app(params, ')'+''); app(params, ')'+'');
@ -621,7 +659,7 @@ begin
end end
end; end;
function getTypeDesc(m: BModule; typ: PType): PRope; function getTypeDesc(m: BModule; typ: PType): PRope; overload;
var var
check: TIntSet; check: TIntSet;
begin begin
@ -629,6 +667,19 @@ begin
result := getTypeDescAux(m, typ, check); result := getTypeDescAux(m, typ, check);
end; 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); procedure finishTypeDescriptions(m: BModule);
var var
i: int; i: int;

View file

@ -20,6 +20,7 @@ uses
function toCChar(c: Char): string; function toCChar(c: Char): string;
function makeCString(const s: string): PRope; function makeCString(const s: string): PRope;
function makeLLVMString(const s: string): PRope;
function TableGetType(const tab: TIdTable; key: PType): PObject; function TableGetType(const tab: TIdTable; key: PType): PObject;
function GetUniqueType(key: PType): PType; function GetUniqueType(key: PType): PType;
@ -154,6 +155,33 @@ begin
app(result, toRope(res)); app(result, toRope(res));
end; 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 begin
InitTypeTables(); InitTypeTables();
end. end.

View file

@ -37,9 +37,9 @@ type
// reasons // reasons
cfsFieldInfo, // section for field information cfsFieldInfo, // section for field information
cfsTypeInfo, // section for type information cfsTypeInfo, // section for type information
cfsProcHeaders, // section for C procs prototypes
cfsData, // section for C constant data cfsData, // section for C constant data
cfsVars, // section for C variable declarations cfsVars, // section for C variable declarations
cfsProcHeaders, // section for C procs prototypes
cfsProcs, // section for C procs that are not inline cfsProcs, // section for C procs that are not inline
cfsTypeInit1, // section 1 for declarations of type information cfsTypeInit1, // section 1 for declarations of type information
cfsTypeInit2, // section 2 for initialization of type information cfsTypeInit2, // section 2 for initialization of type information
@ -120,6 +120,7 @@ type
forwardedProcs: TSymSeq; // keep forwarded procs here forwardedProcs: TSymSeq; // keep forwarded procs here
typeNodes, nimTypes: int;// used for type info generation typeNodes, nimTypes: int;// used for type info generation
typeNodesName, nimTypesName: PRope; // used for type info generation typeNodesName, nimTypesName: PRope; // used for type info generation
labels: natural; // for generating unique module-scope names
end; end;
var var
@ -132,6 +133,24 @@ var
gForwardedProcsCounter: int = 0; gForwardedProcsCounter: int = 0;
gmti: BModule; // generated type info: no need to initialize: defaults fit 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); procedure addForwardedProc(m: BModule; prc: PSym);
var var
L: int; L: int;
@ -240,8 +259,12 @@ end;
procedure getTemp(p: BProc; t: PType; var result: TLoc); procedure getTemp(p: BProc; t: PType; var result: TLoc);
begin begin
inc(p.labels); inc(p.labels);
result.r := con('LOC', toRope(p.labels)); if gCmd = cmdCompileToLLVM then
appf(p.s[cpsLocals], '$1 $2;$n', [getTypeDesc(p.module, t), result.r]); 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.k := locTemp;
result.a := -1; result.a := -1;
result.t := getUniqueType(t); result.t := getUniqueType(t);
@ -251,6 +274,86 @@ end;
// -------------------------- Variable manager ---------------------------- // -------------------------- 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); procedure assignLocalVar(p: BProc; s: PSym);
begin begin
//assert(s.loc.k == locNone) // not yet assigned //assert(s.loc.k == locNone) // not yet assigned
@ -258,45 +361,54 @@ begin
// for each module that uses them! // for each module that uses them!
if s.loc.k = locNone then if s.loc.k = locNone then
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack); fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack);
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t)); if gCmd = cmdCompileToLLVM then begin
if sfRegister in s.flags then appf(p.s[cpsLocals], '$1 = alloca $2$n',
app(p.s[cpsLocals], ' register'); [s.loc.r, getTypeDesc(p.module, s.loc.t)]);
if (sfVolatile in s.flags) or (p.nestedTryStmts > 0) then include(s.loc.flags, lfIndirect);
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);
end 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; end;
procedure assignGlobalVar(m: BModule; s: PSym); procedure assignGlobalVar(p: BProc; s: PSym);
begin begin
if s.loc.k = locNone then if s.loc.k = locNone then
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap); fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap);
useHeader(m, s); if gCmd = cmdCompileToLLVM then begin
if lfNoDecl in s.loc.flags then exit; appf(p.module.s[cfsVars], '$1 = linkonce global $2 zeroinitializer$n',
if sfImportc in s.flags then app(m.s[cfsVars], 'extern '); [s.loc.r, getTypeDesc(p.module, s.loc.t)]);
app(m.s[cfsVars], getTypeDesc(m, s.loc.t)); include(s.loc.flags, lfIndirect);
if sfRegister in s.flags then end
app(m.s[cfsVars], ' register'); else begin
if sfVolatile in s.flags then useHeader(p.module, s);
app(m.s[cfsVars], ' volatile'); if lfNoDecl in s.loc.flags then exit;
if sfThreadVar in s.flags then if sfImportc in s.flags then app(p.module.s[cfsVars], 'extern ');
app(m.s[cfsVars], ' NIM_THREADVAR'); app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t));
appf(m.s[cfsVars], ' $1;$n', [s.loc.r]); if sfRegister in s.flags then app(p.module.s[cfsVars], ' register');
if [optStackTrace, optEndb] * m.module.options = 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 [optStackTrace, optEndb] then begin
useMagic(m, 'dbgRegisterGlobal'); useMagic(p.module, 'dbgRegisterGlobal');
appf(m.s[cfsDebugInit], appff(p.module.s[cfsDebugInit],
'dbgRegisterGlobal($1, &$2, $3);$n', 'dbgRegisterGlobal($1, &$2, $3);$n',
[makeCString(normalize(s.owner.name.s + '.' +{&} s.name.s)), s.loc.r, 'call void @dbgRegisterGlobal(i8* $1, i8* $2, $4* $3)$n',
genTypeInfo(m, s.typ)]) [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;
end; end;
@ -308,15 +420,9 @@ end;
procedure assignParam(p: BProc; s: PSym); procedure assignParam(p: BProc; s: PSym);
begin begin
assert(s.loc.r <> nil); assert(s.loc.r <> nil);
if [optStackTrace, optEndb] * p.options = [optStackTrace, optEndb] then begin if (sfAddrTaken in s.flags) and (gCmd = cmdCompileToLLVM) then
appf(p.s[cpsInit], allocParam(p, s);
'F.s[$1].name = $2; F.s[$1].address = (void*)$3; ' + localDebugInfo(p, s);
'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
end; end;
procedure fillProcLoc(sym: PSym); procedure fillProcLoc(sym: PSym);
@ -359,19 +465,26 @@ begin
assert(lib <> nil); assert(lib <> nil);
if not lib.generated then begin if not lib.generated then begin
lib.generated := true; 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, 'nimLoadLibrary');
useMagic(m, 'nimUnloadLibrary'); useMagic(m, 'nimUnloadLibrary');
useMagic(m, 'NimStringDesc'); useMagic(m, 'NimStringDesc');
tmp := getTempName(); end;
appf(m.s[cfsVars], 'static void* $1;$n', [tmp]); if lib.name = nil then InternalError('loadDynamicLib');
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; end;
procedure SymInDynamicLib(m: BModule; sym: PSym); procedure SymInDynamicLib(m: BModule; sym: PSym);
@ -383,14 +496,25 @@ begin
extname := sym.loc.r; extname := sym.loc.r;
loadDynamicLib(m, lib); loadDynamicLib(m, lib);
useMagic(m, 'nimGetProcAddr'); 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.loc.r := tmp; // from now on we only need the internal name
sym.typ.sym := nil; // generate a new name sym.typ.sym := nil; // generate a new name
appf(m.s[cfsDynLibInit], '$1 = ($2) nimGetProcAddr($3, $4);$n', inc(m.labels, 2);
[tmp, getTypeDesc(m, sym.typ), lib.name, makeCString(ropeToStr(extname))]); 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)); appff(m.s[cfsVars],
appf(m.s[cfsVars], ' $1;$n', [sym.loc.r]); '$2 $1;$n',
'$1 = linkonce global $2 zeroinitializer$n',
[sym.loc.r, getTypeDesc(m, sym.loc.t)]);
end; end;
// ----------------------------- sections --------------------------------- // ----------------------------- sections ---------------------------------
@ -433,14 +557,23 @@ var
begin begin
if p.frameLen > 0 then begin if p.frameLen > 0 then begin
useMagic(p.module, 'TVarSlot'); 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 end
else else
slots := nil; slots := nil;
appf(p.s[cpsLocals], 'volatile struct {TFrame* prev;' + appff(p.s[cpsLocals],
'volatile struct {TFrame* prev;' +
'NCSTRING procname;NI line;NCSTRING filename;' + 'NCSTRING procname;NI line;NCSTRING filename;' +
'NI len;$n$1} F;$n', [slots]); 'NI len;$n$1} F;$n',
prepend(p.s[cpsInit], ropef('F.len = $1;$n', [toRope(p.frameLen)])) '%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; end;
function retIsNotVoid(s: PSym): bool; function retIsNotVoid(s: PSym): bool;
@ -448,10 +581,48 @@ begin
result := (s.typ.sons[0] <> nil) and not isInvalidReturnType(s.typ.sons[0]) result := (s.typ.sons[0] <> nil) and not isInvalidReturnType(s.typ.sons[0])
end; 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); procedure genProcAux(m: BModule; prc: PSym);
var var
p: BProc; p: BProc;
generatedProc, header, returnStmt: PRope; generatedProc, header, returnStmt, procname, filename: PRope;
i: int; i: int;
res, param: PSym; res, param: PSym;
begin begin
@ -466,7 +637,7 @@ begin
// declare the result symbol: // declare the result symbol:
assignLocalVar(p, res); assignLocalVar(p, res);
assert(res.loc.r <> nil); 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 end
else begin else begin
fillResult(res); fillResult(res);
@ -482,22 +653,21 @@ begin
genStmts(p, prc.ast.sons[codePos]); // modifies p.locals, p.init, etc. genStmts(p, prc.ast.sons[codePos]); // modifies p.locals, p.init, etc.
if sfPure in prc.flags then 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]]) [header, p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]])
else begin else begin
generatedProc := con(header, '{' + tnl); generatedProc := ropeff('$1 {$n', 'define $1 {$n', [header]);
if optStackTrace in prc.options then begin if optStackTrace in prc.options then begin
getFrameDecl(p); getFrameDecl(p);
prepend(p.s[cpsInit], ropef( app(generatedProc, p.s[cpsLocals]);
'F.procname = $1;$n' + procname := CStringLit(p, generatedProc,
'F.prev = framePtr;$n' + prc.owner.name.s +{&} '.' +{&} prc.name.s);
'F.filename = $2;$n' + filename := CStringLit(p, generatedProc, toFilename(prc.info));
'F.line = 0;$n' + app(generatedProc, initFrame(p, procname, filename));
'framePtr = (TFrame*)&F;$n', end
[makeCString(prc.owner.name.s +{&} '.' +{&} prc.name.s), else
makeCString(toFilename(prc.info))])); app(generatedProc, p.s[cpsLocals]);
end; if (optProfiler in prc.options) and (gCmd <> cmdCompileToLLVM) then begin
if optProfiler in prc.options then begin
if gProcProfile >= 64*1024 then // XXX: hard coded value! if gProcProfile >= 64*1024 then // XXX: hard coded value!
InternalError(prc.info, 'too many procedures for profiling'); InternalError(prc.info, 'too many procedures for profiling');
useMagic(m, 'profileData'); useMagic(m, 'profileData');
@ -511,12 +681,13 @@ begin
end; end;
prepend(p.s[cpsInit], toRope('NIM_profilingStart = getticks();' + tnl)); prepend(p.s[cpsInit], toRope('NIM_profilingStart = getticks();' + tnl));
end; end;
app(generatedProc, con(p.s)); app(generatedProc, p.s[cpsInit]);
app(generatedProc, p.s[cpsStmts]);
if p.beforeRetNeeded then if p.beforeRetNeeded then
app(generatedProc, 'BeforeRet: ;' + tnl); app(generatedProc, 'BeforeRet: ;' + tnl);
if optStackTrace in prc.options then if optStackTrace in prc.options then
app(generatedProc, 'framePtr = framePtr->prev;' + tnl); app(generatedProc, deinitFrame(p));
if optProfiler in prc.options then if (optProfiler in prc.options) and (gCmd <> cmdCompileToLLVM) then
appf(generatedProc, appf(generatedProc,
'profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n', 'profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n',
[toRope(prc.loc.a)]); [toRope(prc.loc.a)]);
@ -533,8 +704,10 @@ begin
if lfDynamicLib in sym.loc.Flags then begin if lfDynamicLib in sym.loc.Flags then begin
if (sym.owner.id <> m.module.id) and if (sym.owner.id <> m.module.id) and
not intSetContainsOrIncl(m.declaredThings, sym.id) then begin not intSetContainsOrIncl(m.declaredThings, sym.id) then begin
appf(m.s[cfsVars], 'extern $1 Dl_$2;$n', appff(m.s[cfsVars], 'extern $1 Dl_$2;$n',
[getTypeDesc(m, sym.loc.t), toRope(sym.id)]) '@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
end end
else begin else begin
@ -586,15 +759,22 @@ begin
if sym.owner.id <> m.module.id then begin if sym.owner.id <> m.module.id then begin
// else we already have the symbol generated! // else we already have the symbol generated!
assert(sym.loc.r <> nil); assert(sym.loc.r <> nil);
app(m.s[cfsVars], 'extern '); if gCmd = cmdCompileToLLVM then begin
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t)); include(sym.loc.flags, lfIndirect);
if sfRegister in sym.flags then appf(m.s[cfsVars], '$1 = linkonce global $2 zeroinitializer$n',
app(m.s[cfsVars], ' register'); [sym.loc.r, getTypeDesc(m, sym.loc.t)]);
if sfVolatile in sym.flags then end
app(m.s[cfsVars], ' volatile'); else begin
if sfThreadVar in sym.flags then app(m.s[cfsVars], 'extern ');
app(m.s[cfsVars], ' NIM_THREADVAR'); app(m.s[cfsVars], getTypeDesc(m, sym.loc.t));
appf(m.s[cfsVars], ' $1;$n', [sym.loc.r]) 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
end; end;
@ -609,8 +789,9 @@ begin
if sym.owner.id <> m.module.id then begin if sym.owner.id <> m.module.id then begin
// else we already have the symbol generated! // else we already have the symbol generated!
assert(sym.loc.r <> nil); assert(sym.loc.r <> nil);
app(m.s[cfsData], 'extern '); appff(m.s[cfsData],
appf(m.s[cfsData], 'NIM_CONST $1 $2;$n', 'extern NIM_CONST $1 $2;$n',
'$1 = linkonce constant $2 zeroinitializer',
[getTypeDesc(m, sym.loc.t), sym.loc.r]) [getTypeDesc(m, sym.loc.t), sym.loc.r])
end end
end; end;
@ -618,27 +799,36 @@ end;
function getFileHeader(const cfilenoext: string): PRope; function getFileHeader(const cfilenoext: string): PRope;
begin begin
if optCompileOnly in gGlobalOptions then if optCompileOnly in gGlobalOptions then
result := ropef( result := ropeff(
'/* Generated by the Nimrod Compiler v$1 */$n' + '/* Generated by the Nimrod Compiler v$1 */$n' +
'/* (c) 2008 Andreas Rumpf */$n', '/* (c) 2008 Andreas Rumpf */$n',
'; Generated by the Nimrod Compiler v$1$n' +
'; (c) 2008 Andreas Rumpf$n',
[toRope(versionAsString)]) [toRope(versionAsString)])
else else
result := ropef( result := ropeff(
'/* Generated by the Nimrod Compiler v$1 */$n' + '/* Generated by the Nimrod Compiler v$1 */$n' +
'/* (c) 2008 Andreas Rumpf */$n' + '/* (c) 2008 Andreas Rumpf */$n' +
'/* Compiled for: $2, $3, $4 */$n' + '/* Compiled for: $2, $3, $4 */$n' +
'/* Command for C compiler:$n $5 */$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(versionAsString), toRope(platform.OS[targetOS].name),
toRope(platform.CPU[targetCPU].name), toRope(platform.CPU[targetCPU].name),
toRope(extccomp.CC[extccomp.ccompiler].name), toRope(extccomp.CC[extccomp.ccompiler].name),
toRope(getCompileCFileCmd(cfilenoext))]); toRope(getCompileCFileCmd(cfilenoext))]);
case platform.CPU[targetCPU].intSize of case platform.CPU[targetCPU].intSize of
16: appf(result, '$ntypedef short int NI;$n' + 16: appff(result, '$ntypedef short int NI;$n' +
'typedef unsigned short int NU;$n', []); 'typedef unsigned short int NU;$n',
32: appf(result, '$ntypedef long int NI;$n' + '$n%NI = type i16$n', []);
'typedef unsigned long int NU;$n', []); 32: appff(result, '$ntypedef long int NI;$n' +
64: appf(result, '$ntypedef long long int NI;$n' + 'typedef unsigned long int NU;$n',
'typedef unsigned long 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 else begin end
end end
end; end;
@ -651,18 +841,37 @@ const
' systemInit();$n' + ' systemInit();$n' +
'$1' + '$1' +
'$2'; '$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 = PosixMain =
'NI cmdCount;$n' + 'int cmdCount;$n' +
'char** cmdLine;$n' + 'char** cmdLine;$n' +
'char** gEnv;$n' + 'char** gEnv;$n' +
'int main(int argc, char** args, char** env) {$n' + 'int main(int argc, char** args, char** env) {$n' +
' int dummy[8];$n' + ' int dummy[8];$n' +
' cmdLine = args;$n' + ' cmdLine = args;$n' +
' cmdCount = (NI)argc;$n' + ' cmdCount = argc;$n' +
' gEnv = env;$n' +{&} ' gEnv = env;$n' +{&}
CommonMainBody +{&} CommonMainBody +{&}
' return 0;$n' + ' return 0;$n' +
'}$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 = WinMain =
'N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $n' + 'N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $n' +
' HINSTANCE hPrevInstance, $n' + ' HINSTANCE hPrevInstance, $n' +
@ -671,6 +880,14 @@ const
CommonMainBody +{&} CommonMainBody +{&}
' return 0;$n' + ' return 0;$n' +
'}$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 = WinDllMain =
'BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n' + 'BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n' +
' LPVOID lpvReserved) {$n' + ' LPVOID lpvReserved) {$n' +
@ -678,6 +895,13 @@ const
CommonMainBody +{&} CommonMainBody +{&}
' return 1;$n' + ' return 1;$n' +
'}$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 var
frmt: TFormatStr; frmt: TFormatStr;
begin begin
@ -685,21 +909,28 @@ begin
if (platform.targetOS = osWindows) and if (platform.targetOS = osWindows) and
(gGlobalOptions * [optGenGuiApp, optGenDynLib] <> []) then begin (gGlobalOptions * [optGenGuiApp, optGenDynLib] <> []) then begin
if optGenGuiApp in gGlobalOptions then if optGenGuiApp in gGlobalOptions then
frmt := WinMain if gCmd = cmdCompileToLLVM then frmt := WinMainLLVM else frmt := WinMain
else else
frmt := WinDllMain; if gCmd = cmdCompileToLLVM then
frmt := WinDllMainLLVM
else
frmt := WinDllMain;
{@discard} lists.IncludeStr(m.headerFiles, '<windows.h>') {@discard} lists.IncludeStr(m.headerFiles, '<windows.h>')
end end
else else
frmt := PosixMain; if gCmd = cmdCompileToLLVM then
frmt := PosixMainLLVM
else
frmt := PosixMain;
if gBreakpoints <> nil then if gBreakpoints <> nil then
useMagic(m, 'dbgRegisterBreakpoint'); useMagic(m, 'dbgRegisterBreakpoint');
appf(m.s[cfsProcs], frmt, [gBreakpoints, mainModInit]) inc(m.labels);
appf(m.s[cfsProcs], frmt, [gBreakpoints, mainModInit, toRope(m.labels)])
end; end;
function getInitName(m: PSym): PRope; function getInitName(m: PSym): PRope;
begin begin
result := con(m.name.s, toRope('Init')); result := ropeff('$1Init', '@$1Init', [toRope(m.name.s)]);
end; end;
procedure registerModuleToMain(m: PSym); procedure registerModuleToMain(m: PSym);
@ -707,14 +938,15 @@ var
initname: PRope; initname: PRope;
begin begin
initname := getInitName(m); 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 if not (sfSystemModule in m.flags) then
appf(mainModInit, '$1();$n', [initname]); appff(mainModInit, '$1();$n', 'call void ()* $1$n', [initname]);
end; end;
procedure genInitCode(m: BModule); procedure genInitCode(m: BModule);
var var
initname, prc: PRope; initname, prc, procname, filename: PRope;
begin begin
if optProfiler in m.initProc.options then begin if optProfiler in m.initProc.options then begin
// This does not really belong here, but there is no good place for this // 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>'); {@discard} lists.IncludeStr(m.headerFiles, '<cycle.h>');
end; end;
initname := getInitName(m.module); 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 if m.typeNodes > 0 then begin
useMagic(m, 'TNimNode'); 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)]); [m.typeNodesName, toRope(m.typeNodes)]);
end; end;
if m.nimTypes > 0 then begin if m.nimTypes > 0 then begin
useMagic(m, 'TNimType'); 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)]); [m.nimTypesName, toRope(m.nimTypes)]);
end; end;
if optStackTrace in m.initProc.options then begin if optStackTrace in m.initProc.options then begin
getFrameDecl(m.initProc); getFrameDecl(m.initProc);
app(prc, m.initProc.s[cpsLocals]); app(prc, m.initProc.s[cpsLocals]);
app(prc, m.s[cfsTypeInit1]); app(prc, m.s[cfsTypeInit1]);
appf(prc,
'F.len = 0;$n' + // IMPORTANT: else the debugger crashes! procname := CStringLit(m.initProc, prc, 'module ' +{&} m.module.name.s);
'F.procname = $1;$n' + filename := CStringLit(m.initProc, prc, toFilename(m.module.info));
'F.prev = framePtr;$n' + app(prc, initFrame(m.initProc, procname, filename));
'F.filename = $2;$n' +
'F.line = 0;$n' +
'framePtr = (TFrame*)&F;$n',
[makeCString('module ' + m.module.name.s),
makeCString(toFilename(m.module.info))])
end end
else begin else begin
app(prc, m.initProc.s[cpsLocals]); app(prc, m.initProc.s[cpsLocals]);
@ -760,7 +989,7 @@ begin
app(prc, m.initProc.s[cpsInit]); app(prc, m.initProc.s[cpsInit]);
app(prc, m.initProc.s[cpsStmts]); app(prc, m.initProc.s[cpsStmts]);
if optStackTrace in m.initProc.options then if optStackTrace in m.initProc.options then
app(prc, 'framePtr = framePtr->prev;' + tnl); app(prc, deinitFrame(m.initProc));
app(prc, '}' +{&} tnl +{&} tnl); app(prc, '}' +{&} tnl +{&} tnl);
app(m.s[cfsProcs], prc) app(m.s[cfsProcs], prc)
end; end;
@ -817,7 +1046,8 @@ begin
s := NewSym(skModule, getIdent(moduleName), nil); s := NewSym(skModule, getIdent(moduleName), nil);
gmti := rawNewModule(s, joinPath(options.projectPath, moduleName)+'.nim'); gmti := rawNewModule(s, joinPath(options.projectPath, moduleName)+'.nim');
addPendingModule(gmti); 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; end;
function myOpen(module: PSym; const filename: string): PPassContext; function myOpen(module: PSym; const filename: string): PPassContext;

View file

@ -77,14 +77,14 @@ const
+{&} ' --debugger:on|off turn Embedded Nimrod Debugger ON|OFF' +{&} nl +{&} ' --debugger:on|off turn Embedded Nimrod Debugger ON|OFF' +{&} nl
+{&} ' -x, --checks:on|off code generation for all runtime checks 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 +{&} ' --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 +{&} ' --range_checks:on|off code generation for range checks ON|OFF' +{&} nl
+{&} ' --bound_checks:on|off code generation for bound 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 +{&} ' --overflow_checks:on|off code generation for over-/underflow checks ON|OFF' +{&} nl
+{&} ' -a, --assertions:on|off code generation for assertions 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 +{&} ' --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 +{&} ' --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 +{&} ' -r, --run run the compiled program with given arguments' +{&} nl
+{&} ' --advanced show advanced command line switches' +{&} nl +{&} ' --advanced show advanced command line switches' +{&} nl
+{&} ' -h, --help show this help' +{&} nl +{&} ' -h, --help show this help' +{&} nl
@ -125,7 +125,6 @@ const
+{&} ' --checkpoints:on|off turn on|off checkpoints; for debugging Nimrod' +{&} nl +{&} ' --checkpoints:on|off turn on|off checkpoints; for debugging Nimrod' +{&} nl
+{&} ' --skip_cfg do not read the general configuration file' +{&} nl +{&} ' --skip_cfg do not read the general configuration file' +{&} nl
+{&} ' --skip_proj_cfg do not read the project''s 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 +{&} ' --index:FILE use FILE to generate a documenation index file' +{&} nl
+{&} ' --putenv:key=value set an environment variable' +{&} nl +{&} ' --putenv:key=value set an environment variable' +{&} nl
+{&} ' --list_cmd list the commands used to execute external programs' +{&} nl +{&} ' --list_cmd list the commands used to execute external programs' +{&} nl
@ -506,7 +505,7 @@ begin
expectArg(switch, arg, pass, info); expectArg(switch, arg, pass, info);
gErrorMax := parseInt(arg); gErrorMax := parseInt(arg);
end; end;
else if findSubStr('.', switch) >= strStart then else if strutils.find(switch, '.') >= strStart then
options.setConfigVar(switch, arg) options.setConfigVar(switch, arg)
else else
InvalidCmdLineOption(pass, switch, info) InvalidCmdLineOption(pass, switch, info)

View file

@ -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.

View file

@ -40,7 +40,7 @@ type
modDesc: PRope; // module description modDesc: PRope; // module description
dependsOn: PRope; // dependencies dependsOn: PRope; // dependencies
id: int; // for generating IDs 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; tocPart: array of TTocEntry;
hasToc: bool; hasToc: bool;
toc, section: TSections; toc, section: TSections;
@ -777,7 +777,7 @@ begin
rnLineBlock: outer := '<p>$1</p>'; rnLineBlock: outer := '<p>$1</p>';
rnLineBlockItem: outer := '$1<br />'; rnLineBlockItem: outer := '$1<br />';
rnBlockQuote: outer := '<blockquote>$1</blockquote>$n'; rnBlockQuote: outer := '<blockquote><p>$1</p></blockquote>$n';
rnTable, rnGridTable: rnTable, rnGridTable:
outer := '<table border="1" class="docutils">$1</table>'; outer := '<table border="1" class="docutils">$1</table>';

View file

@ -352,13 +352,13 @@ end;
procedure addCompileOption(const option: string); procedure addCompileOption(const option: string);
begin begin
if strutils.findSubStr(option, compileOptions, strStart) < strStart then if strutils.find(compileOptions, option, strStart) < strStart then
addOpt(compileOptions, option) addOpt(compileOptions, option)
end; end;
procedure addLinkOption(const option: string); procedure addLinkOption(const option: string);
begin begin
if findSubStr(option, linkOptions, strStart) < strStart then if find(linkOptions, option, strStart) < strStart then
addOpt(linkOptions, option) addOpt(linkOptions, option)
end; end;

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -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 // special version for the scanner; the scanner's buffering scheme makes
// this horribly efficient. Most of the time no character copying is needed! // this horribly efficient. Most of the time no character copying is needed!
function IdentEq(id: PIdent; const name: string): bool;
implementation implementation
function IdentEq(id: PIdent; const name: string): bool;
begin
result := id.id = getIdent(name).id;
end;
var var
buckets: array [0..4096*2-1] of PIdent; buckets: array [0..4096*2-1] of PIdent;

View file

@ -22,7 +22,7 @@ type
llsFile, // stream encapsulates a file llsFile, // stream encapsulates a file
llsStdIn); // stream encapsulates stdin llsStdIn); // stream encapsulates stdin
TLLStream = object(NObject) 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; f: TBinaryFile;
s: string; s: string;
pos: int; // for string streams pos: int; // for string streams

View file

@ -119,7 +119,7 @@ procedure CompileProject(const filename: string);
begin begin
{@discard} CompileModule( {@discard} CompileModule(
JoinPath(options.libpath, appendFileExt('system', nimExt)), false, true); JoinPath(options.libpath, appendFileExt('system', nimExt)), false, true);
{@discard} CompileModule(filename, true, false); {@discard} CompileModule(appendFileExt(filename, nimExt), true, false);
end; end;
procedure semanticPasses; procedure semanticPasses;
@ -352,6 +352,11 @@ begin
wantFile(filename); wantFile(filename);
CommandCompileToEcmaScript(filename); CommandCompileToEcmaScript(filename);
end; end;
wCompileToLLVM: begin
gCmd := cmdCompileToLLVM;
wantFile(filename);
CommandCompileToC(filename);
end;
wPretty: begin wPretty: begin
gCmd := cmdPretty; gCmd := cmdPretty;
wantFile(filename); wantFile(filename);

View file

@ -278,6 +278,7 @@ type
errXRequiresOneArgument, errXRequiresOneArgument,
errUnhandledExceptionX, errUnhandledExceptionX,
errCyclicTree, errCyclicTree,
errXisNoMacroOrTemplate,
errUser, errUser,
warnCannotOpenFile, warnCannotOpenFile,
warnOctalEscape, warnOctalEscape,
@ -491,7 +492,7 @@ const
'$1 here not allowed', '$1 here not allowed',
'invalid control flow: $1', 'invalid control flow: $1',
'a type has no value', 'a type has no value',
'''$1'' is no type', 'invalid type: ''$1''',
'''^'' needs a pointer or reference type', '''^'' needs a pointer or reference type',
'invalid context for builtin ''$1''', 'invalid context for builtin ''$1''',
'invalid expression', 'invalid expression',
@ -536,6 +537,7 @@ const
'converter requires one parameter', 'converter requires one parameter',
'unhandled exception: $1', 'unhandled exception: $1',
'macro returned a cyclic abstract syntax tree', 'macro returned a cyclic abstract syntax tree',
'''$1'' is no macro or template',
'$1', '$1',
'cannot open ''$1'' [CannotOpenFile]', 'cannot open ''$1'' [CannotOpenFile]',
'octal escape sequences do not exist; leading zero is ignored [OctalEscape]', 'octal escape sequences do not exist; leading zero is ignored [OctalEscape]',

View file

@ -9,9 +9,7 @@
unit nimconf; unit nimconf;
// This module used to handle the reading of the config file. We now just // This module handles the reading of the config file.
// read environment variables. This is easier to avoid bootstraping issues.
{$include 'config.inc'} {$include 'config.inc'}
interface interface
@ -258,7 +256,7 @@ begin
addChar(s, '.'); addChar(s, '.');
confTok(L, tok); confTok(L, tok);
checkSymbol(L, tok); checkSymbol(L, tok);
s := s +{&} tokToStr(tok); add(s, tokToStr(tok));
confTok(L, tok) confTok(L, tok)
end; end;
if tok.tokType = tkBracketLe then begin if tok.tokType = tkBracketLe then begin
@ -266,7 +264,7 @@ begin
// BUGFIX: do not copy '['! // BUGFIX: do not copy '['!
confTok(L, tok); confTok(L, tok);
checkSymbol(L, tok); checkSymbol(L, tok);
val := val +{&} tokToStr(tok); add(val, tokToStr(tok));
confTok(L, tok); confTok(L, tok);
if tok.tokType = tkBracketRi then confTok(L, tok) if tok.tokType = tkBracketRi then confTok(L, tok)
else lexMessage(L, errTokenExpected, ''']'''); else lexMessage(L, errTokenExpected, ''']''');
@ -276,12 +274,12 @@ begin
if length(val) > 0 then addChar(val, ':'); // BUGFIX if length(val) > 0 then addChar(val, ':'); // BUGFIX
confTok(L, tok); // skip ':' or '=' confTok(L, tok); // skip ':' or '='
checkSymbol(L, tok); checkSymbol(L, tok);
val := val +{&} tokToStr(tok); add(val, tokToStr(tok));
confTok(L, tok); // skip symbol confTok(L, tok); // skip symbol
while (tok.ident <> nil) and (tok.ident.id = getIdent('&'+'').id) do begin while (tok.ident <> nil) and (tok.ident.id = getIdent('&'+'').id) do begin
confTok(L, tok); confTok(L, tok);
checkSymbol(L, tok); checkSymbol(L, tok);
val := val +{&} tokToStr(tok); add(val, tokToStr(tok));
confTok(L, tok) confTok(L, tok)
end end
end; end;

View file

@ -61,7 +61,7 @@ type
procedure HandleCmdLine; procedure HandleCmdLine;
var var
command, filename: string; command, filename, prog: string;
start: TTime; start: TTime;
begin begin
{@emit start := getTime(); } {@emit start := getTime(); }
@ -92,13 +92,21 @@ begin
toString(getTime() - start)]); toString(getTime() - start)]);
} }
end; end;
if optRun in gGlobalOptions then if optRun in gGlobalOptions then begin
execExternalProgram(quoteIfContainsWhite(changeFileExt(filename, '')) +{&} {$ifdef unix}
' ' +{&} arguments) prog := './' + quoteIfContainsWhite(changeFileExt(filename, ''));
{$else}
prog := quoteIfContainsWhite(changeFileExt(filename, ''));
{$endif}
execExternalProgram(prog +{&} ' ' +{&} arguments)
end
end end
end; end;
begin begin
//{@emit
// GC_disableMarkAndSweep();
//}
cmdLineInfo := newLineInfo('command line', -1, -1); cmdLineInfo := newLineInfo('command line', -1, -1);
condsyms.InitDefines(); condsyms.InitDefines();
HandleCmdLine(); HandleCmdLine();

View file

@ -123,7 +123,7 @@ procedure createDir(const dir: string);
var var
i: int; i: int;
begin 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)); if dir[i] in [sep, altsep] then sysutils.createDir(ncopy(dir, 1, i-1));
end; end;
sysutils.createDir(dir); sysutils.createDir(dir);

View file

@ -43,6 +43,7 @@ type
{$endif} {$endif}
EOutOfRange = class(Exception) EOutOfRange = class(Exception)
end; end;
EOS = class(Exception) end;
float32 = single; float32 = single;
float64 = double; float64 = double;

View file

@ -31,10 +31,10 @@ const
//cog.outl('VersionMinor = %s;' % ver[1]) //cog.outl('VersionMinor = %s;' % ver[1])
//cog.outl('VersionPatch = %s;' % ver[2]) //cog.outl('VersionPatch = %s;' % ver[2])
//]]] //]]]
VersionAsString = '0.7.4'; VersionAsString = '0.7.6';
VersionMajor = 0; VersionMajor = 0;
VersionMinor = 7; VersionMinor = 7;
VersionPatch = 4; VersionPatch = 6;
//[[[[end]]]] //[[[[end]]]]
implementation implementation

View file

@ -55,6 +55,7 @@ type
cmdCompileToC, cmdCompileToC,
cmdCompileToCpp, cmdCompileToCpp,
cmdCompileToEcmaScript, cmdCompileToEcmaScript,
cmdCompileToLLVM,
cmdInterpret, cmdInterpret,
cmdPretty, cmdPretty,
cmdDoc, cmdDoc,
@ -207,7 +208,15 @@ begin
if startsWith(dir, prefix) then begin if startsWith(dir, prefix) then begin
result := ncopy(dir, length(prefix) + strStart); exit result := ncopy(dir, length(prefix) + strStart); exit
end; 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; end;
function toGeneratedFile(const path, ext: string): string; function toGeneratedFile(const path, ext: string): string;
@ -215,7 +224,8 @@ var
head, tail: string; head, tail: string;
begin begin
splitPath(path, head, tail); 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)]) changeFileExt(tail, ext)])
end; end;
@ -225,10 +235,18 @@ var
head, tail, subdir: string; head, tail, subdir: string;
begin begin
splitPath(f, head, tail); 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 if createSubDir then begin
//Writeln(output, subdir); try
createDir(subdir); createDir(subdir);
except
on EOS do begin
writeln(output, 'cannot create directory: ' + subdir);
halt(1)
end
end
end; end;
result := joinPath(subdir, tail) result := joinPath(subdir, tail)
end; end;

View file

@ -350,7 +350,7 @@ begin
addChar(result.key, '.'); addChar(result.key, '.');
rawGetTok(c, c.tok); rawGetTok(c, c.tok);
if c.tok.kind = tkSymbol then begin if c.tok.kind = tkSymbol then begin
result.key := result.key +{&} c.tok.literal; add(result.key, c.tok.literal);
rawGetTok(c, c.tok); rawGetTok(c, c.tok);
end end
else begin else begin

View file

@ -57,7 +57,7 @@ function astNeeded(s: PSym): bool;
// appropriate to free the procedure body's memory. This is important // appropriate to free the procedure body's memory. This is important
// to keep memory usage down. // to keep memory usage down.
// some passes (the semantic checker) need these: // the semantic checker needs these:
var var
gImportModule: function (const filename: string): PSym; gImportModule: function (const filename: string): PSym;
gIncludeFile: function (const filename: string): PNode; gIncludeFile: function (const filename: string): PNode;

View file

@ -12,7 +12,7 @@ unit platform;
// and operating systems. // and operating systems.
// Note: Unfortunately if an OS or CPU is listed here this does not mean that // 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. // 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 interface

View file

@ -228,7 +228,7 @@ procedure processDynLib(c: PContext; n: PNode; sym: PSym);
var var
lib: PLib; lib: PLib;
begin begin
if sym = nil then if (sym = nil) or (sym.kind = skModule) then
POptionEntry(c.optionStack.tail).dynlib := getLib(c, libDynamic, POptionEntry(c.optionStack.tail).dynlib := getLib(c, libDynamic,
expectStrLit(c, n)) expectStrLit(c, n))
else begin else begin
@ -553,13 +553,6 @@ begin
wPassL: extccomp.addLinkOption(expectStrLit(c, it)); wPassL: extccomp.addLinkOption(expectStrLit(c, it));
wPassC: extccomp.addCompileOption(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); wBreakpoint: PragmaBreakpoint(c, it);
wCheckpoint: PragmaCheckpoint(c, it); wCheckpoint: PragmaCheckpoint(c, it);
@ -585,7 +578,7 @@ begin
processNote(c, n) processNote(c, n)
end; end;
end; end;
if sym <> nil then begin if (sym <> nil) and (sym.kind <> skModule) then begin
lib := POptionEntry(c.optionstack.tail).dynlib; lib := POptionEntry(c.optionstack.tail).dynlib;
if ([lfDynamicLib, lfHeader] * sym.loc.flags = []) and if ([lfDynamicLib, lfHeader] * sym.loc.flags = []) and
(sfImportc in sym.flags) and (sfImportc in sym.flags) and
@ -625,7 +618,7 @@ begin
wHints, wLinedir, wStacktrace, wLinetrace, wOptimization, wHints, wLinedir, wStacktrace, wLinetrace, wOptimization,
wHint, wWarning, wError, wFatal, wDefine, wUndef, wHint, wWarning, wError, wFatal, wDefine, wUndef,
wCompile, wLink, wLinkSys, wPure, wCompile, wLink, wLinkSys, wPure,
wPush, wPop, wFixupSystem, wBreakpoint, wCheckpoint, wPush, wPop, wBreakpoint, wCheckpoint,
wPassL, wPassC, wDeadCodeElim]); wPassL, wPassC, wDeadCodeElim]);
end; end;

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -123,7 +123,7 @@ type
files: TStringSeq; files: TStringSeq;
dataIdx: int; // offset of start of data section dataIdx: int; // offset of start of data section
convertersIdx: int; // offset of start of converters section convertersIdx: int; // offset of start of converters section
initIdx, interfIdx, compilerProcsIdx: int; initIdx, interfIdx, compilerProcsIdx, cgenIdx: int;
filename: string; filename: string;
index, imports: TIndex; index, imports: TIndex;
readerIndex: int; readerIndex: int;
@ -841,6 +841,10 @@ begin
r.initIdx := r.pos+2; // "(\10" r.initIdx := r.pos+2; // "(\10"
skipSection(r); skipSection(r);
end end
else if section = 'CGEN' then begin
r.cgenIdx := r.pos+2;
skipSection(r);
end
else begin else begin
MessageOut('skipping section: ' + toString(r.pos)); MessageOut('skipping section: ' + toString(r.pos));
skipSection(r); skipSection(r);

View file

@ -530,6 +530,7 @@ begin
nkVarSection: begin nkVarSection: begin
for i := 0 to sonsLen(n)-1 do begin for i := 0 to sonsLen(n)-1 do begin
a := n.sons[i]; a := n.sons[i];
if a.kind = nkCommentStmt then continue;
if a.kind <> nkIdentDefs then InternalError(a.info, 'rodwrite.process'); if a.kind <> nkIdentDefs then InternalError(a.info, 'rodwrite.process');
addInterfaceSym(w, a.sons[0].sym); addInterfaceSym(w, a.sons[0].sym);
end end
@ -537,6 +538,7 @@ begin
nkConstSection: begin nkConstSection: begin
for i := 0 to sonsLen(n)-1 do begin for i := 0 to sonsLen(n)-1 do begin
a := n.sons[i]; a := n.sons[i];
if a.kind = nkCommentStmt then continue;
if a.kind <> nkConstDef then InternalError(a.info, 'rodwrite.process'); if a.kind <> nkConstDef then InternalError(a.info, 'rodwrite.process');
addInterfaceSym(w, a.sons[0].sym); addInterfaceSym(w, a.sons[0].sym);
end end
@ -544,6 +546,7 @@ begin
nkTypeSection: begin nkTypeSection: begin
for i := 0 to sonsLen(n)-1 do begin for i := 0 to sonsLen(n)-1 do begin
a := n.sons[i]; a := n.sons[i];
if a.kind = nkCommentStmt then continue;
if a.sons[0].kind <> nkSym then if a.sons[0].kind <> nkSym then
InternalError(a.info, 'rodwrite.process'); InternalError(a.info, 'rodwrite.process');
s := a.sons[0].sym; s := a.sons[0].sym;

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -112,8 +112,6 @@ function ropef(const frmt: TFormatStr; const args: array of PRope): PRope;
procedure appf(var c: PRope; const frmt: TFormatStr; procedure appf(var c: PRope; const frmt: TFormatStr;
const args: array of PRope); const args: array of PRope);
procedure RopeSeqInsert(var rs: TRopeSeq; r: PRope; at: Natural);
function getCacheStats: string; function getCacheStats: string;
function RopeEqualsFile(r: PRope; const f: string): Boolean; function RopeEqualsFile(r: PRope; const f: string): Boolean;
@ -524,8 +522,7 @@ begin
assert(RopeInvariant(result)); assert(RopeInvariant(result));
end; end;
procedure appf(var c: PRope; const frmt: TFormatStr; procedure appf(var c: PRope; const frmt: TFormatStr; const args: array of PRope);
const args: array of PRope);
begin begin
app(c, ropef(frmt, args)) app(c, ropef(frmt, args))
end; end;

View file

@ -1967,6 +1967,10 @@ begin
initParser(q, p.s); initParser(q, p.s);
q.filename := filename; q.filename := filename;
getTokens(readFile(path), false, q.tok); 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); result := parseDoc(q);
end end
end end

View file

@ -428,7 +428,7 @@ begin
L.bufpos := pos; // restore position L.bufpos := pos; // restore position
try 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 then begin
inc(pos, 2); inc(pos, 2);
xi := 0; xi := 0;
@ -451,7 +451,7 @@ begin
end end
end end
end; end;
'o': begin 'o', 'c', 'C': begin
result.base := base8; result.base := base8;
while true do begin while true do begin
case L.buf[pos] of case L.buf[pos] of

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -183,7 +183,7 @@ begin
c := newContext(module, filename); c := newContext(module, filename);
if (c.p <> nil) then InternalError(module.info, 'sem.myOpen'); if (c.p <> nil) then InternalError(module.info, 'sem.myOpen');
c.semConstExpr := semConstExpr; c.semConstExpr := semConstExpr;
c.p := newProcCon(nil); c.p := newProcCon(module);
pushOwner(c.module); pushOwner(c.module);
openScope(c.tab); // scope for imported symbols openScope(c.tab); // scope for imported symbols
SymTabAdd(c.tab, module); // a module knows itself SymTabAdd(c.tab, module); // a module knows itself

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -128,6 +128,7 @@ end;
function newProcCon(owner: PSym): PProcCon; function newProcCon(owner: PSym): PProcCon;
begin begin
if owner = nil then InternalError('owner is nil');
new(result); new(result);
{@ignore} {@ignore}
fillChar(result^, sizeof(result^), 0); fillChar(result^, sizeof(result^), 0);

View file

@ -1,7 +1,7 @@
// //
// //
// The Ethexor Morpork Compiler // The Ethexor Morpork Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -87,19 +87,23 @@ begin
end end
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 var
ds, ss: biggestInt; ds, ss: biggestInt;
begin begin
// this is very unrestrictive; cast is allowed if castDest.size >= src.size // this is very unrestrictive; cast is allowed if castDest.size >= src.size
ds := computeSize(castDest); ds := computeSize(dst);
ss := computeSize(src); ss := computeSize(src);
if ds < 0 then result := false if ds < 0 then result := false
else if ss < 0 then result := false else if ss < 0 then result := false
else else
result := (ds >= ss) or result := (ds >= ss) or
(castDest.kind in [tyInt..tyFloat128]) or (skipGeneric(dst).kind in [tyInt..tyFloat128]) or
(src.kind in [tyInt..tyFloat128]) (skipGeneric(src).kind in [tyInt..tyFloat128])
end; end;
function semConv(c: PContext; n: PNode; s: PSym): PNode; function semConv(c: PContext; n: PNode; s: PSym): PNode;
@ -620,6 +624,11 @@ begin
end; end;
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; function semSym(c: PContext; n: PNode; s: PSym; flags: TExprFlags): PNode;
begin begin
result := newSymNode(s); result := newSymNode(s);
@ -652,7 +661,8 @@ begin
end end
end end
else begin end else begin end
end end;
checkDeprecated(n, s);
end; end;
function isTypeExpr(n: PNode): bool; function isTypeExpr(n: PNode): bool;
@ -787,6 +797,7 @@ begin
result := newSymNode(f); result := newSymNode(f);
result.info := n.info; result.info := n.info;
result.typ := ty; result.typ := ty;
checkDeprecated(n, f);
end end
else else
liMessage(n.sons[1].info, errEnumHasNoValueX, i.s); liMessage(n.sons[1].info, errEnumHasNoValueX, i.s);
@ -814,6 +825,7 @@ begin
n.sons[0] := makeDeref(n.sons[0]); n.sons[0] := makeDeref(n.sons[0]);
n.sons[1] := newSymNode(f); // we now have the correct field n.sons[1] := newSymNode(f); // we now have the correct field
n.typ := f.typ; n.typ := f.typ;
checkDeprecated(n, f);
if check = nil then result := n if check = nil then result := n
else begin else begin
check.sons[0] := n; check.sons[0] := n;
@ -831,14 +843,17 @@ begin
n.sons[1] := newSymNode(f); n.sons[1] := newSymNode(f);
n.typ := f.typ; n.typ := f.typ;
result := n; result := n;
checkDeprecated(n, f);
exit exit
end end
end; end;
// allow things like "".replace(...) // allow things like "".replace(...)
// --> replace("", ...) // --> replace("", ...)
f := SymTabGet(c.tab, i); f := SymTabGet(c.tab, i);
if (f <> nil) and (f.kind = skStub) then loadStub(f); //if (f <> nil) and (f.kind = skStub) then loadStub(f);
if (f <> nil) and (f.kind in [skProc, skIterator]) then begin // 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); result := newNodeI(nkDotCall, n.info);
// This special node kind is to merge with the call handler in `semExpr`. // This special node kind is to merge with the call handler in `semExpr`.
addSon(result, newIdentNode(i, n.info)); addSon(result, newIdentNode(i, n.info));
@ -1121,6 +1136,57 @@ begin
result := semFieldAccess(c, n, flags); result := semFieldAccess(c, n, flags);
end; 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; function semExpr(c: PContext; n: PNode; flags: TExprFlags = {@set}[]): PNode;
var var
s: PSym; s: PSym;
@ -1174,6 +1240,7 @@ begin
checkMinSonsLen(n, 1); checkMinSonsLen(n, 1);
s := qualifiedLookup(c, n.sons[0], false); s := qualifiedLookup(c, n.sons[0], false);
if (s <> nil) then begin if (s <> nil) then begin
checkDeprecated(n, s);
case s.kind of case s.kind of
skMacro: begin skMacro: begin
include(s.flags, sfUsed); include(s.flags, sfUsed);
@ -1200,6 +1267,9 @@ begin
end end
else result := semIndirectOp(c, n); else result := semIndirectOp(c, n);
end; end;
nkMacroStmt: begin
result := semMacroStmt(c, n);
end;
nkBracketExpr: begin nkBracketExpr: begin
checkMinSonsLen(n, 1); checkMinSonsLen(n, 1);
s := qualifiedLookup(c, n.sons[0], false); s := qualifiedLookup(c, n.sons[0], false);
@ -1221,7 +1291,7 @@ begin
nkPar: begin nkPar: begin
case checkPar(n) of case checkPar(n) of
paNone: result := nil; paNone: result := nil;
paTuplePositions: result := semTuplePositionsConstr(c, n); paTuplePositions: result := semTuplePositionsConstr(c, n);
paTupleFields: result := semTupleFieldsConstr(c, n); paTupleFields: result := semTupleFieldsConstr(c, n);
paSingle: result := semExpr(c, n.sons[0]); paSingle: result := semExpr(c, n.sons[0]);
end; end;

View file

@ -406,6 +406,8 @@ begin
else result := copyTree(s.ast); // BUGFIX else result := copyTree(s.ast); // BUGFIX
end end
end end
else if s.kind = skProc then // BUGFIX
result := n
end; end;
nkCharLit..nkNilLit: result := copyNode(n); nkCharLit..nkNilLit: result := copyNode(n);
nkIfExpr: result := getConstIfExpr(module, n); nkIfExpr: result := getConstIfExpr(module, n);

View file

@ -58,10 +58,29 @@ begin
end end
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; function instantiateType(c: PInstantiateClosure; typ: PType): PType;
var var
i: int; i: int;
begin begin
if typ = nil then begin result := nil; exit end;
result := PType(idTableGet(c.typeMap, typ)); result := PType(idTableGet(c.typeMap, typ));
if result <> nil then exit; if result <> nil then exit;
//if typ.kind = tyOpenArray then //if typ.kind = tyOpenArray then
@ -73,6 +92,7 @@ begin
result.sons[i] := instantiateType(c, result.sons[i]); result.sons[i] := instantiateType(c, result.sons[i]);
if result.n <> nil then if result.n <> nil then
result.n := instTypeNode(c, result.n); result.n := instTypeNode(c, result.n);
genericToConcreteTypeKind(result);
end end
else else
result := typ; result := typ;
@ -272,6 +292,20 @@ begin
result := instantiateType(c, t); result := instantiateType(c, t);
end; 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; function partialSpecialization(c: PContext; n: PNode; s: PSym): PNode;
begin begin
result := n; result := n;

View file

@ -42,7 +42,7 @@ begin
end; end;
if result = nil then result := newNodeI(nkNilLit, n.info); if result = nil then result := newNodeI(nkNilLit, n.info);
// The ``when`` statement implements the mechanism for platform dependant // 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. // ``when`` statement.
IDsynchronizationPoint(200); IDsynchronizationPoint(200);
end; end;
@ -59,9 +59,11 @@ begin
case it.kind of case it.kind of
nkElifBranch: begin nkElifBranch: begin
checkSonsLen(it, 2); checkSonsLen(it, 2);
openScope(c.tab);
it.sons[0] := semExprWithType(c, it.sons[0]); it.sons[0] := semExprWithType(c, it.sons[0]);
checkBool(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; end;
nkElse: begin nkElse: begin
if sonsLen(it) = 1 then it.sons[0] := semStmtScope(c, it.sons[0]) 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: // now parse the string literal and substitute symbols:
a := strStart; a := strStart;
repeat repeat
b := findSubStr(marker, str, a); b := strutils.find(str, marker, a);
if b < strStart then if b < strStart then
sub := ncopy(str, a) sub := ncopy(str, a)
else else
@ -153,7 +155,7 @@ begin
addSon(result, newStrNode(nkStrLit, sub)); addSon(result, newStrNode(nkStrLit, sub));
if b < strStart then break; if b < strStart then break;
c := findSubStr(marker, str, b+1); c := strutils.find(str, marker, b+1);
if c < strStart then if c < strStart then
sub := ncopy(str, b+1) sub := ncopy(str, b+1)
else else
@ -179,11 +181,13 @@ function semWhile(c: PContext; n: PNode): PNode;
begin begin
result := n; result := n;
checkSonsLen(n, 2); checkSonsLen(n, 2);
openScope(c.tab);
n.sons[0] := semExprWithType(c, n.sons[0]); n.sons[0] := semExprWithType(c, n.sons[0]);
CheckBool(n.sons[0]); CheckBool(n.sons[0]);
inc(c.p.nestedLoopCounter); inc(c.p.nestedLoopCounter);
n.sons[1] := semStmtScope(c, n.sons[1]); n.sons[1] := semStmt(c, n.sons[1]);
dec(c.p.nestedLoopCounter); dec(c.p.nestedLoopCounter);
closeScope(c.tab);
end; end;
function semCase(c: PContext; n: PNode): PNode; function semCase(c: PContext; n: PNode): PNode;
@ -197,6 +201,7 @@ begin
// check selector: // check selector:
result := n; result := n;
checkMinSonsLen(n, 2); checkMinSonsLen(n, 2);
openScope(c.tab);
n.sons[0] := semExprWithType(c, n.sons[0]); n.sons[0] := semExprWithType(c, n.sons[0]);
chckCovered := false; chckCovered := false;
covered := 0; covered := 0;
@ -216,7 +221,7 @@ begin
end; end;
nkElifBranch: begin nkElifBranch: begin
chckCovered := false; chckCovered := false;
checkSonsLen(n, 2); checkSonsLen(x, 2);
x.sons[0] := semExprWithType(c, x.sons[0]); x.sons[0] := semExprWithType(c, x.sons[0]);
checkBool(x.sons[0]); checkBool(x.sons[0]);
x.sons[1] := semStmtScope(c, x.sons[1]) x.sons[1] := semStmtScope(c, x.sons[1])
@ -231,6 +236,7 @@ begin
end; end;
if chckCovered and (covered <> lengthOrd(n.sons[0].typ)) then if chckCovered and (covered <> lengthOrd(n.sons[0].typ)) then
liMessage(n.info, errNotAllCasesCovered); liMessage(n.info, errNotAllCasesCovered);
closeScope(c.tab);
end; end;
function semAsgn(c: PContext; n: PNode): PNode; function semAsgn(c: PContext; n: PNode): PNode;
@ -401,6 +407,8 @@ begin
end end
else else
def := nil; def := nil;
if not typeAllowed(typ, skVar) then
liMessage(a.info, errXisNoType, typeToString(typ));
tup := skipGeneric(typ); tup := skipGeneric(typ);
if a.kind = nkVarTuple then begin if a.kind = nkVarTuple then begin
if tup.kind <> tyTuple then liMessage(a.info, errXExpected, 'tuple'); if tup.kind <> tyTuple then liMessage(a.info, errXExpected, 'tuple');
@ -408,7 +416,7 @@ begin
liMessage(a.info, errWrongNumberOfVariables); liMessage(a.info, errWrongNumberOfVariables);
end; end;
for j := 0 to len-3 do begin 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]); v := semIdentWithPragma(c, skVar, a.sons[j], {@set}[sfStar, sfMinus]);
include(v.flags, sfGlobal); include(v.flags, sfGlobal);
end end
@ -441,7 +449,7 @@ begin
if a.kind = nkCommentStmt then continue; if a.kind = nkCommentStmt then continue;
if (a.kind <> nkConstDef) then IllFormedAst(a); if (a.kind <> nkConstDef) then IllFormedAst(a);
checkSonsLen(a, 3); 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]); v := semIdentWithPragma(c, skConst, a.sons[0], {@set}[sfStar, sfMinus]);
include(v.flags, sfGlobal); include(v.flags, sfGlobal);
end end
@ -456,6 +464,8 @@ begin
def := fitRemoveHiddenConv(c, typ, def); def := fitRemoveHiddenConv(c, typ, def);
end end
else typ := def.typ; else typ := def.typ;
if not typeAllowed(typ, skConst) then
liMessage(a.info, errXisNoType, typeToString(typ));
v.typ := typ; v.typ := typ;
v.ast := def; // no need to copy v.ast := def; // no need to copy
@ -480,6 +490,7 @@ begin
result := n; result := n;
checkMinSonsLen(n, 3); checkMinSonsLen(n, 3);
len := sonsLen(n); len := sonsLen(n);
openScope(c.tab);
if n.sons[len-2].kind = nkRange then begin if n.sons[len-2].kind = nkRange then begin
checkSonsLen(n.sons[len-2], 2); checkSonsLen(n.sons[len-2], 2);
// convert ``in 3..5`` to ``in countup(3, 5)`` // convert ``in 3..5`` to ``in countup(3, 5)``
@ -500,7 +511,6 @@ begin
end; end;
n.sons[len-2] := semExprWithType(c, n.sons[len-2]); n.sons[len-2] := semExprWithType(c, n.sons[len-2]);
iter := skipGeneric(n.sons[len-2].typ); iter := skipGeneric(n.sons[len-2].typ);
openScope(c.tab);
if iter.kind <> tyTuple then begin if iter.kind <> tyTuple then begin
if len <> 3 then liMessage(n.info, errWrongNumberOfVariables); if len <> 3 then liMessage(n.info, errWrongNumberOfVariables);
v := newSymS(skForVar, n.sons[0], c); v := newSymS(skForVar, n.sons[0], c);
@ -573,7 +583,7 @@ begin
end; end;
end; end;
procedure semGenericParamList(c: PContext; n: PNode); procedure semGenericParamList(c: PContext; n: PNode; father: PType = nil);
var var
i: int; i: int;
s: PSym; s: PSym;
@ -590,6 +600,7 @@ begin
s.typ := newTypeS(tyGenericParam, c); s.typ := newTypeS(tyGenericParam, c);
s.typ.sym := s; s.typ.sym := s;
end; end;
if father <> nil then addSon(father, s.typ);
s.position := i; s.position := i;
n.sons[i] := newSymNode(s); n.sons[i] := newSymNode(s);
addDecl(c, s); addDecl(c, s);
@ -631,7 +642,7 @@ begin
if a.kind = nkCommentStmt then continue; if a.kind = nkCommentStmt then continue;
if (a.kind <> nkTypeDef) then IllFormedAst(a); if (a.kind <> nkTypeDef) then IllFormedAst(a);
checkSonsLen(a, 3); 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]); s := semIdentWithPragma(c, skType, a.sons[0], {@set}[sfStar, sfMinus]);
include(s.flags, sfGlobal); include(s.flags, sfGlobal);
end end
@ -665,7 +676,8 @@ begin
openScope(c.tab); openScope(c.tab);
pushOwner(s); pushOwner(s);
s.typ.kind := tyGeneric; 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 // process the type body for symbol lookup of generic params
// we can use the same algorithm as for template parameters: // we can use the same algorithm as for template parameters:
a.sons[2] := resolveTemplateParams(c, a.sons[2]); a.sons[2] := resolveTemplateParams(c, a.sons[2]);
@ -736,7 +748,7 @@ var
begin begin
result := n; result := n;
checkSonsLen(n, codePos+1); checkSonsLen(n, codePos+1);
if c.p.owner <> nil then if c.p.owner.kind <> skModule then
liMessage(n.info, errIteratorNotAllowed); liMessage(n.info, errIteratorNotAllowed);
oldP := c.p; // restore later oldP := c.p; // restore later
s := semIdentVis(c, skIterator, n.sons[0], {@set}[sfStar]); s := semIdentVis(c, skIterator, n.sons[0], {@set}[sfStar]);
@ -857,7 +869,7 @@ var
begin begin
result := n; result := n;
checkSonsLen(n, codePos+1); 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]); s := semIdentVis(c, kind, n.sons[0], {@set}[sfStar]);
include(s.flags, sfGlobal); include(s.flags, sfGlobal);
end end
@ -885,7 +897,7 @@ begin
proto := SearchForProc(c, s, c.tab.tos-2); // -2 because we have a scope open proto := SearchForProc(c, s, c.tab.tos-2); // -2 because we have a scope open
// for parameters // for parameters
if proto = nil then begin 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 s.typ.callConv := ccClosure
else else
s.typ.callConv := lastOptionEntry(c).defaultCC; s.typ.callConv := lastOptionEntry(c).defaultCC;
@ -1025,7 +1037,7 @@ begin
if nfSem in n.flags then exit; if nfSem in n.flags then exit;
case n.kind of case n.kind of
nkAsgn: result := semAsgn(c, n); nkAsgn: result := semAsgn(c, n);
nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand: nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkMacroStmt:
result := semCommand(c, n); result := semCommand(c, n);
nkEmpty, nkCommentStmt, nkNilLit: begin end; nkEmpty, nkCommentStmt, nkNilLit: begin end;
nkBlockStmt: result := semBlock(c, n); nkBlockStmt: result := semBlock(c, n);
@ -1051,8 +1063,7 @@ begin
nkDiscardStmt: result := semDiscard(c, n); nkDiscardStmt: result := semDiscard(c, n);
nkWhileStmt: result := semWhile(c, n); nkWhileStmt: result := semWhile(c, n);
nkTryStmt: result := semTry(c, n); nkTryStmt: result := semTry(c, n);
nkBreakStmt, nkContinueStmt: nkBreakStmt, nkContinueStmt: result := semBreakOrContinue(c, n);
result := semBreakOrContinue(c, n);
nkForStmt: result := semFor(c, n); nkForStmt: result := semFor(c, n);
nkCaseStmt: result := semCase(c, n); nkCaseStmt: result := semCase(c, n);
nkReturnStmt: result := semReturn(c, n); nkReturnStmt: result := semReturn(c, n);

View file

@ -164,7 +164,7 @@ var
i, j, len, counter: int; i, j, len, counter: int;
params, p, paramKind: PNode; params, p, paramKind: PNode;
begin 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]); s := semIdentVis(c, skTemplate, n.sons[0], {@set}[sfStar]);
include(s.flags, sfGlobal); include(s.flags, sfGlobal);
end end

View file

@ -51,10 +51,12 @@ begin
e := newSymS(skEnumField, n.sons[i].sons[0], c); e := newSymS(skEnumField, n.sons[i].sons[0], c);
v := semConstExpr(c, n.sons[i].sons[1]); v := semConstExpr(c, n.sons[i].sons[1]);
x := getOrdValue(v); x := getOrdValue(v);
if (x <> counter) and (i <> 1) then if i <> 1 then begin
include(result.flags, tfEnumHasWholes); if (x <> counter) then
if x < counter then include(result.flags, tfEnumHasWholes);
liMessage(n.sons[i].info, errInvalidOrderInEnumX, e.name.s); if x < counter then
liMessage(n.sons[i].info, errInvalidOrderInEnumX, e.name.s);
end;
counter := x; counter := x;
end; end;
nkSym: e := n.sons[i].sym; nkSym: e := n.sons[i].sym;
@ -247,7 +249,7 @@ begin
end end
end end
end; end;
(*
function instGenericAux(c: PContext; templ, actual: PNode; function instGenericAux(c: PContext; templ, actual: PNode;
sym: PSym): PNode; sym: PSym): PNode;
var var
@ -271,34 +273,48 @@ begin
result.sons[i] := instGenericAux(c, templ.sons[i], actual, sym); result.sons[i] := instGenericAux(c, templ.sons[i], actual, sym);
end end
end end
end; end; *)
function semGeneric(c: PContext; n: PNode; s: PSym; prev: PType): PType; function semGeneric(c: PContext; n: PNode; s: PSym; prev: PType): PType;
var var
i: int; i: int;
elem: PType; elem: PType;
inst: PNode; inst: PNode;
cl: PInstantiateClosure;
begin begin
if (s.typ = nil) or (s.typ.kind <> tyGeneric) then if (s.typ = nil) or (s.typ.kind <> tyGeneric) then
liMessage(n.info, errCannotInstantiateX, s.name.s); liMessage(n.info, errCannotInstantiateX, s.name.s);
result := newOrPrevType(tyGenericInst, prev, c); // new ID... result := newOrPrevType(tyGenericInst, prev, c); // new ID...
result.containerID := s.typ.containerID; // ... but the same containerID result.containerID := s.typ.containerID; // ... but the same containerID
result.sym := s; result.sym := s;
if (s.typ.containerID = 0) then if (s.typ.containerID = 0) then InternalError(n.info, 'semtypes.semGeneric');
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 for i := 1 to sonsLen(n)-1 do begin
elem := semTypeNode(c, n.sons[i], nil); elem := semTypeNode(c, n.sons[i], nil);
if elem.kind = tyGenericParam then 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); addSon(result, elem);
end; end;
if s.ast <> nil then begin if s.ast <> nil then begin
if (result.kind = tyGenericInst) 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'); internalError(n.info, 'Generic containers not implemented');
// XXX: implementation does not work this way // 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: // 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; elem.id := result.containerID;
addSon(result, elem); addSon(result, elem);
end end

View file

@ -80,7 +80,7 @@ var
begin begin
result := msgKindToString(errTypeMismatch); result := msgKindToString(errTypeMismatch);
for i := 1 to sonsLen(n)-1 do begin 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)); add(result, typeToString(n.sons[i].typ));
if i <> sonsLen(n)-1 then add(result, ', '); if i <> sonsLen(n)-1 then add(result, ', ');
end; end;
@ -361,9 +361,7 @@ begin // is a subtype of f?
tyProc: begin tyProc: begin
if (sonsLen(f) = sonsLen(a)) and (f.callconv = a.callconv) then begin if (sonsLen(f) = sonsLen(a)) and (f.callconv = a.callconv) then begin
// Note: We have to do unification for the parameters before the // Note: We have to do unification for the parameters before the
// return type! Otherwise it'd be counter-intuitive for the standard // return type!
// Nimrod syntax. For the C-based syntax it IS counter-intuitive.
// But that is one of the reasons a standard syntax was picked.
result := isEqual; // start with maximum; also correct for no result := isEqual; // start with maximum; also correct for no
// params at all // params at all
for i := 1 to sonsLen(f)-1 do begin for i := 1 to sonsLen(f)-1 do begin
@ -433,8 +431,9 @@ begin // is a subtype of f?
end; end;
tyAnyEnum: begin tyAnyEnum: begin
case a.kind of case a.kind of
tyRange: result := typeRel(mapping, f, base(a)); tyRange: result := typeRel(mapping, f, base(a));
tyEnum: result := isSubtype; tyEnum: result := isSubtype;
tyAnyEnum: result := isEqual;
else begin end else begin end
end end
end; end;
@ -726,11 +725,11 @@ begin
end end
else begin else begin
setSon(m.call, formal.position+1, arg); setSon(m.call, formal.position+1, arg);
end; end
inc(f);
end end
end; end;
inc(a); inc(a);
inc(f);
end; end;
// iterate over all formal params and check all are provided: // iterate over all formal params and check all are provided:
f := 1; f := 1;

View file

@ -269,14 +269,14 @@ begin
j := i+1; j := i+1;
while (j <= length(f)+strStart-1) and (f[j] <> '}') do inc(j); while (j <= length(f)+strStart-1) and (f[j] <> '}') do inc(j);
key := ncopy(f, i+2+strStart-1, j-1+strStart-1); 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 i := j+1
end; end;
'a'..'z', 'A'..'Z', #128..#255, '_': begin 'a'..'z', 'A'..'Z', #128..#255, '_': begin
j := i+1; j := i+1;
while (j <= length(f)+strStart-1) and (f[j] in PatternChars) do inc(j); 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); 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 i := j
end end
else begin else begin

View file

@ -28,6 +28,7 @@ function cmp(const x, y: string): int;
function cmpIgnoreCase(const x, y: string): int; function cmpIgnoreCase(const x, y: string): int;
function format(const f: string; const args: array of string): string; 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 toHex(x: BiggestInt; len: int): string;
function toOctal(value: Char): string; function toOctal(value: Char): string;
@ -47,7 +48,7 @@ function ToString(b: Boolean): string; overload;
function IntToStr(i: BiggestInt; minChars: int): string; 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; function replaceStr(const s, search, by: string): string;
procedure deleteStr(var s: string; first, last: int); procedure deleteStr(var s: string; first, last: int);
@ -81,8 +82,8 @@ implementation
function quoteIfContainsWhite(const s: string): string; function quoteIfContainsWhite(const s: string): string;
begin begin
if ((findSubStr(' ', s) >= strStart) if ((find(s, ' ') >= strStart)
or (findSubStr(#9, s) >= strStart)) and (s[strStart] <> '"') then or (find(s, #9) >= strStart)) and (s[strStart] <> '"') then
result := '"' +{&} s +{&} '"' result := '"' +{&} s +{&} '"'
else else
result := s result := s
@ -247,7 +248,7 @@ begin
result := result + s[i] result := result + s[i]
end; end;
function findSubStr(const sub, s: string; start: int = 1): int; function find(const s, sub: string; start: int = 1): int;
var var
i, j, M, N: int; i, j, M, N: int;
begin begin
@ -277,7 +278,7 @@ begin
result := ''; result := '';
i := 1; i := 1;
repeat repeat
j := findSubStr(search, s, i); j := find(s, search, i);
if j = 0 then begin if j = 0 then begin
// copy the rest: // copy the rest:
result := result + copy(s, i, length(s) - i + 1); result := result + copy(s, i, length(s) - i + 1);
@ -475,7 +476,7 @@ begin
until false until false
end; 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 var
i: int; i: int;
y: string; y: string;
@ -491,30 +492,29 @@ begin
result := -1 result := -1
end; 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 const
PatternChars = ['a'..'z', 'A'..'Z', '0'..'9', '_', #128..#255]; PatternChars = ['a'..'z', 'A'..'Z', '0'..'9', '_', #128..#255];
var var
i, j, x: int; i, j, x: int;
begin begin
result := '';
i := 1; i := 1;
while i <= length(f) do while i <= length(f) do
if f[i] = '$' then begin if f[i] = '$' then begin
case f[i+1] of case f[i+1] of
'$': begin '$': begin
result := result + '$'; addChar(result, '$');
inc(i, 2); inc(i, 2);
end; end;
'1'..'9': begin '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); inc(i, 2);
end; end;
'{': begin '{': begin
j := i+1; j := i+1;
while (j <= length(f)) and (f[j] <> '}') do inc(j); while (j <= length(f)) and (f[j] <> '}') do inc(j);
x := find(ncopy(f, i+2, j-1), args); 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(''); else raise EInvalidFormatStr.create('');
i := j+1 i := j+1
end; end;
@ -522,7 +522,7 @@ begin
j := i+1; j := i+1;
while (j <= length(f)) and (f[j] in PatternChars) do inc(j); while (j <= length(f)) and (f[j] in PatternChars) do inc(j);
x := find(ncopy(f, i+1, j-1), args); 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)); else raise EInvalidFormatStr.create(ncopy(f, i+1, j-1));
i := j i := j
end end
@ -530,11 +530,17 @@ begin
end end
end end
else begin else begin
result := result + f[i]; addChar(result, f[i]);
inc(i) inc(i)
end end
end; end;
function format(const f: string; const args: array of string): string;
begin
result := '';
addf(result, f, args)
end;
{@ignore} {@ignore}
{$ifopt Q-} {$Q+} {$ifopt Q-} {$Q+}
{$else} {$define Q_off} {$else} {$define Q_off}

View file

@ -14,6 +14,7 @@ unit transf;
// * inlines iterators // * inlines iterators
// * inlines constants // * inlines constants
// * performes contant folding // * performes contant folding
// * introduces nkHiddenDeref, nkHiddenSubConv, etc.
interface interface
@ -446,12 +447,51 @@ begin
end; end;
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; function transformFor(c: PTransf; n: PNode): PNode;
// generate access statements for the parameters (unless they are constant) // generate access statements for the parameters (unless they are constant)
// put mapping from formal parameters to actual parameters // put mapping from formal parameters to actual parameters
var var
i, len: int; i, len: int;
call, e, v, body: PNode; call, v, body, arg: PNode;
newC: PTransCon; newC: PTransCon;
temp, formal: PSym; temp, formal: PSym;
begin begin
@ -473,24 +513,24 @@ begin
// generate access statements for the parameters (unless they are constant) // generate access statements for the parameters (unless they are constant)
pushTransCon(c, newC); pushTransCon(c, newC);
for i := 1 to sonsLen(call)-1 do begin 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; formal := skipGeneric(newC.owner.typ).n.sons[i].sym;
if e <> nil then //if IdentEq(newc.Owner.name, 'items') then
IdNodeTablePut(newC.mapping, formal, e) // liMessage(arg.info, warnUser, 'items: ' + nodeKindToStr[arg.kind]);
else if (skipConv(call.sons[i]).kind = nkSym) then begin case putArgInto(arg, formal.typ) of
// since parameters cannot be modified, we can identify the formal and paDirectMapping: IdNodeTablePut(newC.mapping, formal, arg);
// the actual params paFastAsgn: begin
IdNodeTablePut(newC.mapping, formal, call.sons[i]); // generate a temporary and produce an assignment statement:
end temp := newTemp(c, formal.typ, formal.info);
else begin addVar(v, newSymNode(temp));
// generate a temporary and produce an assignment statement: addSon(result, newAsgnStmt(c, newSymNode(temp), arg));
temp := newTemp(c, formal.typ, formal.info); IdNodeTablePut(newC.mapping, formal, newSymNode(temp));
addVar(v, newSymNode(temp)); end;
// BUGFIX: do not copy call.sons[i], but transform it! paVarAsgn: begin
addSon(result, newAsgnStmt(c, newSymNode(temp), assert(skipGeneric(formal.typ).kind = tyVar);
transform(c, call.sons[i]))); InternalError(arg.info, 'not implemented: pass to var parameter');
IdNodeTablePut(newC.mapping, formal, newSymNode(temp)); // BUGFIX end;
end end;
end; end;
body := newC.owner.ast.sons[codePos]; body := newC.owner.ast.sons[codePos];
pushInfoContext(n.info); pushInfoContext(n.info);
@ -668,7 +708,7 @@ function transformCase(c: PTransf; n: PNode): PNode;
// removes `elif` branches of a case stmt // removes `elif` branches of a case stmt
var var
len, i, j: int; len, i, j: int;
ifs: PNode; ifs, elsen: PNode;
begin begin
len := sonsLen(n); len := sonsLen(n);
i := len-1; i := len-1;
@ -678,9 +718,11 @@ begin
if (n.sons[i].kind <> nkOfBranch) then if (n.sons[i].kind <> nkOfBranch) then
InternalError(n.sons[i].info, 'transformCase'); InternalError(n.sons[i].info, 'transformCase');
ifs := newNodeI(nkIfStmt, n.sons[i+1].info); 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]); for j := i+1 to len-1 do addSon(ifs, n.sons[j]);
setLength(n.sons, i+2); setLength(n.sons, i+2);
n.sons[i+1] := ifs; addSon(elsen, ifs);
n.sons[i+1] := elsen;
end; end;
result := n; result := n;
for j := 0 to sonsLen(n)-1 do result.sons[j] := transform(c, n.sons[j]); for j := 0 to sonsLen(n)-1 do result.sons[j] := transform(c, n.sons[j]);

View file

@ -1,12 +1,11 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
// //
unit types; unit types;
// this module contains routines for accessing and iterating over 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`. // Returns result of `iter`.
function SameType(x, y: PType): Boolean; function SameType(x, y: PType): Boolean;
function SameTypeOrNil(a, b: 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 // made or intializing of the type field suffices or if there is no type field
// at all in this type. // at all in this type.
function typeAllowed(t: PType; kind: TSymKind): bool;
implementation implementation
function InvalidGenericInst(f: PType): bool; function InvalidGenericInst(f: PType): bool;
@ -185,11 +185,13 @@ begin
n := sym.typ.n; n := sym.typ.n;
for i := 1 to sonsLen(n)-1 do begin for i := 1 to sonsLen(n)-1 do begin
p := n.sons[i]; p := n.sons[i];
assert(p.kind = nkSym); if (p.kind <> nkSym) then InternalError('getProcHeader');
result := result +{&} p.sym.name.s +{&} ': ' +{&} typeToString(p.sym.typ); add(result, p.sym.name.s);
if i <> sonsLen(n)-1 then result := result + ', '; add(result, ': ');
add(result, typeToString(p.sym.typ));
if i <> sonsLen(n)-1 then add(result, ', ');
end; end;
result := result + ')'; addChar(result, ')');
if n.sons[0].typ <> nil then if n.sons[0].typ <> nil then
result := result +{&} ': ' +{&} typeToString(n.sons[0].typ); result := result +{&} ': ' +{&} typeToString(n.sons[0].typ);
end; end;
@ -928,6 +930,96 @@ begin
end end
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; function align(address, alignment: biggestInt): biggestInt;
begin begin
result := (address + (alignment-1)) and not (alignment-1); result := (address + (alignment-1)) and not (alignment-1);

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -66,7 +66,7 @@ type
wMerge, wLib, wDynlib, wCompilerproc, wCppmethod, wFatal, wMerge, wLib, wDynlib, wCompilerproc, wCppmethod, wFatal,
wError, wWarning, wHint, wLine, wPush, wPop, wError, wWarning, wHint, wLine, wPush, wPop,
wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wPragma, wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wPragma,
wLink, wCompile, wLinksys, wFixupsystem, wDeprecated, wVarargs, wLink, wCompile, wLinksys, wDeprecated, wVarargs,
wByref, wCallconv, wBreakpoint, wDebugger, wNimcall, wStdcall, wByref, wCallconv, wBreakpoint, wDebugger, wNimcall, wStdcall,
wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure, wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure,
wNoconv, wOn, wOff, wChecks, wRangechecks, wBoundchecks, wNoconv, wOn, wOff, wChecks, wRangechecks, wBoundchecks,
@ -84,7 +84,7 @@ type
wMaxErr, wExpr, wStmt, wTypeDesc, wMaxErr, wExpr, wStmt, wTypeDesc,
wSubsChar, wAstCache, wAcyclic, wIndex, wSubsChar, wAstCache, wAcyclic, wIndex,
// commands: // commands:
wCompileToC, wCompileToCpp, wCompileToEcmaScript, wCompileToC, wCompileToCpp, wCompileToEcmaScript, wCompileToLLVM,
wPretty, wDoc, wPas, wPretty, wDoc, wPas,
wGenDepend, wListDef, wCheck, wParse, wScan, wBoot, wLazy, wGenDepend, wListDef, wCheck, wParse, wScan, wBoot, wLazy,
wRst2html, wI, wRst2html, wI,
@ -140,7 +140,7 @@ const
'merge', 'lib', 'dynlib', 'compilerproc', 'cppmethod', 'fatal', 'merge', 'lib', 'dynlib', 'compilerproc', 'cppmethod', 'fatal',
'error', 'warning', 'hint', 'line', 'push', 'pop', 'error', 'warning', 'hint', 'line', 'push', 'pop',
'define', 'undef', 'linedir', 'stacktrace', 'linetrace', 'pragma', 'define', 'undef', 'linedir', 'stacktrace', 'linetrace', 'pragma',
'link', 'compile', 'linksys', 'fixupsystem', 'deprecated', 'varargs', 'link', 'compile', 'linksys', 'deprecated', 'varargs',
'byref', 'callconv', 'breakpoint', 'debugger', 'nimcall', 'stdcall', 'byref', 'callconv', 'breakpoint', 'debugger', 'nimcall', 'stdcall',
'cdecl', 'safecall', 'syscall', 'inline', 'noinline', 'fastcall', 'closure', 'cdecl', 'safecall', 'syscall', 'inline', 'noinline', 'fastcall', 'closure',
'noconv', 'on', 'off', 'checks', 'rangechecks', 'boundchecks', 'noconv', 'on', 'off', 'checks', 'rangechecks', 'boundchecks',
@ -158,7 +158,7 @@ const
'maxerr', 'expr', 'stmt', 'typedesc', 'maxerr', 'expr', 'stmt', 'typedesc',
'subschar', 'astcache', 'acyclic', 'index', 'subschar', 'astcache', 'acyclic', 'index',
// commands: // commands:
'compiletoc', 'compiletocpp', 'compiletoecmascript', 'compiletoc', 'compiletocpp', 'compiletoecmascript', 'compiletollvm',
'pretty', 'doc', 'pas', 'gendepend', 'listdef', 'check', 'parse', 'pretty', 'doc', 'pas', 'gendepend', 'listdef', 'check', 'parse',
'scan', 'boot', 'lazy', 'rst2html', 'i'+'', 'scan', 'boot', 'lazy', 'rst2html', 'i'+'',

View file

@ -13,3 +13,4 @@
@elif vcc: @elif vcc:
cgen.speed = "" cgen.speed = ""
@end @end

View file

@ -59,6 +59,12 @@ Files: "tests/*.cfg"
Files: "tests/*.tmpl" Files: "tests/*.tmpl"
Files: "tests/gtk/*.nim" 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/*.c"
Files: "lib/base/*.nim" Files: "lib/base/*.nim"
Files: "lib/base/gtk/*.nim" Files: "lib/base/gtk/*.nim"
@ -75,8 +81,8 @@ Files: "lib/base/lua/*.nim"
[Windows] [Windows]
Files: "bin/nimrod.exe" Files: "bin/nimrod.exe"
Files: "dist/llvm-gcc4.2" Files: "dist/mingw"
BinPath: r"bin;dist\llvm-gcc4.2\bin" BinPath: r"bin;dist\mingw\bin"
[Unix] [Unix]

View file

@ -1,4 +1,4 @@
# Test the case statment # Test the case statement
type type
tenum = enum eA, eB, eC tenum = enum eA, eB, eC
@ -6,6 +6,7 @@ type
var var
x: string x: string
y: Tenum = eA y: Tenum = eA
i: int
case y case y
of eA: write(stdout, "a\n") 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 "cc", "hash", "when": nil
of "will", "it", "finally", "be", "generated": nil of "will", "it", "finally", "be", "generated": nil
else: write(stdout, "das sollte nicht passieren!\N") 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

View file

@ -1,7 +1,8 @@
# Test wether the bindings at least compile... # Test wether the bindings at least compile...
import import
unicode, unicode, cgi, terminal, libcurl, web,
parsexml, parseopt, parsecfg,
osproc, zipfiles, osproc, zipfiles,
sdl, smpeg, sdl_gfx, sdl_net, sdl_mixer, sdl_ttf, sdl, smpeg, sdl_gfx, sdl_net, sdl_mixer, sdl_ttf,
sdl_image, sdl_mixer_nosmpeg, sdl_image, sdl_mixer_nosmpeg,

View file

@ -1,5 +1,14 @@
# test variables of type proc # 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 var
x: proc (a, b: int): int {.cdecl.} x: proc (a, b: int): int {.cdecl.}
@ -7,8 +16,11 @@ proc ha(c, d: int): int {.cdecl.} =
echo(c + d) echo(c + d)
result = c + d result = c + d
for a in items(algos):
a()
x = ha x = ha
discard x(3, 4) discard x(3, 4)
#OUT 7 #OUT papbpcpdpe7

View file

@ -13,5 +13,9 @@ type
PB = ref TB PB = ref TB
TB = array [0..3, P1] TB = array [0..3, P1]
T1 = array [0..6, PB] T1 = array [0..6, PB]
var
x: PA
new(x)
#ERROR_MSG internal error: cannot generate C type for: PA #ERROR_MSG internal error: cannot generate C type for: PA

View file

@ -4,9 +4,16 @@
import import
regexprs 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]+"): if "Username".match("[A-Za-z]+"):
echo("Yes!") echo("Yes!")
else: else:
echo("Bug!") echo("Bug!")
#OUT Yes! #OUT key: keyAYes!

Some files were not shown because too many files have changed in this diff Show more