version 0.7.6
This commit is contained in:
parent
439aa2d04d
commit
e792940f52
106 changed files with 8394 additions and 10083 deletions
242
cogapp.py
242
cogapp.py
|
|
@ -5,13 +5,10 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# $Id: cogapp.py 141 2008-05-22 10:56:43Z nedbat $
|
# $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)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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'},
|
||||||
|
|
|
||||||
21
doc/docs.txt
21
doc/docs.txt
|
|
@ -1,28 +1,25 @@
|
||||||
"Incorrect documentation is often worse than no documentation."
|
Incorrect documentation is often worse than no documentation.
|
||||||
-- Bertrand Meyer
|
-- 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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)*]
|
||||||
|
|
|
||||||
50
doc/lib.txt
50
doc/lib.txt
|
|
@ -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
|
||||||
|
|
@ -86,6 +109,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>`_
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
12308
doc/theindex.txt
12308
doc/theindex.txt
File diff suppressed because it is too large
Load diff
110
doc/tut1.txt
110
doc/tut1.txt
|
|
@ -46,12 +46,14 @@ The most used commands and switches have abbreviations, so you can also use::
|
||||||
|
|
||||||
Though it should be pretty obvious what the program does, I will explain the
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -426,7 +431,11 @@ The ``when`` statement is useful for writing platform specific code, similar to
|
||||||
the ``#ifdef`` construct in the C programming language.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
160
doc/tut2.txt
160
doc/tut2.txt
|
|
@ -33,7 +33,8 @@ Object Oriented Programming
|
||||||
While Nimrod's support for object oriented programming (OOP) is minimalistic,
|
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
|
||||||
|
|
@ -237,15 +238,15 @@ The ``[]`` array access operator can be overloaded to provide
|
||||||
type
|
type
|
||||||
TVector* = object
|
TVector* = object
|
||||||
x, y, z: float
|
x, y, z: float
|
||||||
|
|
||||||
proc `[]=`* (v: var TVector, i: int, value: float) =
|
proc `[]=`* (v: var TVector, i: int, value: float) =
|
||||||
# setter
|
# setter
|
||||||
case i
|
case i
|
||||||
of 0: v.x = value
|
of 0: v.x = value
|
||||||
of 1: v.y = value
|
of 1: v.y = value
|
||||||
of 2: v.z = value
|
of 2: v.z = value
|
||||||
else: assert(false)
|
else: assert(false)
|
||||||
|
|
||||||
proc `[]`* (v: TVector, i: int): float =
|
proc `[]`* (v: TVector, i: int): float =
|
||||||
# getter
|
# getter
|
||||||
case i
|
case i
|
||||||
|
|
@ -253,25 +254,29 @@ The ``[]`` array access operator can be overloaded to provide
|
||||||
of 1: result = v.y
|
of 1: result = v.y
|
||||||
of 2: result = v.z
|
of 2: result = v.z
|
||||||
else: assert(false)
|
else: assert(false)
|
||||||
|
|
||||||
The example is silly, since a vector is better modelled by a tuple which
|
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
|
||||||
----------------
|
----------------
|
||||||
|
|
|
||||||
|
|
@ -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,7 +121,21 @@ 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)
|
||||||
|
|
@ -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
219
koch.py
|
|
@ -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"):
|
||||||
|
|
|
||||||
667
lib/alloc.nim
667
lib/alloc.nim
|
|
@ -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
|
|
||||||
|
when defined(linux):
|
||||||
MAP_SHARED = 1 # Share changes
|
const MAP_ANONYMOUS = 0x20 # don't use a file
|
||||||
MAP_PRIVATE = 2 # Changes are private
|
elif defined(macosx):
|
||||||
MAP_TYPE = 0xf # Mask for type of mapping
|
const MAP_ANONYMOUS = 0x1000
|
||||||
MAP_FIXED = 0x10 # Interpret addr exactly
|
else:
|
||||||
MAP_ANONYMOUS = 0x20 # don't use a file
|
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
|
|
||||||
|
SmallChunkSize = PageSize # * 4
|
||||||
|
|
||||||
MemAlignment = sizeof(pointer)*2 # minimal memory block that can be allocated
|
MemAlign = 8 # minimal memory block that can be allocated
|
||||||
BitsPerUnit = sizeof(int)*8
|
|
||||||
# a "unit" is a word, i.e. 4 bytes
|
|
||||||
# on a 32 bit system; I do not use the term "word" because under 32-bit
|
|
||||||
# Windows it is sometimes only 16 bits
|
|
||||||
|
|
||||||
BitsPerPage = PageSize div MemAlignment
|
BitsPerPage = PageSize div MemAlign
|
||||||
UnitsPerPage = BitsPerPage div BitsPerUnit
|
UnitsPerPage = BitsPerPage div (sizeof(int)*8)
|
||||||
# how many units do we need to describe a page:
|
# 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...
|
|
||||||
|
|
||||||
type
|
|
||||||
PChunkDesc = ptr TChunkDesc
|
|
||||||
TChunkDesc {.final, pure.} = object
|
|
||||||
key: TAddress # address at bit 0
|
|
||||||
next: PChunkDesc
|
|
||||||
bits: array[0..127, int] # a bit vector
|
|
||||||
|
|
||||||
PChunkDescArray = ptr array[0..1000_000, PChunkDesc]
|
|
||||||
TChunkSet {.final, pure.} = object
|
|
||||||
counter, max: int
|
|
||||||
head: PChunkDesc
|
|
||||||
data: PChunkDescArray
|
|
||||||
|
|
||||||
when sizeof(int) == 4:
|
# Compile time options:
|
||||||
type THalfWord = int16
|
coalescRight = true
|
||||||
else:
|
coalescLeft = true
|
||||||
type THalfWord = int32
|
|
||||||
|
const
|
||||||
|
TrunkShift = 9
|
||||||
|
BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and divisible by 64
|
||||||
|
TrunkMask = BitsPerTrunk - 1
|
||||||
|
IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8)
|
||||||
|
IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width
|
||||||
|
IntMask = 1 shl IntShift - 1
|
||||||
|
|
||||||
|
type
|
||||||
|
PTrunk = ptr TTrunk
|
||||||
|
TTrunk {.final.} = object
|
||||||
|
next: PTrunk # all nodes are connected with this pointer
|
||||||
|
key: int # start address at bit 0
|
||||||
|
bits: array[0..IntsPerTrunk-1, int] # a bit vector
|
||||||
|
|
||||||
|
TTrunkBuckets = array[0..1023, PTrunk]
|
||||||
|
TIntSet {.final.} = object
|
||||||
|
data: TTrunkBuckets
|
||||||
|
|
||||||
type
|
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)
|
||||||
|
|
||||||
|
|
||||||
const
|
|
||||||
InitChunkSetSize = 1024 # must be a power of two!
|
|
||||||
|
|
||||||
proc ChunkSetInit(s: var TChunkSet) =
|
|
||||||
s.data = cast[PChunkDescArray](llAlloc(InitChunkSetSize * sizeof(PChunkDesc)))
|
|
||||||
s.max = InitChunkSetSize-1
|
|
||||||
s.counter = 0
|
|
||||||
s.head = nil
|
|
||||||
|
|
||||||
proc ChunkSetGet(t: TChunkSet, key: TAddress): PChunkDesc =
|
|
||||||
var h = cast[int](key) and t.max
|
|
||||||
while t.data[h] != nil:
|
|
||||||
if t.data[h].key == key: return t.data[h]
|
|
||||||
h = nextTry(h, t.max)
|
|
||||||
return nil
|
|
||||||
|
|
||||||
proc ChunkSetRawInsert(t: TChunkSet, data: PChunkDescArray,
|
|
||||||
desc: PChunkDesc) =
|
|
||||||
var h = cast[int](desc.key) and t.max
|
|
||||||
while data[h] != nil:
|
|
||||||
assert(data[h] != desc)
|
|
||||||
h = nextTry(h, t.max)
|
|
||||||
assert(data[h] == nil)
|
|
||||||
data[h] = desc
|
|
||||||
|
|
||||||
proc ChunkSetEnlarge(t: var TChunkSet) =
|
|
||||||
var oldMax = t.max
|
|
||||||
t.max = ((t.max+1)*2)-1
|
|
||||||
var n = cast[PChunkDescArray](llAlloc((t.max + 1) * sizeof(PChunkDescArray)))
|
|
||||||
for i in 0 .. oldmax:
|
|
||||||
if t.data[i] != nil:
|
|
||||||
ChunkSetRawInsert(t, n, t.data[i])
|
|
||||||
tlsf_free(t.data)
|
|
||||||
t.data = n
|
|
||||||
|
|
||||||
proc ChunkSetPut(t: var TChunkSet, key: TAddress): PChunkDesc =
|
|
||||||
var h = cast[int](key) and t.max
|
|
||||||
while true:
|
|
||||||
var x = t.data[h]
|
|
||||||
if x == nil: break
|
|
||||||
if x.key == key: return x
|
|
||||||
h = nextTry(h, t.max)
|
|
||||||
|
|
||||||
if ((t.max+1)*2 < t.counter*3) or ((t.max+1)-t.counter < 4):
|
|
||||||
ChunkSetEnlarge(t)
|
|
||||||
inc(t.counter)
|
|
||||||
h = cast[int](key) and t.max
|
|
||||||
while t.data[h] != nil: h = nextTry(h, t.max)
|
|
||||||
assert(t.data[h] == nil)
|
|
||||||
# the new page descriptor goes into result
|
|
||||||
result = cast[PChunkDesc](llAlloc(sizeof(TChunkDesc)))
|
|
||||||
result.next = t.head
|
|
||||||
result.key = key
|
|
||||||
t.head = result
|
|
||||||
t.data[h] = result
|
|
||||||
|
|
||||||
# ---------- slightly higher level procs --------------------------------------
|
|
||||||
|
|
||||||
proc in_Operator(s: TChunkSet, cell: PChunk): bool =
|
|
||||||
var u = cast[TAddress](cell)
|
|
||||||
var t = ChunkSetGet(s, u shr PageShift)
|
|
||||||
if t != nil:
|
|
||||||
u = (u %% PageSize) /% MemAlignment
|
|
||||||
result = (t.bits[u /% BitsPerUnit] and (1 shl (u %% BitsPerUnit))) != 0
|
|
||||||
else:
|
|
||||||
result = false
|
|
||||||
|
|
||||||
proc incl(s: var TCellSet, cell: PCell) =
|
|
||||||
var u = cast[TAddress](cell)
|
|
||||||
var t = ChunkSetPut(s, u shr PageShift)
|
|
||||||
u = (u %% PageSize) /% MemAlignment
|
|
||||||
t.bits[u /% BitsPerUnit] = t.bits[u /% BitsPerUnit] or
|
|
||||||
(1 shl (u %% BitsPerUnit))
|
|
||||||
|
|
||||||
proc excl(s: var TCellSet, cell: PCell) =
|
|
||||||
var u = cast[TAddress](cell)
|
|
||||||
var t = ChunkSetGet(s, u shr PageShift)
|
|
||||||
if t != nil:
|
|
||||||
u = (u %% PageSize) /% MemAlignment
|
|
||||||
t.bits[u /% BitsPerUnit] = (t.bits[u /% BitsPerUnit] and
|
|
||||||
not (1 shl (u %% BitsPerUnit)))
|
|
||||||
|
|
||||||
iterator elements(t: TChunkSet): PChunk {.inline.} =
|
|
||||||
# while traversing it is forbidden to add pointers to the tree!
|
|
||||||
var r = t.head
|
|
||||||
while r != nil:
|
|
||||||
var i = 0
|
|
||||||
while i <= high(r.bits):
|
|
||||||
var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
|
|
||||||
# modifying operations are not allowed during traversation
|
|
||||||
var j = 0
|
|
||||||
while w != 0: # test all remaining bits for zero
|
|
||||||
if (w and 1) != 0: # the bit is set!
|
|
||||||
yield cast[PCell]((r.key shl PageShift) or # +%
|
|
||||||
(i*%BitsPerUnit+%j) *% MemAlignment)
|
|
||||||
inc(j)
|
|
||||||
w = w shr 1
|
|
||||||
inc(i)
|
|
||||||
r = r.next
|
|
||||||
|
|
||||||
|
|
||||||
# ------------- chunk management ----------------------------------------------
|
|
||||||
proc removeChunk(a: var TAllocator, c: PChunk) {.inline.} =
|
|
||||||
if c.prev != nil: c.prev.next = c.next
|
|
||||||
if c.next != nil: c.next.prev = c.prev
|
|
||||||
if a.freeChunks[c.size div PageSize] == c:
|
|
||||||
a.freeChunks[c.size div PageSize] = c.next
|
|
||||||
|
|
||||||
proc addChunk(a: var TAllocator, c: PChunk) {.inline.} =
|
proc IntSetGet(t: TIntSet, key: int): PTrunk =
|
||||||
var s = abs(c.size) div PageSize
|
var it = t.data[key and high(t.data)]
|
||||||
|
while it != nil:
|
||||||
|
if it.key == key: return it
|
||||||
|
it = it.next
|
||||||
|
result = nil
|
||||||
|
|
||||||
|
proc IntSetPut(t: var TIntSet, key: int): PTrunk =
|
||||||
|
result = IntSetGet(t, key)
|
||||||
|
if result == nil:
|
||||||
|
result = cast[PTrunk](llAlloc(allocator, sizeof(result^)))
|
||||||
|
result.next = t.data[key and high(t.data)]
|
||||||
|
t.data[key and high(t.data)] = result
|
||||||
|
result.key = key
|
||||||
|
|
||||||
|
proc Contains(s: TIntSet, key: int): bool =
|
||||||
|
var t = IntSetGet(s, key shr TrunkShift)
|
||||||
|
if t != nil:
|
||||||
|
var u = key and TrunkMask
|
||||||
|
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
|
||||||
|
else:
|
||||||
|
result = false
|
||||||
|
|
||||||
|
proc Incl(s: var TIntSet, key: int) =
|
||||||
|
var t = IntSetPut(s, key shr TrunkShift)
|
||||||
|
var u = key and TrunkMask
|
||||||
|
t.bits[u shr IntShift] = t.bits[u shr IntShift] or (1 shl (u and IntMask))
|
||||||
|
|
||||||
|
proc Excl(s: var TIntSet, key: int) =
|
||||||
|
var t = IntSetGet(s, key shr TrunkShift)
|
||||||
|
if t != nil:
|
||||||
|
var u = key and TrunkMask
|
||||||
|
t.bits[u shr IntShift] = t.bits[u shr IntShift] and not
|
||||||
|
(1 shl (u and IntMask))
|
||||||
|
|
||||||
|
proc ContainsOrIncl(s: var TIntSet, key: int): bool =
|
||||||
|
var t = IntSetGet(s, key shr TrunkShift)
|
||||||
|
if t != nil:
|
||||||
|
var u = key and TrunkMask
|
||||||
|
result = (t.bits[u shr IntShift] and (1 shl (u and IntMask))) != 0
|
||||||
|
if not result:
|
||||||
|
t.bits[u shr IntShift] = t.bits[u shr IntShift] or
|
||||||
|
(1 shl (u and IntMask))
|
||||||
|
else:
|
||||||
|
Incl(s, key)
|
||||||
|
result = false
|
||||||
|
|
||||||
|
# ------------- chunk management ----------------------------------------------
|
||||||
|
proc pageIndex(c: PChunk): int {.inline.} =
|
||||||
|
result = cast[TAddress](c) shr PageShift
|
||||||
|
|
||||||
|
proc pageIndex(p: pointer): int {.inline.} =
|
||||||
|
result = cast[TAddress](p) shr PageShift
|
||||||
|
|
||||||
|
proc pageAddr(p: pointer): PChunk {.inline.} =
|
||||||
|
result = cast[PChunk](cast[TAddress](p) and not PageMask)
|
||||||
|
assert(Contains(allocator.chunkStarts, pageIndex(result)))
|
||||||
|
|
||||||
|
var lastSize = PageSize
|
||||||
|
|
||||||
|
proc requestOsChunks(a: var TAllocator, size: int): PBigChunk =
|
||||||
|
incCurrMem(a, size)
|
||||||
|
result = cast[PBigChunk](osAllocPages(size))
|
||||||
|
assert((cast[TAddress](result) and PageMask) == 0)
|
||||||
|
result.next = nil
|
||||||
|
result.prev = nil
|
||||||
|
result.used = false
|
||||||
|
result.size = size
|
||||||
|
# update next.prevSize:
|
||||||
|
var nxt = cast[TAddress](result) +% size
|
||||||
|
assert((nxt and PageMask) == 0)
|
||||||
|
var next = cast[PChunk](nxt)
|
||||||
|
if pageIndex(next) in a.chunkStarts:
|
||||||
|
#echo("Next already allocated!")
|
||||||
|
next.prevSize = size
|
||||||
|
# set result.prevSize:
|
||||||
|
var prv = cast[TAddress](result) -% lastSize
|
||||||
|
assert((nxt and PageMask) == 0)
|
||||||
|
var prev = cast[PChunk](prv)
|
||||||
|
if pageIndex(prev) in a.chunkStarts and prev.size == lastSize:
|
||||||
|
#echo("Prev already allocated!")
|
||||||
|
result.prevSize = lastSize
|
||||||
|
else:
|
||||||
|
result.prevSize = 0 # unknown
|
||||||
|
lastSize = size # for next request
|
||||||
|
|
||||||
|
proc freeOsChunks(a: var TAllocator, p: pointer, size: int) =
|
||||||
|
# update next.prevSize:
|
||||||
|
var c = cast[PChunk](p)
|
||||||
|
var nxt = cast[TAddress](p) +% c.size
|
||||||
|
assert((nxt and PageMask) == 0)
|
||||||
|
var next = cast[PChunk](nxt)
|
||||||
|
if pageIndex(next) in a.chunkStarts:
|
||||||
|
next.prevSize = 0 # XXX used
|
||||||
|
excl(a.chunkStarts, pageIndex(p))
|
||||||
|
osDeallocPages(p, size)
|
||||||
|
decCurrMem(a, size)
|
||||||
|
|
||||||
|
proc isAccessible(p: pointer): bool {.inline.} =
|
||||||
|
result = Contains(allocator.chunkStarts, pageIndex(p))
|
||||||
|
|
||||||
|
proc ListAdd[T](head: var T, c: T) {.inline.} =
|
||||||
|
assert c.prev == nil
|
||||||
|
assert c.next == nil
|
||||||
|
c.next = head
|
||||||
|
if head != nil:
|
||||||
|
assert head.prev == nil
|
||||||
|
head.prev = c
|
||||||
|
head = c
|
||||||
|
|
||||||
|
proc ListRemove[T](head: var T, c: T) {.inline.} =
|
||||||
|
if c == head:
|
||||||
|
head = c.next
|
||||||
|
assert c.prev == nil
|
||||||
|
if head != nil: head.prev = nil
|
||||||
|
else:
|
||||||
|
assert c.prev != nil
|
||||||
|
c.prev.next = c.next
|
||||||
|
if c.next != nil: c.next.prev = c.prev
|
||||||
|
c.next = nil
|
||||||
c.prev = nil
|
c.prev = nil
|
||||||
c.next = a.freeChunks[s]
|
|
||||||
a.freeChunks[s] = c
|
proc isSmallChunk(c: PChunk): bool {.inline.} =
|
||||||
|
return c.size <= SmallChunkSize-smallChunkOverhead()
|
||||||
|
#return c.size < SmallChunkSize
|
||||||
|
|
||||||
|
proc chunkUnused(c: PChunk): bool {.inline.} =
|
||||||
|
result = not c.used
|
||||||
|
|
||||||
|
proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
|
||||||
|
var c = c
|
||||||
|
assert(c.size >= PageSize)
|
||||||
|
when coalescRight:
|
||||||
|
var ri = cast[PChunk](cast[TAddress](c) +% c.size)
|
||||||
|
assert((cast[TAddress](ri) and PageMask) == 0)
|
||||||
|
if isAccessible(ri) and chunkUnused(ri):
|
||||||
|
if not isSmallChunk(ri):
|
||||||
|
ListRemove(a.freeChunksList, cast[PBigChunk](ri))
|
||||||
|
inc(c.size, ri.size)
|
||||||
|
excl(a.chunkStarts, pageIndex(ri))
|
||||||
|
when coalescLeft:
|
||||||
|
if c.prevSize != 0:
|
||||||
|
var le = cast[PChunk](cast[TAddress](c) -% c.prevSize)
|
||||||
|
assert((cast[TAddress](le) and PageMask) == 0)
|
||||||
|
if isAccessible(le) and chunkUnused(le):
|
||||||
|
if not isSmallChunk(le):
|
||||||
|
ListRemove(a.freeChunksList, cast[PBigChunk](le))
|
||||||
|
inc(le.size, c.size)
|
||||||
|
excl(a.chunkStarts, pageIndex(c))
|
||||||
|
c = cast[PBigChunk](le)
|
||||||
|
|
||||||
proc freeChunk(a: var TAllocator, c: PChunk) =
|
if c.size < ChunkOsReturn:
|
||||||
assert(c.size > 0)
|
ListAdd(a.freeChunksList, c)
|
||||||
if c.size < PageSize: c.size = PageSize
|
c.used = false
|
||||||
var le = cast[PChunk](cast[TAddress](p) and not PageMask -% PageSize)
|
else:
|
||||||
var ri = cast[PChunk](cast[TAddress](p) and not PageMask +%
|
freeOsChunks(a, c, c.size)
|
||||||
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 splitChunk(a: var TAllocator, c: PBigChunk, size: int) =
|
||||||
var rest = cast[PChunk](cast[TAddress](p) + size)
|
var rest = cast[PBigChunk](cast[TAddress](c) +% size)
|
||||||
rest.size = size - c.size # results in negative number, because rest is free
|
rest.size = c.size - size
|
||||||
addChunk(a, rest)
|
rest.used = false
|
||||||
# mark pages as accessible:
|
rest.next = nil # XXX
|
||||||
ChunkTablePut(a, rest, bitAccessible)
|
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))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -1,486 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<!-- This file is generated by Nimrod. -->
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<title>Module cgi</title>
|
|
||||||
<style type="text/css">
|
|
||||||
|
|
||||||
span.DecNumber {color: blue}
|
|
||||||
span.BinNumber {color: blue}
|
|
||||||
span.HexNumber {color: blue}
|
|
||||||
span.OctNumber {color: blue}
|
|
||||||
span.FloatNumber {color: blue}
|
|
||||||
span.Identifier {color: black}
|
|
||||||
span.Keyword {font-weight: bold}
|
|
||||||
span.StringLit {color: blue}
|
|
||||||
span.LongStringLit {color: blue}
|
|
||||||
span.CharLit {color: blue}
|
|
||||||
span.EscapeSequence {color: black}
|
|
||||||
span.Operator {color: black}
|
|
||||||
span.Punctation {color: black}
|
|
||||||
span.Comment, span.LongComment {font-style:italic; color: green}
|
|
||||||
span.RegularExpression {color: pink}
|
|
||||||
span.TagStart {color: yellow}
|
|
||||||
span.TagEnd {color: yellow}
|
|
||||||
span.Key {color: blue}
|
|
||||||
span.Value {color: black}
|
|
||||||
span.RawData {color: blue}
|
|
||||||
span.Assembler {color: blue}
|
|
||||||
span.Preprocessor {color: yellow}
|
|
||||||
span.Directive {color: yellow}
|
|
||||||
span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference,
|
|
||||||
span.Other {color: black}
|
|
||||||
|
|
||||||
div.navigation {
|
|
||||||
float: left;
|
|
||||||
width: 27%; //25em;
|
|
||||||
margin: 0; padding: 0; /*
|
|
||||||
border: 1px dashed gold; */
|
|
||||||
outline: 3px outset #7F7F7F; //#99ff99; //gold;
|
|
||||||
background-color: #7F7F7F;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.navigation ul {list-style-type: none;}
|
|
||||||
div.navigation ul li a, div.navigation ul li a:visited {
|
|
||||||
font-weight: bold;
|
|
||||||
color: #FFFFFF; // #CC0000;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
div.navigation ul li a:hover {
|
|
||||||
font-weight: bold;
|
|
||||||
text-decoration: none;
|
|
||||||
/*outline: 2px outset #7F7F7F;*/
|
|
||||||
color: gold;
|
|
||||||
/* background-color: #FFFFFF; // #1A1A1A; // #779977;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
div.content {
|
|
||||||
margin-left: 27%; // 25em;
|
|
||||||
padding: 0 1em;
|
|
||||||
/*border: 1px dashed #1A1A1A;*/
|
|
||||||
min-width: 16em;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl.item dd, dl.item dd p {
|
|
||||||
margin-top:3px;
|
|
||||||
}
|
|
||||||
dl.item dd pre {
|
|
||||||
margin-left: 15pt;
|
|
||||||
border: 0px;
|
|
||||||
}
|
|
||||||
dl.item dt, dl.item dt pre {
|
|
||||||
margin: 20pt 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre, span.tok {
|
|
||||||
background-color:#F9F9F9;
|
|
||||||
border:1px dotted #2F6FAB;
|
|
||||||
color:black;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
:Author: David Goodger
|
|
||||||
:Contact: goodger@python.org
|
|
||||||
:Date: Date: 2006-05-21 22:44:42 +0200 (Sun, 21 May 2006)
|
|
||||||
:Revision: Revision: 4564
|
|
||||||
:Copyright: This stylesheet has been placed in the public domain.
|
|
||||||
|
|
||||||
Default cascading style sheet for the HTML output of Docutils.
|
|
||||||
|
|
||||||
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
|
||||||
customize this style sheet.
|
|
||||||
*/
|
|
||||||
/* used to remove borders from tables and images */
|
|
||||||
.borderless, table.borderless td, table.borderless th { border: 0 }
|
|
||||||
|
|
||||||
table.borderless td, table.borderless th {
|
|
||||||
/* Override padding for "table.docutils td" with "! important".
|
|
||||||
The right padding separates the table cells. */
|
|
||||||
padding: 0 0.5em 0 0 ! important }
|
|
||||||
|
|
||||||
.first { margin-top: 0 ! important }
|
|
||||||
.last, .with-subtitle { margin-bottom: 0 ! important }
|
|
||||||
.hidden { display: none }
|
|
||||||
a.toc-backref { text-decoration: none ; color: black }
|
|
||||||
blockquote.epigraph { margin: 2em 5em ; }
|
|
||||||
dl.docutils dd { margin-bottom: 0.5em }
|
|
||||||
div.abstract { margin: 2em 5em }
|
|
||||||
div.abstract p.topic-title { font-weight: bold ; text-align: center }
|
|
||||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
|
||||||
div.hint, div.important, div.note, div.tip, div.warning {
|
|
||||||
margin: 2em ; border: medium outset ; padding: 1em }
|
|
||||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
|
||||||
div.important p.admonition-title, div.note p.admonition-title,
|
|
||||||
div.tip p.admonition-title { font-weight: bold ; font-family: sans-serif }
|
|
||||||
|
|
||||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
|
||||||
div.danger p.admonition-title, div.error p.admonition-title,
|
|
||||||
div.warning p.admonition-title { color: red ; font-weight: bold ;
|
|
||||||
font-family: sans-serif }
|
|
||||||
|
|
||||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
|
||||||
compound paragraphs.
|
|
||||||
div.compound .compound-first, div.compound .compound-middle {
|
|
||||||
margin-bottom: 0.5em }
|
|
||||||
|
|
||||||
div.compound .compound-last, div.compound .compound-middle {
|
|
||||||
margin-top: 0.5em }
|
|
||||||
*/
|
|
||||||
|
|
||||||
div.dedication { margin: 2em 5em ; text-align: center ; font-style: italic }
|
|
||||||
div.dedication p.topic-title { font-weight: bold ; font-style: normal }
|
|
||||||
div.figure { margin-left: 2em ; margin-right: 2em }
|
|
||||||
div.footer, div.header { clear: both; font-size: smaller }
|
|
||||||
div.line-block { display: block ; margin-top: 1em ; margin-bottom: 1em }
|
|
||||||
div.line-block div.line-block { margin-top: 0 ; margin-bottom: 0 ;
|
|
||||||
margin-left: 1.5em }
|
|
||||||
div.sidebar { margin-left: 1em ; border: medium outset ;
|
|
||||||
padding: 1em ; background-color: #ffffee ; width: 40% ; float: right ;
|
|
||||||
clear: right }
|
|
||||||
|
|
||||||
div.sidebar p.rubric { font-family: sans-serif ; font-size: medium }
|
|
||||||
div.system-messages { margin: 5em }
|
|
||||||
div.system-messages h1 { color: red }
|
|
||||||
div.system-message { border: medium outset ; padding: 1em }
|
|
||||||
div.system-message p.system-message-title { color: red ; font-weight: bold }
|
|
||||||
div.topic { margin: 2em;}
|
|
||||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
|
||||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
|
||||||
margin-top: 0.4em }
|
|
||||||
h1.title { text-align: center }
|
|
||||||
h2.subtitle { text-align: center }
|
|
||||||
hr.docutils { width: 75% }
|
|
||||||
img.align-left { clear: left }
|
|
||||||
img.align-right { clear: right }
|
|
||||||
ol.simple, ul.simple { margin-bottom: 1em }
|
|
||||||
ol.arabic { list-style: decimal }
|
|
||||||
ol.loweralpha { list-style: lower-alpha }
|
|
||||||
ol.upperalpha { list-style: upper-alpha }
|
|
||||||
ol.lowerroman { list-style: lower-roman }
|
|
||||||
ol.upperroman { list-style: upper-roman }
|
|
||||||
p.attribution { text-align: right ; margin-left: 50% }
|
|
||||||
p.caption { font-style: italic }
|
|
||||||
p.credits { font-style: italic ; font-size: smaller }
|
|
||||||
p.label { white-space: nowrap }
|
|
||||||
p.rubric { font-weight:bold;font-size:larger;color:maroon;text-align:center}
|
|
||||||
p.sidebar-title {font-family: sans-serif ;font-weight: bold ;font-size: larger }
|
|
||||||
p.sidebar-subtitle {font-family: sans-serif ; font-weight: bold }
|
|
||||||
p.topic-title { font-weight: bold }
|
|
||||||
pre.address { margin-bottom: 0;margin-top:0;font-family:serif;font-size:100% }
|
|
||||||
pre.literal-block, pre.doctest-block {margin-left: 2em ;margin-right: 2em }
|
|
||||||
span.classifier {font-family: sans-serif;font-style: oblique }
|
|
||||||
span.classifier-delimiter {font-family: sans-serif;font-weight: bold }
|
|
||||||
span.interpreted {font-family: sans-serif }
|
|
||||||
span.option {white-space: nowrap }
|
|
||||||
span.pre {white-space: pre }
|
|
||||||
span.problematic {color: red }
|
|
||||||
span.section-subtitle {
|
|
||||||
/* font-size relative to parent (h1..h6 element) */
|
|
||||||
font-size: 80% }
|
|
||||||
|
|
||||||
table.citation { border-left: solid 1px gray; margin-left: 1px }
|
|
||||||
table.docinfo {margin: 2em 4em }
|
|
||||||
table.docutils {margin-top: 0.5em;margin-bottom: 0.5em }
|
|
||||||
table.footnote {border-left: solid 1px black;margin-left: 1px }
|
|
||||||
|
|
||||||
table.docutils td, table.docutils th,
|
|
||||||
table.docinfo td, table.docinfo th {padding-left: 0.5em;padding-right: 0.5em;
|
|
||||||
vertical-align: top}
|
|
||||||
|
|
||||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
|
||||||
font-weight: bold;text-align: left;white-space: nowrap;padding-left: 0 }
|
|
||||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
|
||||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {font-size: 100% }
|
|
||||||
ul.auto-toc { list-style-type: none }
|
|
||||||
/*a.reference { color: #E00000; font-weight:bold;}
|
|
||||||
a.reference:hover {color: #E00000;background-color: #ffff00;display: margin;
|
|
||||||
font-weight:bold;}*/
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<script type="text/javascript">
|
|
||||||
//<![CDATA[
|
|
||||||
function toggleElem(id) {
|
|
||||||
var e = document.getElementById(id);
|
|
||||||
e.style.display = e.style.display == 'none' ? 'block' : 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
var gOpen = 'none'
|
|
||||||
function toggleAll() {
|
|
||||||
gOpen = gOpen == 'none' ? 'block' : 'none';
|
|
||||||
var i = 1
|
|
||||||
while (1) {
|
|
||||||
var e = document.getElementById("m"+i)
|
|
||||||
if (!e) break;
|
|
||||||
e.style.display = gOpen
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
document.getElementById('toggleButton').value =
|
|
||||||
gOpen == 'none' ? 'Show Details' : 'Hide Details';
|
|
||||||
}
|
|
||||||
//]]>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="document" id="documentId">
|
|
||||||
<h1 class="title">Module cgi</h1>
|
|
||||||
<div class="navigation">
|
|
||||||
<p class="topic-title first">Navigation</p>
|
|
||||||
<ul class="simple">
|
|
||||||
<li>
|
|
||||||
<a class="reference" href="#6" id="56">Types</a>
|
|
||||||
<ul class="simple">
|
|
||||||
<li><a class="reference" href="#103">ECgi</a></li>
|
|
||||||
<li><a class="reference" href="#104">TRequestMethod</a></li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class="reference" href="#9" id="59">Procs</a>
|
|
||||||
<ul class="simple">
|
|
||||||
<li><a class="reference" href="#101">URLencode</a></li>
|
|
||||||
<li><a class="reference" href="#102">URLdecode</a></li>
|
|
||||||
<li><a class="reference" href="#105">readData</a></li>
|
|
||||||
<li><a class="reference" href="#106">validateData</a></li>
|
|
||||||
<li><a class="reference" href="#107">getContentLength</a></li>
|
|
||||||
<li><a class="reference" href="#108">getContentType</a></li>
|
|
||||||
<li><a class="reference" href="#109">getDocumentRoot</a></li>
|
|
||||||
<li><a class="reference" href="#110">getGatewayInterface</a></li>
|
|
||||||
<li><a class="reference" href="#111">getHttpAccept</a></li>
|
|
||||||
<li><a class="reference" href="#112">getHttpAcceptCharset</a></li>
|
|
||||||
<li><a class="reference" href="#113">getHttpAccept Encoding</a></li>
|
|
||||||
<li><a class="reference" href="#114">getHttpAccept Language</a></li>
|
|
||||||
<li><a class="reference" href="#115">getHttpConnection</a></li>
|
|
||||||
<li><a class="reference" href="#116">getHttpCookie</a></li>
|
|
||||||
<li><a class="reference" href="#117">getHttpHost</a></li>
|
|
||||||
<li><a class="reference" href="#118">getHttpReferer</a></li>
|
|
||||||
<li><a class="reference" href="#119">getHttpUserAgent</a></li>
|
|
||||||
<li><a class="reference" href="#120">getPathInfo</a></li>
|
|
||||||
<li><a class="reference" href="#121">getPathTranslated</a></li>
|
|
||||||
<li><a class="reference" href="#122">getQueryString</a></li>
|
|
||||||
<li><a class="reference" href="#123">getRemoteAddr</a></li>
|
|
||||||
<li><a class="reference" href="#124">getRemoteHost</a></li>
|
|
||||||
<li><a class="reference" href="#125">getRemoteIdent</a></li>
|
|
||||||
<li><a class="reference" href="#126">getRemotePort</a></li>
|
|
||||||
<li><a class="reference" href="#127">getRemoteUser</a></li>
|
|
||||||
<li><a class="reference" href="#128">getRequestMethod</a></li>
|
|
||||||
<li><a class="reference" href="#129">getRequestURI</a></li>
|
|
||||||
<li><a class="reference" href="#130">getScriptFilename</a></li>
|
|
||||||
<li><a class="reference" href="#131">getScriptName</a></li>
|
|
||||||
<li><a class="reference" href="#132">getServerAddr</a></li>
|
|
||||||
<li><a class="reference" href="#133">getServerAdmin</a></li>
|
|
||||||
<li><a class="reference" href="#134">getServerName</a></li>
|
|
||||||
<li><a class="reference" href="#135">getServerPort</a></li>
|
|
||||||
<li><a class="reference" href="#136">getServerProtocol</a></li>
|
|
||||||
<li><a class="reference" href="#137">getServerSignature</a></li>
|
|
||||||
<li><a class="reference" href="#138">getServerSoftware</a></li>
|
|
||||||
<li><a class="reference" href="#139">setTestData</a></li>
|
|
||||||
<li><a class="reference" href="#140">writeContentType</a></li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
This module implements helper procs for CGI applictions. Example:<pre>
|
|
||||||
<span class="Keyword">import</span> <span class="Identifier">strtabs</span><span class="Punctation">,</span> <span class="Identifier">cgi</span>
|
|
||||||
|
|
||||||
<span class="Comment"># Fill the values when debugging:</span>
|
|
||||||
<span class="Keyword">when</span> <span class="Identifier">debug</span><span class="Punctation">:</span>
|
|
||||||
<span class="Identifier">setTestData</span><span class="Punctation">(</span><span class="StringLit">"name"</span><span class="Punctation">,</span> <span class="StringLit">"Klaus"</span><span class="Punctation">,</span> <span class="StringLit">"password"</span><span class="Punctation">,</span> <span class="StringLit">"123456"</span><span class="Punctation">)</span>
|
|
||||||
<span class="Comment"># read the data into `myData`</span>
|
|
||||||
<span class="Keyword">var</span> <span class="Identifier">myData</span> <span class="Operator">=</span> <span class="Identifier">readData</span><span class="Punctation">(</span><span class="Punctation">)</span>
|
|
||||||
<span class="Comment"># check that the data's variable names are "name" or "passwort"</span>
|
|
||||||
<span class="Identifier">validateData</span><span class="Punctation">(</span><span class="Identifier">myData</span><span class="Punctation">,</span> <span class="StringLit">"name"</span><span class="Punctation">,</span> <span class="StringLit">"password"</span><span class="Punctation">)</span>
|
|
||||||
<span class="Comment"># start generating content:</span>
|
|
||||||
<span class="Identifier">writeContentType</span><span class="Punctation">(</span><span class="Punctation">)</span>
|
|
||||||
<span class="Comment"># generate content:</span>
|
|
||||||
<span class="Identifier">write</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">"<!DOCTYPE HTML PUBLIC </span><span class="EscapeSequence">\"</span>-//W3C//DTD HTML 4.01//EN<span class="EscapeSequence">\"</span><span class="EscapeSequence">>\n</span><span class="StringLit">"</span><span class="Punctation">)</span>
|
|
||||||
<span class="Identifier">write</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">"<html><head><title>Test</title></head><body></span><span class="EscapeSequence">\n</span><span class="StringLit">"</span><span class="Punctation">)</span>
|
|
||||||
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">"your name: "</span> <span class="Operator">&</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">"name"</span><span class="Punctation">]</span><span class="Punctation">)</span>
|
|
||||||
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">"your password: "</span> <span class="Operator">&</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">"password"</span><span class="Punctation">]</span><span class="Punctation">)</span>
|
|
||||||
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">"</body></html>"</span><span class="Punctation">)</span></pre>
|
|
||||||
<div class="section" id="6">
|
|
||||||
<h1><a class="toc-backref" href="#56">Types</a></h1>
|
|
||||||
<dl class="item">
|
|
||||||
<dt id="103"><pre><span class="Identifier">ECgi</span><span class="Operator">*</span> <span class="Other">=</span> <span class="Keyword">object</span> <span class="Keyword">of</span> <span class="Identifier">EIO</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
the exception that is raised, if a CGI error occurs
|
|
||||||
</dd>
|
|
||||||
<dt id="104"><pre><span class="Identifier">TRequestMethod</span><span class="Operator">*</span> <span class="Other">=</span> <span class="Keyword">enum</span>
|
|
||||||
<span class="Identifier">methodPost</span><span class="Other">,</span> <span class="Comment">## query uses the POST method</span>
|
|
||||||
<span class="Identifier">methodGet</span> <span class="Comment">## query uses the GET method</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
the used request method
|
|
||||||
</dd>
|
|
||||||
|
|
||||||
</dl></div>
|
|
||||||
<div class="section" id="9">
|
|
||||||
<h1><a class="toc-backref" href="#59">Procs</a></h1>
|
|
||||||
<dl class="item">
|
|
||||||
<dt id="101"><pre><span class="Keyword">proc</span> <span class="Identifier">URLencode</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">s</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
Encodes a value to be HTTP safe: This means that characters in the set <tt class="docutils literal"><span class="pre">{'A'..'Z', 'a'..'z', '0'..'9', '_'}</span></tt> are carried over to the result, a space is converted to <tt class="docutils literal"><span class="pre">'+'</span></tt> and every other character is encoded as <tt class="docutils literal"><span class="pre">'%xx'</span></tt> where <tt class="docutils literal"><span class="pre">xx</span></tt> denotes its hexadecimal value.
|
|
||||||
</dd>
|
|
||||||
<dt id="102"><pre><span class="Keyword">proc</span> <span class="Identifier">URLdecode</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">s</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
Decodes a value from its HTTP representation: This means that a <tt class="docutils literal"><span class="pre">'+'</span></tt> is converted to a space, <tt class="docutils literal"><span class="pre">'%xx'</span></tt> (where <tt class="docutils literal"><span class="pre">xx</span></tt> denotes a hexadecimal value) is converted to the character with ordinal number <tt class="docutils literal"><span class="pre">xx</span></tt>, and and every other character is carried over.
|
|
||||||
</dd>
|
|
||||||
<dt id="105"><pre><span class="Keyword">proc</span> <span class="Identifier">readData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">allowedMethods</span><span class="Other">:</span> <span class="Identifier">set</span><span class="Other">[</span><span class="Identifier">TRequestMethod</span><span class="Other">]</span> <span class="Other">=</span> <span class="Other">{</span><span class="Identifier">methodPost</span><span class="Other">,</span> <span class="Identifier">methodGet</span><span class="Other">}</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">PStringTable</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
Read CGI data. If the client does not use a method listed in the <cite>allowedMethods</cite> set, an <cite>ECgi</cite> exception is raised.
|
|
||||||
</dd>
|
|
||||||
<dt id="106"><pre><span class="Keyword">proc</span> <span class="Identifier">validateData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">data</span><span class="Other">:</span> <span class="Identifier">PStringTable</span><span class="Other">,</span> <span class="Identifier">validKeys</span><span class="Other">:</span> <span class="Identifier">openarray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
validates data; raises <cite>ECgi</cite> if this fails. This checks that each variable name of the CGI <cite>data</cite> occurs in the <cite>validKeys</cite> array.
|
|
||||||
</dd>
|
|
||||||
<dt id="107"><pre><span class="Keyword">proc</span> <span class="Identifier">getContentLength</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">CONTENT_LENGTH</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="108"><pre><span class="Keyword">proc</span> <span class="Identifier">getContentType</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">CONTENT_TYPE</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="109"><pre><span class="Keyword">proc</span> <span class="Identifier">getDocumentRoot</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">DOCUMENT_ROOT</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="110"><pre><span class="Keyword">proc</span> <span class="Identifier">getGatewayInterface</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">GATEWAY_INTERFACE</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="111"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAccept</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="112"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptCharset</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_CHARSET</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="113"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptEncoding</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_ENCODING</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="114"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptLanguage</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_LANGUAGE</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="115"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpConnection</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_CONNECTION</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="116"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpCookie</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_COOKIE</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="117"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpHost</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_HOST</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="118"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpReferer</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_REFERER</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="119"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpUserAgent</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_USER_AGENT</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="120"><pre><span class="Keyword">proc</span> <span class="Identifier">getPathInfo</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">PATH_INFO</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="121"><pre><span class="Keyword">proc</span> <span class="Identifier">getPathTranslated</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">PATH_TRANSLATED</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="122"><pre><span class="Keyword">proc</span> <span class="Identifier">getQueryString</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">QUERY_STRING</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="123"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteAddr</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_ADDR</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="124"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteHost</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_HOST</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="125"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteIdent</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_IDENT</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="126"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemotePort</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_PORT</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="127"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteUser</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_USER</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="128"><pre><span class="Keyword">proc</span> <span class="Identifier">getRequestMethod</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">REQUEST_METHOD</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="129"><pre><span class="Keyword">proc</span> <span class="Identifier">getRequestURI</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">REQUEST_URI</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="130"><pre><span class="Keyword">proc</span> <span class="Identifier">getScriptFilename</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">SCRIPT_FILENAME</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="131"><pre><span class="Keyword">proc</span> <span class="Identifier">getScriptName</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">SCRIPT_NAME</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="132"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerAddr</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_ADDR</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="133"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerAdmin</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_ADMIN</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="134"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerName</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_NAME</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="135"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerPort</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_PORT</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="136"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerProtocol</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_PROTOCOL</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="137"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerSignature</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_SIGNATURE</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="138"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerSoftware</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_SOFTWARE</span></tt> environment variable
|
|
||||||
</dd>
|
|
||||||
<dt id="139"><pre><span class="Keyword">proc</span> <span class="Identifier">setTestData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">keysvalues</span><span class="Other">:</span> <span class="Identifier">openarray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
fills the appropriate environment variables to test your CGI application. This can only simulate the 'GET' 'REQUEST_METHOD'. <cite>keysvalues</cite> should provide embedded (name, value)-pairs. Example:<pre><span class="Identifier">setTestData</span><span class="Punctation">(</span><span class="StringLit">"name"</span><span class="Punctation">,</span> <span class="StringLit">"Hanz"</span><span class="Punctation">,</span> <span class="StringLit">"password"</span><span class="Punctation">,</span> <span class="StringLit">"12345"</span><span class="Punctation">)</span></pre>
|
|
||||||
</dd>
|
|
||||||
<dt id="140"><pre><span class="Keyword">proc</span> <span class="Identifier">writeContentType</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span></pre></dt>
|
|
||||||
<dd>
|
|
||||||
call this before starting to send your HTML data to <cite>stdout</cite>. This is just a shorthand for: .. code-block:: Nimrod<blockquote>write(stdout, "Content-type: text/htmlnn")</blockquote>
|
|
||||||
|
|
||||||
</dd>
|
|
||||||
|
|
||||||
</dl></div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<small>Generated: 2008-12-08 20:29:49 UTC</small>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# 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, "&")
|
||||||
|
of '<': add(dest, "<")
|
||||||
|
of '>': add(dest, ">")
|
||||||
|
of '\"': add(dest, """)
|
||||||
|
else: add(dest, c)
|
||||||
|
|
||||||
|
proc XMLencode*(s: string): string =
|
||||||
|
## Encodes a value to be XML safe:
|
||||||
|
## * ``"`` is replaced by ``"``
|
||||||
|
## * ``<`` is replaced by ``<``
|
||||||
|
## * ``>`` is replaced by ``>``
|
||||||
|
## * ``&`` is replaced by ``&``
|
||||||
|
## * every other character is carried over.
|
||||||
|
result = ""
|
||||||
|
for i in 0..len(s)-1: addXmlChar(result, s[i])
|
||||||
|
|
||||||
type
|
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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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.}
|
||||||
|
|
|
||||||
|
|
@ -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.}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
286
lib/gc.nim
286
lib/gc.nim
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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".}
|
||||||
|
|
@ -133,43 +157,81 @@ proc `typ=`*(n: PNimrodNode, typ: PNimrodType) {.magic: "NSetType".}
|
||||||
proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
|
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
|
||||||
## the arguments ``args[0..]``.
|
## the arguments ``args[0..]``.
|
||||||
result = newNimNode(nnkCall)
|
result = newNimNode(nnkCall)
|
||||||
result.add(newIdentNode(theProc))
|
result.add(newIdentNode(theProc))
|
||||||
|
result.add(args)
|
||||||
|
|
||||||
|
proc newCall*(theProc: string,
|
||||||
|
args: openArray[PNimrodNode]): PNimrodNode {.compileTime.} =
|
||||||
|
## produces a new call node. `theProc` is the proc that is called with
|
||||||
|
## the arguments ``args[0..]``.
|
||||||
|
result = newNimNode(nnkCall)
|
||||||
|
result.add(newIdentNode(theProc))
|
||||||
result.add(args)
|
result.add(args)
|
||||||
|
|
||||||
|
|
|
||||||
33
lib/math.nim
33
lib/math.nim
|
|
@ -51,6 +51,20 @@ proc classify*(x: float): TFloatClass =
|
||||||
# XXX: fcSubnormal is not detected!
|
# 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
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
34
lib/os.nim
34
lib/os.nim
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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':
|
||||||
|
|
|
||||||
|
|
@ -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
14
lib/repr.nim
14
lib/repr.nim
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
312
lib/strutils.nim
312
lib/strutils.nim
|
|
@ -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.}
|
||||||
|
|
||||||
|
|
@ -33,13 +32,66 @@ type
|
||||||
const
|
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,25 +441,29 @@ 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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
||||||
|
proc reprAny(p: pointer, typ: PNimType): string {.compilerproc.}
|
||||||
|
|
||||||
when not defined(boehmgc) and not defined(nogc):
|
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
|
||||||
|
assert(n.kind == nkCase)
|
||||||
|
var d: int
|
||||||
|
var a = cast[TAddress](aa)
|
||||||
|
case n.typ.size
|
||||||
|
of 1: d = ze(cast[ptr int8](a +% n.offset)^)
|
||||||
|
of 2: d = ze(cast[ptr int16](a +% n.offset)^)
|
||||||
|
of 4: d = int(cast[ptr int32](a +% n.offset)^)
|
||||||
|
else: assert(false)
|
||||||
|
return d
|
||||||
|
|
||||||
|
proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode =
|
||||||
|
var discr = getDiscriminant(aa, n)
|
||||||
|
if discr <% n.len:
|
||||||
|
result = n.sons[discr]
|
||||||
|
if result == nil: result = n.sons[n.len]
|
||||||
|
# n.sons[n.len] contains the ``else`` part (but may be nil)
|
||||||
|
else:
|
||||||
|
result = n.sons[n.len]
|
||||||
|
|
||||||
|
when defined(boehmgc):
|
||||||
|
const
|
||||||
|
boehmLib = "/opt/lib/libgc.so"
|
||||||
|
|
||||||
|
proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.}
|
||||||
|
proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.}
|
||||||
|
proc boehmGCincremental {.
|
||||||
|
importc: "GC_enable_incremental", dynlib: boehmLib.}
|
||||||
|
proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.}
|
||||||
|
proc boehmAlloc(size: int): pointer {.
|
||||||
|
importc: "GC_malloc", dynlib: boehmLib.}
|
||||||
|
proc boehmAllocAtomic(size: int): pointer {.
|
||||||
|
importc: "GC_malloc_atomic", dynlib: boehmLib.}
|
||||||
|
proc boehmRealloc(p: pointer, size: int): pointer {.
|
||||||
|
importc: "GC_realloc", dynlib: boehmLib.}
|
||||||
|
proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.}
|
||||||
|
|
||||||
|
include cellsets
|
||||||
|
|
||||||
|
when defined(boehmGC):
|
||||||
|
proc initGC() = nil
|
||||||
|
|
||||||
|
#boehmGCincremental()
|
||||||
|
|
||||||
|
proc GC_disable() = boehmGC_disable()
|
||||||
|
proc GC_enable() = boehmGC_enable()
|
||||||
|
proc GC_fullCollect() = boehmGCfullCollect()
|
||||||
|
proc GC_setStrategy(strategy: TGC_Strategy) = nil
|
||||||
|
proc GC_enableMarkAndSweep() = nil
|
||||||
|
proc GC_disableMarkAndSweep() = nil
|
||||||
|
proc GC_getStatistics(): string = return ""
|
||||||
|
|
||||||
|
proc getOccupiedMem(): int = return -1
|
||||||
|
proc getFreeMem(): int = return -1
|
||||||
|
proc getTotalMem(): int = return -1
|
||||||
|
|
||||||
|
proc growObj(old: pointer, newsize: int): pointer {.inline.} =
|
||||||
|
result = boehmRealloc(old, newsize)
|
||||||
|
proc newObj(size: int): pointer {.compilerproc.} =
|
||||||
|
result = boehmAlloc(size)
|
||||||
|
proc newSeq(baseSize, len: int): pointer {.compilerproc.} =
|
||||||
|
# XXX: overflow checks!
|
||||||
|
result = newObj(len * baseSize + GenericSeqSize)
|
||||||
|
cast[PGenericSeq](result).len = len
|
||||||
|
cast[PGenericSeq](result).space = len
|
||||||
|
|
||||||
|
proc setStackBottom(theStackBottom: pointer) {.compilerproc.} = nil
|
||||||
|
proc nimGCref(p: pointer) {.compilerproc, inline.} = nil
|
||||||
|
proc nimGCunref(p: pointer) {.compilerproc, inline.} = nil
|
||||||
|
|
||||||
|
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
|
||||||
|
dest^ = src
|
||||||
|
proc asgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
|
||||||
|
dest^ = src
|
||||||
|
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerproc, inline.} =
|
||||||
|
dest^ = src
|
||||||
|
|
||||||
|
elif not defined(nogc):
|
||||||
include gc
|
include 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
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
# Memory handling for small objects
|
|
||||||
|
|
||||||
const
|
|
||||||
minRequestSize = 2 * sizeof(pointer) # minimal block is 16 bytes
|
|
||||||
pageSize = 1024 * sizeof(int)
|
|
||||||
pageBits = pageSize div minRequestSize
|
|
||||||
pageMask = pageSize-1
|
|
||||||
|
|
||||||
bitarraySize = pageBits div (sizeof(int)*8)
|
|
||||||
dataSize = pageSize - (bitarraySize+6) * sizeof(pointer)
|
|
||||||
|
|
||||||
type
|
|
||||||
TMinRequest {.final.} = object
|
|
||||||
next, prev: ptr TMinRequest # stores next free bit
|
|
||||||
|
|
||||||
TChunk {.pure.} = object # a chunk manages at least a page
|
|
||||||
size: int # lowest bit signals if it is a small chunk (0) or
|
|
||||||
# a big chunk (1)
|
|
||||||
typ: PNimType
|
|
||||||
next, prev: ptr TChunk
|
|
||||||
nextOfSameType: ptr TChunk
|
|
||||||
|
|
||||||
TSmallChunk = object of TChunk ## manages pageSize bytes for a type and a
|
|
||||||
## fixed size
|
|
||||||
free: int ## index of first free bit
|
|
||||||
bits: array[0..bitarraySize-1, int]
|
|
||||||
data: array[0..dataSize div minRequestSize - 1, TMinRequest]
|
|
||||||
|
|
||||||
PSmallChunk = ptr TSmallChunk
|
|
||||||
|
|
||||||
assert(sizeof(TSmallChunk) == pageSize)
|
|
||||||
|
|
||||||
proc getNewChunk(size: int, typ: PNimType): PSmallChunk =
|
|
||||||
result = cast[PSmallChunk](getPages(1))
|
|
||||||
result.size = PageSize
|
|
||||||
result.typ = typ
|
|
||||||
result.next = chunkHead
|
|
||||||
result.prev = nil
|
|
||||||
chunkHead.prev = result
|
|
||||||
chunkHead = result.next
|
|
||||||
result.nextOfSameType = cast[PSmallChunk](typ.chunk)
|
|
||||||
typ.chunk = result
|
|
||||||
result.free = addr(result.data[0])
|
|
||||||
result.data[0].next = addr(result.data[1])
|
|
||||||
result.data[0].prev = nil
|
|
||||||
result.data[high(result.data)].next = nil
|
|
||||||
result.data[high(result.data)].prev = addr(result.data[high(result.data)-1])
|
|
||||||
for i in 1..high(result.data)-1:
|
|
||||||
result.data[i].next = addr(result.data[i+1])
|
|
||||||
result.data[i].prev = addr(result.data[i-1])
|
|
||||||
|
|
||||||
proc newSmallObj(size: int, typ: PNimType): pointer =
|
|
||||||
var chunk = cast[PSmallChunk](typ.chunk)
|
|
||||||
if chunk == nil or chunk.free <= 0:
|
|
||||||
if chunk.free < 0: GC_collect()
|
|
||||||
chunk = getNewChunk(size, typ)
|
|
||||||
chunk.nextOfSameType = typ.chunk
|
|
||||||
typ.chunk = chunk
|
|
||||||
var idx = chunk.free
|
|
||||||
setBit(chunk.bits[idx /% bitarraySize], idx %% bitarraySize)
|
|
||||||
result = cast[pointer](cast[TAddress](addr(chunk.data)) +
|
|
||||||
minRequestSize * idx)
|
|
||||||
var res = cast[PMinRequest](result)
|
|
||||||
chunk.free = res.next
|
|
||||||
res.next
|
|
||||||
|
|
||||||
proc freeObj(obj: pointer) =
|
|
||||||
var chunk = cast[PChunk](cast[TAddress(obj) and not pageMask)
|
|
||||||
if size and 1 == 0: # small chunk
|
|
||||||
var idx = (cast[TAddress](obj) shr pageShift) div minRequestSize
|
|
||||||
resetBit(chunk.bits[idx /% bitarraySize], idx %% bitarraySize)
|
|
||||||
|
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
## This module provides support to handle the Unicode UTF-8 encoding.
|
## 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
|
||||||
|
|
|
||||||
27
nim/ast.pas
27
nim/ast.pas
|
|
@ -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,
|
||||||
|
|
@ -327,7 +327,8 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
@ -1324,7 +1334,8 @@ 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);
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -1012,4 +1063,4 @@ begin
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
*)
|
*)
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
482
nim/cgen.pas
482
nim/cgen.pas
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
129
nim/debugids.pas
129
nim/debugids.pas
|
|
@ -1,129 +0,0 @@
|
||||||
//
|
|
||||||
//
|
|
||||||
// The Nimrod Compiler
|
|
||||||
// (c) Copyright 2008 Andreas Rumpf
|
|
||||||
//
|
|
||||||
// See the file "copying.txt", included in this
|
|
||||||
// distribution, for details about the copyright.
|
|
||||||
//
|
|
||||||
unit debugids;
|
|
||||||
|
|
||||||
interface
|
|
||||||
|
|
||||||
{$include 'config.inc'}
|
|
||||||
|
|
||||||
uses
|
|
||||||
nsystem, nos, strutils, ast;
|
|
||||||
|
|
||||||
const
|
|
||||||
idfile = 'debugids.txt';
|
|
||||||
|
|
||||||
// This module implements debugging facilities for the ID mechanism.
|
|
||||||
procedure registerID(s: PSym);
|
|
||||||
|
|
||||||
procedure writeIDTable();
|
|
||||||
procedure loadIDTable();
|
|
||||||
|
|
||||||
implementation
|
|
||||||
|
|
||||||
type
|
|
||||||
TIdSymTuple = record{@tuple} // keep id from sym to better detect bugs
|
|
||||||
id: int;
|
|
||||||
s: PSym;
|
|
||||||
end;
|
|
||||||
TIdSymTupleSeq = array of TIdSymTuple;
|
|
||||||
TIdSymTable = record
|
|
||||||
counter: int;
|
|
||||||
data: TIdSymTupleSeq;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TableRawGet(const t: TTable; key: PObject): int;
|
|
||||||
var
|
|
||||||
h: THash;
|
|
||||||
begin
|
|
||||||
h := hashNode(key) and high(t.data); // start with real hash value
|
|
||||||
while t.data[h].key <> nil do begin
|
|
||||||
if (t.data[h].key = key) then begin
|
|
||||||
result := h; exit
|
|
||||||
end;
|
|
||||||
h := nextTry(h, high(t.data))
|
|
||||||
end;
|
|
||||||
result := -1
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TableSearch(const t: TTable; key, closure: PObject;
|
|
||||||
comparator: TCmpProc): PObject;
|
|
||||||
var
|
|
||||||
h: THash;
|
|
||||||
begin
|
|
||||||
h := hashNode(key) and high(t.data); // start with real hash value
|
|
||||||
while t.data[h].key <> nil do begin
|
|
||||||
if (t.data[h].key = key) then
|
|
||||||
if comparator(t.data[h].val, closure) then begin // BUGFIX 1
|
|
||||||
result := t.data[h].val; exit
|
|
||||||
end;
|
|
||||||
h := nextTry(h, high(t.data))
|
|
||||||
end;
|
|
||||||
result := nil
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TableGet(const t: TTable; key: PObject): PObject;
|
|
||||||
var
|
|
||||||
index: int;
|
|
||||||
begin
|
|
||||||
index := TableRawGet(t, key);
|
|
||||||
if index >= 0 then result := t.data[index].val
|
|
||||||
else result := nil
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TableRawInsert(var data: TPairSeq; key, val: PObject);
|
|
||||||
var
|
|
||||||
h: THash;
|
|
||||||
begin
|
|
||||||
h := HashNode(key) and high(data);
|
|
||||||
while data[h].key <> nil do begin
|
|
||||||
assert(data[h].key <> key);
|
|
||||||
h := nextTry(h, high(data))
|
|
||||||
end;
|
|
||||||
assert(data[h].key = nil);
|
|
||||||
data[h].key := key;
|
|
||||||
data[h].val := val;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TableEnlarge(var t: TTable);
|
|
||||||
var
|
|
||||||
n: TPairSeq;
|
|
||||||
i: int;
|
|
||||||
begin
|
|
||||||
{@ignore}
|
|
||||||
n := emptySeq;
|
|
||||||
setLength(n, length(t.data) * growthFactor);
|
|
||||||
fillChar(n[0], length(n)*sizeof(n[0]), 0);
|
|
||||||
{@emit
|
|
||||||
newSeq(n, length(t.data) * growthFactor); }
|
|
||||||
for i := 0 to high(t.data) do
|
|
||||||
if t.data[i].key <> nil then
|
|
||||||
TableRawInsert(n, t.data[i].key, t.data[i].val);
|
|
||||||
{@ignore}
|
|
||||||
t.data := n;
|
|
||||||
{@emit
|
|
||||||
swap(t.data, n);
|
|
||||||
}
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TablePut(var t: TTable; key, val: PObject);
|
|
||||||
var
|
|
||||||
index: int;
|
|
||||||
begin
|
|
||||||
index := TableRawGet(t, key);
|
|
||||||
if index >= 0 then
|
|
||||||
t.data[index].val := val
|
|
||||||
else begin
|
|
||||||
if mustRehash(length(t.data), t.counter) then TableEnlarge(t);
|
|
||||||
TableRawInsert(t.data, key, val);
|
|
||||||
inc(t.counter)
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
end.
|
|
||||||
|
|
@ -40,7 +40,7 @@ type
|
||||||
modDesc: PRope; // module description
|
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>';
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
66
nim/msgs.pas
66
nim/msgs.pas
|
|
@ -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]',
|
||||||
|
|
@ -645,8 +647,8 @@ const // this format is understood by many text editors: it is the same that
|
||||||
procedure MessageOut(const s: string);
|
procedure MessageOut(const s: string);
|
||||||
|
|
||||||
procedure rawMessage(const msg: TMsgKind; const arg: string = ''); overload;
|
procedure rawMessage(const msg: TMsgKind; const arg: string = ''); overload;
|
||||||
procedure rawMessage(const msg: TMsgKind; const args: array of string); overload;
|
procedure rawMessage(const msg: TMsgKind; const args: array of string); overload;
|
||||||
|
|
||||||
procedure liMessage(const info: TLineInfo; const msg: TMsgKind;
|
procedure liMessage(const info: TLineInfo; const msg: TMsgKind;
|
||||||
const arg: string = '');
|
const arg: string = '');
|
||||||
|
|
||||||
|
|
@ -840,37 +842,37 @@ begin
|
||||||
getMessageStr(errInstantiationFrom, '')]));
|
getMessageStr(errInstantiationFrom, '')]));
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure rawMessage(const msg: TMsgKind; const args: array of string);
|
procedure rawMessage(const msg: TMsgKind; const args: array of string);
|
||||||
var
|
var
|
||||||
frmt: string;
|
frmt: string;
|
||||||
begin
|
begin
|
||||||
case msg of
|
case msg of
|
||||||
errMin..errMax: begin
|
errMin..errMax: begin
|
||||||
writeContext();
|
writeContext();
|
||||||
frmt := rawErrorFormat;
|
frmt := rawErrorFormat;
|
||||||
end;
|
end;
|
||||||
warnMin..warnMax: begin
|
warnMin..warnMax: begin
|
||||||
if not (optWarns in gOptions) then exit;
|
if not (optWarns in gOptions) then exit;
|
||||||
if not (msg in gNotes) then exit;
|
if not (msg in gNotes) then exit;
|
||||||
frmt := rawWarningFormat;
|
frmt := rawWarningFormat;
|
||||||
inc(gWarnCounter);
|
inc(gWarnCounter);
|
||||||
end;
|
end;
|
||||||
hintMin..hintMax: begin
|
hintMin..hintMax: begin
|
||||||
if not (optHints in gOptions) then exit;
|
if not (optHints in gOptions) then exit;
|
||||||
if not (msg in gNotes) then exit;
|
if not (msg in gNotes) then exit;
|
||||||
frmt := rawHintFormat;
|
frmt := rawHintFormat;
|
||||||
inc(gHintCounter);
|
inc(gHintCounter);
|
||||||
end;
|
end;
|
||||||
else assert(false) // cannot happen
|
else assert(false) // cannot happen
|
||||||
end;
|
end;
|
||||||
MessageOut(Format(frmt, format(msgKindToString(msg), args)));
|
MessageOut(Format(frmt, format(msgKindToString(msg), args)));
|
||||||
handleError(msg);
|
handleError(msg);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure rawMessage(const msg: TMsgKind; const arg: string = '');
|
procedure rawMessage(const msg: TMsgKind; const arg: string = '');
|
||||||
begin
|
begin
|
||||||
rawMessage(msg, [arg]);
|
rawMessage(msg, [arg]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure liMessage(const info: TLineInfo; const msg: TMsgKind;
|
procedure liMessage(const info: TLineInfo; const msg: TMsgKind;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -388,7 +394,7 @@ begin
|
||||||
if (a.kind <> nkIdentDefs) and (a.kind <> nkVarTuple) then IllFormedAst(a);
|
if (a.kind <> nkIdentDefs) and (a.kind <> nkVarTuple) then IllFormedAst(a);
|
||||||
checkMinSonsLen(a, 3);
|
checkMinSonsLen(a, 3);
|
||||||
len := sonsLen(a);
|
len := sonsLen(a);
|
||||||
if a.sons[len-2] <> nil then
|
if a.sons[len-2] <> nil then
|
||||||
typ := semTypeNode(c, a.sons[len-2], nil)
|
typ := semTypeNode(c, a.sons[len-2], nil)
|
||||||
else
|
else
|
||||||
typ := nil;
|
typ := nil;
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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]);
|
||||||
|
|
|
||||||
106
nim/types.pas
106
nim/types.pas
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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'+'',
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,4 @@
|
||||||
@elif vcc:
|
@elif vcc:
|
||||||
cgen.speed = ""
|
cgen.speed = ""
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,13 @@
|
||||||
# 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.}
|
||||||
|
|
@ -6,9 +15,12 @@ var
|
||||||
proc ha(c, d: int): int {.cdecl.} =
|
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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,17 @@
|
||||||
|
|
||||||
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
Loading…
Add table
Add a link
Reference in a new issue