version 0.7.6

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

242
cogapp.py
View file

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

View file

@ -1,5 +1,5 @@
# This is the config file for the documentation generator.
# (c) 2008 Andreas Rumpf
# (c) 2009 Andreas Rumpf
# Feel free to edit the templates as you need.
split.item.toc = "20"

View file

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

View file

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

View file

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

View file

@ -19,14 +19,14 @@ Options:
--debugger:on|off turn Embedded Nimrod Debugger ON|OFF
-x, --checks:on|off code generation for all runtime checks ON|OFF
--obj_checks:on|off code generation for obj conversion checks ON|OFF
--field_checks:on|off code generation for case record fields ON|OFF
--field_checks:on|off code generation for case variant fields ON|OFF
--range_checks:on|off code generation for range checks ON|OFF
--bound_checks:on|off code generation for bound checks ON|OFF
--overflow_checks:on|off code generation for over-/underflow checks ON|OFF
-a, --assertions:on|off code generation for assertions ON|OFF
--dead_code_elim:on|off whole program dead code elimination ON|OFF
--opt:none|speed|size optimize not at all or for speed|size
--app:console|gui|lib generate a console|GUI application or a shared lib
--app:console|gui generate a console|GUI application
-r, --run run the compiled program with given arguments
--advanced show advanced command line switches
-h, --help show this help

View file

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

View file

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

View file

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

View file

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

View file

@ -6,12 +6,15 @@ Nimrod Standard Library
:Version: |nimrodversion|
Though the Nimrod Standard Library is still evolving, it is already quite
usable. It is divided into basic libraries that contains modules that virtually
every program will need and advanced libraries which are more heavy weight.
Advanced libraries are in the ``lib/base`` directory.
usable. It is divided into *pure libraries*, *impure libraries* and *wrappers*.
Basic libraries
===============
Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary
while impure libraries do. A wrapper is an impure library that is a very
low-level interface to a C library.
Pure libraries
==============
* `system <system.html>`_
Basic procs and operators that every program needs. It also provides IO
@ -51,6 +54,12 @@ Basic libraries
literals, raw string literals and triple quote string literals are supported
as in the Nimrod programming language.
* `parsexml <parsexml.html>`_
The ``parsexml`` module implements a simple high performance XML/HTML parser.
The only encoding that is supported is UTF-8. The parser has been designed
to be somewhat error correcting, so that even some "wild HTML" found on the
web can be parsed with it.
* `strtabs <strtabs.html>`_
The ``strtabs`` module implements an efficient hash table that is a mapping
from strings to strings. Supports a case-sensitive, case-insensitive and
@ -68,17 +77,31 @@ Basic libraries
Nimrod types.
* `lexbase <lexbase.html>`_
This is a low level module that implements an extremely efficent buffering
This is a low level module that implements an extremely efficient buffering
scheme for lexers and parsers. This is used by the ``parsecfg`` module.
* `terminal <terminal.html>`_
This module contains a few procedures to control the *terminal*
(also called *console*). The implementation simply uses ANSI escape
sequences and does not depend on any other module.
Advanced libaries
=================
* `cgi <cgi.html>`_
This module implements helper procs for CGI applictions.
* `unicode <unicode.html>`_
This module provides support to handle the Unicode UTF-8 encoding.
* `regexprs <regexprs.html>`_
This module contains procedures and operators for handling regular
expressions.
* `md5 <md5.html>`_
This module implements the MD5 checksum algorithm.
Impure libraries
================
* `dialogs <dialogs.html>`_
This module implements portable dialogs for Nimrod; the implementation
builds on the GTK interface. On Windows, native dialogs are shown if
@ -87,6 +110,11 @@ Advanced libaries
* `zipfiles <zipfiles.html>`_
This module implements a zip archive creator/reader/modifier.
* `web <web.html>`_
This module contains simple high-level procedures for dealing with the
web like loading the contents of a web page from an URL.
Wrappers
========
@ -97,6 +125,12 @@ not contained in the distribution. You can then find them on the website.
Contains a wrapper for the POSIX standard.
* `windows <windows.html>`_
Contains a wrapper for the Win32 API.
* `mysql <mysql.html>`_
Contains a wrapper for the mySQL API.
* `sqlite3 <sqlite3.html>`_
Contains a wrapper for SQLite 3 API.
* `libcurl <libcurl.html>`_
Contains a wrapper for the libcurl library.
* `shellapi <shellapi.html>`_
Contains a wrapper for the ``shellapi.h`` header.
* `shfolder <shfolder.html>`_

View file

@ -1011,8 +1011,8 @@ char '\0'
bool false
ref or pointer type nil
procedural type nil
sequence nil (**not** ``@[]``)
string nil (**not** "")
sequence nil (*not* ``@[]``)
string nil (*not* "")
tuple[x: A, y: B, ...] (default(A), default(B), ...)
(analogous for objects)
array[0..., T] [default(T), ...]
@ -1080,9 +1080,9 @@ Case statement
Syntax::
caseStmt ::= 'case' expr ('of' sliceExprList ':' stmt)*
('elif' expr ':' stmt)*
['else' ':' stmt]
caseStmt ::= 'case' expr [':'] ('of' sliceExprList ':' stmt)*
('elif' expr ':' stmt)*
['else' ':' stmt]
Example:
@ -1241,11 +1241,11 @@ sugar for:
``return`` without an expression is a short notation for ``return result`` if
the proc has a return type. The `result`:idx: variable is always the return
value of the procedure. It is automatically declared by the compiler. As all
variables, ``result`` is initialized to (binary) zero::
variables, ``result`` is initialized to (binary) zero:
.. code-block:: nimrod
proc returnZero(): int =
# implicitely returns 0
proc returnZero(): int =
# implicitely returns 0
Yield statement
@ -1649,7 +1649,7 @@ possible within a single ``type`` section.
Generics
~~~~~~~~
`Version 0.7.4: Complex generic types like in the example do not work.`:red:
`Version 0.7.6: Generic types like in the example do not work.`:red:
Example:

View file

@ -208,8 +208,9 @@ Acyclic Pragma
~~~~~~~~~~~~~~
The `acyclic`:idx: pragma can be used for object types to mark them as acyclic
even though they seem to be cyclic. This is an **optimization** for the garbage
collector to not consider objects of this type as part of a cycle::
collector to not consider objects of this type as part of a cycle:
.. code-block:: nimrod
type
PNode = ref TNode
TNode {.acyclic, final.} = object

File diff suppressed because it is too large Load diff

View file

@ -46,12 +46,14 @@ The most used commands and switches have abbreviations, so you can also use::
Though it should be pretty obvious what the program does, I will explain the
syntax: Statements which are not indented are executed when the program
starts. Indentation is Nimrod's way of grouping statements. String literals
are enclosed in double quotes. The ``var`` statement declares a new variable
named ``name`` of type ``string`` with the value that is returned by the
``readline`` procedure. Since the compiler knows that ``readline`` returns
a string, you can leave out the type in the declaration (this is called
`local type inference`:idx:). So this will work too:
starts. Indentation is Nimrod's way of grouping statements. Indentation is
done with spaces only, tabulators are not allowed.
String literals are enclosed in double quotes. The ``var`` statement declares
a new variable named ``name`` of type ``string`` with the value that is
returned by the ``readline`` procedure. Since the compiler knows that
``readline`` returns a string, you can leave out the type in the declaration
(this is called `local type inference`:idx:). So this will work too:
.. code-block:: Nimrod
var name = readline(stdin)
@ -73,7 +75,7 @@ keywords, comments, operators, and other punctation marks. Case is
*insignificant* in Nimrod and even underscores are ignored:
``This_is_an_identifier`` and this is the same identifier
``ThisIsAnIdentifier``. This feature enables you to use other
peoples code without bothering about a naming convention that conflicts with
people's code without bothering about a naming convention that conflicts with
yours. It also frees you from remembering the exact spelling of an identifier
(was it ``parseURL`` or ``parseUrl`` or ``parse_URL``?).
@ -129,6 +131,9 @@ the syntax, watch their indentation:
Echo("Hi!")
# comment has not the right indentation -> syntax error!
**Note**: To comment out a large piece of code, it is often better to use a
``when false:`` statement.
Numbers
-------
@ -137,7 +142,7 @@ Numerical literals are written as in most other languages. As a special twist,
underscores are allowed for better readability: ``1_000_000`` (one million).
A number that contains a dot (or 'e' or 'E') is a floating point literal:
``1.0e9`` (one million). Hexadecimal literals are prefixed with ``0x``,
binary literals with ``0b`` and octal literals with ``0c``. A leading zero
binary literals with ``0b`` and octal literals with ``0o``. A leading zero
alone does not produce an octal.
@ -428,6 +433,10 @@ the ``#ifdef`` construct in the C programming language.
**Note**: The documentation generator currently always follows the first branch
of when statements.
**Note**: To comment out a large piece of code, it is often better to use a
``when false:`` statement than to use real comments. This way nesting is
possible.
Statements and indentation
==========================
@ -440,7 +449,7 @@ statements*. *Simple statements* cannot contain other statements:
Assignment, procedure calls or the ``return`` statement belong to the simple
statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can
contain other statements. To avoid ambiguities, complex statements always have
to be intended, but single simple statements do not:
to be indented, but single simple statements do not:
.. code-block:: nimrod
# no indentation needed for single assignment statement:
@ -518,9 +527,11 @@ shorthand for ``return result``. So all tree code snippets are equivalent:
.. code-block:: nimrod
return 42
.. code-block:: nimrod
result = 42
return
.. code-block:: nimrod
result = 42
return result
@ -880,7 +891,7 @@ In Nimrod new types can be defined within a ``type`` statement:
.. code-block:: nimrod
type
biggestInt = int64 # biggest integer type that is available
biggestFLoat = float64 # biggest float type that is available
biggestFloat = float64 # biggest float type that is available
Enumeration and object types cannot be defined on the fly, but only within a
``type`` statement.
@ -980,7 +991,7 @@ basetype can only be an ordinal type. The reason is that sets are implemented
as high performance bit vectors.
Sets can be constructed via the set constructor: ``{}`` is the empty set. The
empty set is type combatible with any concrete set type. The constructor
empty set is type compatible with any concrete set type. The constructor
can also be used to include elements (and ranges of elements):
.. code-block:: nimrod
@ -1013,7 +1024,7 @@ operation meaning
================== ========================================================
Sets are often used to define a type for the *flags* of a procedure. This is
much cleaner (and type safe solution) solution than just defining integer
much cleaner (and type safe) solution than just defining integer
constants that should be ``or``'ed together.
@ -1047,36 +1058,6 @@ The built-in ``len`` proc returns the array's length. ``low(a)`` returns the
lowest valid index for the array `a` and ``high(a)`` the highest valid index.
Open arrays
-----------
Often fixed size arrays turn out to be too inflexible; procedures should
be able to deal with arrays of different sizes. The `openarray`:idx: type
allows this. Openarrays are always indexed with an ``int`` starting at
position 0. The ``len``, ``low`` and ``high`` operations are available
for open arrays too. Any array with a compatible base type can be passed to
an openarray parameter, the index type does not matter.
The openarray type cannot be nested: Multidimensional openarrays are not
supported because this is seldom needed and cannot be done efficiently.
An openarray is also a means to implement passing a variable number of
arguments to a procedure. The compiler converts the list of arguments
to an array automatically:
.. code-block:: nimrod
proc myWriteln(f: TFile, a: openarray[string]) =
for s in items(a):
write(f, s)
write(f, "\n")
myWriteln(stdout, "abc", "def", "xyz")
# is transformed by the compiler to:
myWriteln(stdout, ["abc", "def", "xyz"])
This transformation is only done if the openarray parameter is the
last parameter in the procedure header.
Sequences
---------
`Sequences`:idx: are similar to arrays but of dynamic length which may change
@ -1108,6 +1089,38 @@ rather than ``nil`` as the *empty* value. But ``@[]`` creates a sequence
object on the heap, so there is a trade-off to be made here.
Open arrays
-----------
**Note**: Openarrays can only be used for parameters.
Often fixed size arrays turn out to be too inflexible; procedures should
be able to deal with arrays of different sizes. The `openarray`:idx: type
allows this. Openarrays are always indexed with an ``int`` starting at
position 0. The ``len``, ``low`` and ``high`` operations are available
for open arrays too. Any array with a compatible base type can be passed to
an openarray parameter, the index type does not matter.
The openarray type cannot be nested: Multidimensional openarrays are not
supported because this is seldom needed and cannot be done efficiently.
An openarray is also a means to implement passing a variable number of
arguments to a procedure. The compiler converts the list of arguments
to an array automatically:
.. code-block:: nimrod
proc myWriteln(f: TFile, a: openarray[string]) =
for s in items(a):
write(f, s)
write(f, "\n")
myWriteln(stdout, "abc", "def", "xyz")
# is transformed by the compiler to:
myWriteln(stdout, ["abc", "def", "xyz"])
This transformation is only done if the openarray parameter is the
last parameter in the procedure header.
Tuples
------
@ -1271,8 +1284,19 @@ with an asterisk (``*``) are exported:
# multiply two int sequences:
for i in 0..len(a)-1: result[i] = a[i] * b[i]
when isMainModule:
# test the new ``*`` operator for sequences:
assert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9])
The above module exports ``x`` and ``*``, but not ``y``.
The top-level statements of a module are executed at the start of the program.
This can be used to initalize complex data structures for example.
Each module has a special magic constant ``isMainModule`` that is true if the
module is compiled as the main file. This is very useful to embed tests within
the module as shown by the above example.
Modules that depend on each other are possible, but strongly discouraged,
because then one module cannot be reused without the other.

View file

@ -33,7 +33,8 @@ Object Oriented Programming
While Nimrod's support for object oriented programming (OOP) is minimalistic,
powerful OOP technics can be used. OOP is seen as *one* way to design a
program, not *the only* way. Often a procedural approach leads to simpler
and more efficient code.
and more efficient code. In particular, prefering aggregation over inheritance
often yields to a better design.
Objects
@ -196,8 +197,8 @@ for any type:
stdout.writeln("Hallo") # the same as write(stdout, "Hallo")
If it gives you warm fuzzy feelings, you can even write ``1.`+`(2)`` instead of
``1 + 2`` and claim that Nimrod is a pure object oriented language. (That
would not even be lying: *pure OO* has no meaning anyway. :-)
``1 + 2`` and claim that Nimrod is a pure object oriented language. (But
that's not true. :-)
Properties
@ -258,20 +259,24 @@ The example is silly, since a vector is better modelled by a tuple which
already provides ``v[]`` access.
Dynamic binding
---------------
In Nimrod procedural types are used to implement dynamic binding. The following
example also shows some more conventions: The ``self`` or ``this`` object
is named ``my`` (because it is shorter than the alternatives), each class
provides a constructor, etc.
Dynamic dispatch
----------------
In Nimrod procedural types are used to implement dynamic dispatch. The
following example also shows some more conventions: The ``self`` or ``this``
object is named ``my`` (because it is shorter than the alternatives), each
class provides a constructor, etc.
.. code-block:: nimrod
type
TFigure = object of TObject # abstract base class:
draw: proc (my: var TFigure) # concrete classes implement this proc
TFigure = object of TObject # abstract base class:
fDraw: proc (my: var TFigure) # concrete classes implement this proc
proc init(f: var TFigure) =
f.draw = nil
f.fDraw = nil
proc draw(f: var TFigure) =
# ``draw`` dispatches dynamically:
f.fDraw(f)
type
TCircle = object of TFigure
@ -282,7 +287,7 @@ provides a constructor, etc.
proc init(my: var TCircle) =
init(TFigure(my)) # call base constructor
my.radius = 5
my.draw = drawCircle
my.fdraw = drawCircle
type
TRectangle = object of TFigure
@ -294,50 +299,7 @@ provides a constructor, etc.
init(TFigure(my)) # call base constructor
my.width = 5
my.height = 10
my.draw = drawRectangle
# now use these classes:
var
r: TRectangle
c: TCircle
init(r)
init(c)
r.draw(r)
c.draw(c)
The last line shows the syntactical difference between static and dynamic
binding: The ``r.draw(r)`` dynamic call refers to ``r`` twice. This difference
is not necessarily bad. But if you want to eliminate the somewhat redundant
``r``, it can be done by using *closures*:
.. code-block:: nimrod
type
TFigure = object of TObject # abstract base class:
draw: proc () {.closure.} # concrete classes implement this proc
proc init(f: var TFigure) =
f.draw = nil
type
TCircle = object of TFigure
radius: int
proc init(me: var TCircle) =
init(TFigure(me)) # call base constructor
me.radius = 5
me.draw = lambda () =
echo("o " & $me.radius)
type
TRectangle = object of TFigure
width, height: int
proc init(me: var TRectangle) =
init(TFigure(me)) # call base constructor
me.width = 5
me.height = 10
me.draw = lambda () =
echo("[]")
my.fdraw = drawRectangle
# now use these classes:
var
@ -348,10 +310,12 @@ is not necessarily bad. But if you want to eliminate the somewhat redundant
r.draw()
c.draw()
The example also introduces `lambda`:idx: expressions: A ``lambda`` expression
defines a new proc with the ``closure`` calling convention on the fly.
The code uses a ``draw`` procedure that is bound statically, but inside it
the dynamic dispatch happens with the help of the ``fdraw`` field. This is
slightly more inconvienent than in traditional OOP-languages, but has the
advantage of being much more flexible (and somewhat faster). The above approach
also allows some form *monkey patching* by modifying the ``fdraw`` field.
`Version 0.7.4: Closures and lambda expressions are not implemented.`:red:
Exceptions
@ -432,7 +396,7 @@ is not executed (if an exception occurs).
Generics
========
`Version 0.7.4: Complex generic types like in the example do not work.`:red:
`Version 0.7.6: Complex generic types like in the example do not work.`:red:
`Generics`:idx: are Nimrod's means to parametrize procs, iterators or types
with `type parameters`:idx:. They are most useful for efficient type safe
@ -458,8 +422,8 @@ containers:
else:
var it = root
while it != nil:
# compare the data items; uses the generic ``cmd`` proc that works for
# any type that has a ``==`` and ``<`` operator
# compare the data items; uses the generic ``cmd`` proc
# that works for any type that has a ``==`` and ``<`` operator
var c = cmp(it.data, n.data)
if c < 0:
if it.le == nil:
@ -491,7 +455,7 @@ containers:
var
root: PBinaryTree[string] # instantiate a PBinaryTree with ``string``
add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and ``add``
add(root, newNode("hallo")) # instantiates ``newNode`` and ``add``
add(root, "world") # instantiates the second ``add`` proc
for str in preorder(root):
stdout.writeln(str)
@ -567,6 +531,7 @@ Turning the ``log`` proc into a template solves this problem in an elegant way:
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
description*). These are no real types, they just help the compiler parsing.
In later versions, real types will be supported too.
The template body does not open a new scope. To open a new scope
use a ``block`` statement:
@ -587,6 +552,35 @@ use a ``block`` statement:
b = 42 # does not work, `b` is unknown
If there is a ``stmt`` parameter it should be the last in the template
declaration. The reason is that statements can be passed to a template
via a special ``:`` syntax:
.. code-block:: nimrod
template withFile(f, filename, mode: expr, actions: stmt): stmt =
block:
var fn = filename
var f: TFile
if openFile(f, fn, mode):
try:
actions
finally:
closeFile(f)
else:
quit("cannot open: " & fn)
withFile(txt, "ttempl3.txt", fmWrite):
txt.writeln("line 1")
txt.writeln("line 2")
In the example the two ``writeln`` statements are bound to the ``actions``
parameter. The ``withFile`` template contains boilerplate code and helps to
avoid a common bug: To forget to close the file. Note how the
``var fn = filename`` statement ensures that ``filename`` is evaluated only
once.
Macros
======
@ -658,36 +652,6 @@ The macro call expands to:
writeln(stdout, x)
Lets return to the dynamic binding ``r.draw(r)`` notational "problem". Apart
from closures, there is another "solution": Define an infix ``!`` macro
operator which hides it:
.. code-block::
macro `!` (n: expr): expr =
result = newNimNode(nnkCall, n)
var dot = newNimNode(nnkDotExpr, n)
dot.add(n[1]) # obj
if n[2].kind == nnkCall:
# transforms ``obj!method(arg1, arg2, ...)`` to
# ``(obj.method)(obj, arg1, arg2, ...)``
dot.add(n[2][0]) # method
result.add(dot)
result.add(n[1]) # obj
for i in 1..n[2].len-1:
result.add(n[2][i])
else:
# transforms ``obj!method`` to
# ``(obj.method)(obj)``
dot.add(n[2]) # method
result.add(dot)
result.add(n[1]) # obj
r!draw(a, b, c) # will be transfomed into ``r.draw(r, a, b, c)``
Great! 20 lines of complex code to safe a few keystrokes! Obviously, this is
exactly you should not do! (But it makes a cool example.)
Statement Macros
----------------

View file

@ -22,6 +22,8 @@ type
menu: PGtkMenuBar
notebook: PGtkNotebook
tabname: int # used for generating tab names
tabs: seq[PTab]
currTab: int
PEditor = ptr TEditor
proc on_window_destroy(obj: PGtkObject, event: PGdkEvent,
@ -46,9 +48,50 @@ proc getTabIndex(e: PEditor, tab: PTab): int =
if tab.hbox == v: return i
inc(i)
proc getActiveTab(e: PEditor): PTab =
nil
type
TAnswer = enum
answerYes, answerNo, answerCancel
proc askWhetherToSave(e: PEditor): TAnswer =
var dialog = gtk_dialog_new_with_buttons("Should the changes be saved?",
e.window, GTK_DIALOG_MODAL, GTK_STOCK_SAVE, 1,
"gtk-discard", 2,
GTK_STOCK_CANCEL, 3, nil)
result = TAnswer(gtk_dialog_run(dialog)+1)
gtk_widget_destroy(dialog)
proc saveTab(tab: PTab) =
if tab.untitled:
tab.filename = ChooseFileToSave(tab.e.window, getRoot(tab))
tab.untitled = false
XXX
proc OnCloseTab(button: PGtkButton, tab: PTab) {.cdecl.} =
var idx = getTabIndex(tab.e, tab)
if idx >= 0: gtk_notebook_remove_page(tab.e.notebook, idx)
var idx = -1
for i in 0..high(tab.e.tabs):
if tab.e.tabs[i] == tab:
idx = i
break
if idx >= 0:
if gtk_text_buffer_get_modified(gtk_text_view_get_buffer(tab.textView)):
case askWhetherToSave(tab.e)
of answerCancel: return
of answerYes: saveTab(tab)
of answerNo: nil
gtk_notebook_remove_page(tab.e.notebook, idx)
if idx < high(tab.e.tabs):
for i in idx..high(tab.e.tabs)-1:
tab.e.tabs[i] = tab.e.tabs[i+1]
else:
dec currTab
GC_unref(tab.filename)
dealloc(tab)
#var idx = getTabIndex(tab.e, tab)
#if idx >= 0: gtk_notebook_remove_page(tab.e.notebook, idx)
proc createTab(e: PEditor, filename: string, untitled: bool) =
var t = cast[PTab](alloc0(sizeof(TTab)))
@ -78,8 +121,22 @@ proc createTab(e: PEditor, filename: string, untitled: bool) =
gtk_widget_show(lab)
var idx = gtk_notebook_append_page(e.notebook, scroll, t.hbox)
e.currTab = idx
add(e.tabs, t)
gtk_notebook_set_current_page(e.notebook, idx)
proc on_open_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
var files = ChooseFilesToOpen(e.window, getRoot(getActiveTab(e)))
for f in items(files): createTab(e, f, untitled=false)
proc on_save_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
var cp = gtk_notebook_get_current_page(e.notebook)
proc on_save_as_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
nil
proc on_new_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
inc(e.tabname)
createTab(e, "untitled-" & $e.tabname, true)
@ -91,6 +148,8 @@ proc main(e: PEditor) =
e.window = GTK_WINDOW(glade_xml_get_widget(builder, "window"))
e.statusbar = GTK_STATUSBAR(glade_xml_get_widget(builder, "statusbar"))
e.notebook = GTK_NOTEBOOK(glade_xml_get_widget(builder, "notebook"))
e.tabs = @[]
e.currTab = -1
setHomogeneous(e.notebook^, 1)
# connect the signal handlers:
@ -108,6 +167,18 @@ proc main(e: PEditor) =
discard g_signal_connect(quitItem, "activate",
G_CALLBACK(on_window_destroy), e)
var openItem = GTK_MENU_ITEM(glade_xml_get_widget(builder, "open_menu_item"))
discard g_signal_connect(openItem, "activate",
G_CALLBACK(on_open_menu_item_activate), e)
var saveItem = GTK_MENU_ITEM(glade_xml_get_widget(builder, "save_menu_item"))
discard g_signal_connect(saveItem, "activate",
G_CALLBACK(on_save_menu_item_activate), e)
var saveAsItem = GTK_MENU_ITEM(glade_xml_get_widget(builder, "save_as_menu_item"))
discard g_signal_connect(saveAsItem, "activate",
G_CALLBACK(on_save_as_menu_item_activate), e)
gtk_window_set_default_icon_name(GTK_STOCK_EDIT)
gtk_widget_show(e.window)
gtk_main()

219
koch.py
View file

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

View file

@ -1,13 +1,20 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# Low level allocator for Nimrod.
# TODO:
# - eliminate "used" field
# - make searching for block O(1)
proc raiseOutOfMem {.noinline.} =
assert false
quit(1)
# ------------ platform specific chunk allocation code -----------------------
@ -15,20 +22,14 @@ when defined(posix):
const # XXX: make these variables for portability?
PROT_READ = 1 # page can be read
PROT_WRITE = 2 # page can be written
PROT_EXEC = 4 # page can be executed
PROT_NONE = 0 # page can not be accessed
MAP_PRIVATE = 2 # Changes are private
MAP_SHARED = 1 # Share changes
MAP_PRIVATE = 2 # Changes are private
MAP_TYPE = 0xf # Mask for type of mapping
MAP_FIXED = 0x10 # Interpret addr exactly
MAP_ANONYMOUS = 0x20 # don't use a file
MAP_GROWSDOWN = 0x100 # stack-like segment
MAP_DENYWRITE = 0x800 # ETXTBSY
MAP_EXECUTABLE = 0x1000 # mark it as an executable
MAP_LOCKED = 0x2000 # pages are locked
MAP_NORESERVE = 0x4000 # don't check for reservations
when defined(linux):
const MAP_ANONYMOUS = 0x20 # don't use a file
elif defined(macosx):
const MAP_ANONYMOUS = 0x1000
else:
const MAP_ANONYMOUS = 0 # other operating systems may not know about this
proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
off: int): pointer {.header: "<sys/mman.h>".}
@ -42,7 +43,7 @@ when defined(posix):
raiseOutOfMem()
proc osDeallocPages(p: pointer, size: int) {.inline} =
munmap(p, len)
munmap(p, size)
elif defined(windows):
const
@ -51,20 +52,27 @@ elif defined(windows):
MEM_TOP_DOWN = 0x100000
PAGE_READWRITE = 0x04
MEM_DECOMMIT = 0x4000
MEM_RELEASE = 0x8000
proc VirtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType,
flProtect: int32): pointer {.
header: "<windows.h>", stdcall.}
proc VirtualFree(lpAddress: pointer, dwSize: int,
dwFreeType: int32) {.header: "<windows.h>", stdcall.}
proc osAllocPages(size: int): pointer {.inline.} =
result = VirtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT,
PAGE_READWRITE)
if result == nil: raiseOutOfMem()
proc osDeallocPages(p: pointer, size: int) {.inline.} =
nil
# according to Microsoft, 0 is the only correct value here:
VirtualFree(p, 0, MEM_RELEASE)
else:
{.error: "Port GC to your platform".}
{.error: "Port memory manager to your platform".}
# --------------------- end of non-portable code -----------------------------
@ -78,59 +86,79 @@ else:
# Guess the page size of the system; if it is the
# wrong value, performance may be worse (this is not
# for sure though), but GC still works; must be a power of two!
when defined(linux) or defined(windows) or defined(macosx):
const
PageShift = 12
PageSize = 1 shl PageShift # on 32 bit systems 4096
else:
{.error: "unkown page size".}
const
PageShift = if sizeof(pointer) == 4: 12 else: 13
PageSize = 1 shl PageShift # on 32 bit systems 4096
PageMask = PageSize-1
MemAlignment = sizeof(pointer)*2 # minimal memory block that can be allocated
BitsPerUnit = sizeof(int)*8
# a "unit" is a word, i.e. 4 bytes
# on a 32 bit system; I do not use the term "word" because under 32-bit
# Windows it is sometimes only 16 bits
SmallChunkSize = PageSize # * 4
BitsPerPage = PageSize div MemAlignment
UnitsPerPage = BitsPerPage div BitsPerUnit
# how many units do we need to describe a page:
MemAlign = 8 # minimal memory block that can be allocated
BitsPerPage = PageSize div MemAlign
UnitsPerPage = BitsPerPage div (sizeof(int)*8)
# how many ints do we need to describe a page:
# on 32 bit systems this is only 16 (!)
smallRequest = PageSize div 4
ChunkOsReturn = 1024 # in pages
ChunkOsReturn = 64 * PageSize
InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn!
debugMemMan = true # we wish to debug the memory manager...
# Compile time options:
coalescRight = true
coalescLeft = true
const
TrunkShift = 9
BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and divisible by 64
TrunkMask = BitsPerTrunk - 1
IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8)
IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width
IntMask = 1 shl IntShift - 1
type
PChunkDesc = ptr TChunkDesc
TChunkDesc {.final, pure.} = object
key: TAddress # address at bit 0
next: PChunkDesc
bits: array[0..127, int] # a bit vector
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
PChunkDescArray = ptr array[0..1000_000, PChunkDesc]
TChunkSet {.final, pure.} = object
counter, max: int
head: PChunkDesc
data: PChunkDescArray
when sizeof(int) == 4:
type THalfWord = int16
else:
type THalfWord = int32
TTrunkBuckets = array[0..1023, PTrunk]
TIntSet {.final.} = object
data: TTrunkBuckets
type
TAlignType = float
TFreeCell {.final, pure.} = object
zeroField: pointer # type info nil means cell is not used
next: ptr TFreeCell # next free cell in chunk
next: ptr TFreeCell # next free cell in chunk (overlaid with refcount)
zeroField: pointer # nil means cell is not used (overlaid with typ field)
PChunk = ptr TChunk
TChunk {.final, pure.} = object
size: int # lowest two bits are used for merging:
# bit 0: chunk to the left is accessible and free
# bit 1: chunk to the right is accessible and free
len: int # for small object allocation
prev, next: PChunk # chunks of the same (or bigger) size
#len, used: THalfWord # index of next to allocate cell
PChunk = ptr TBaseChunk
PBigChunk = ptr TBigChunk
PSmallChunk = ptr TSmallChunk
TBaseChunk {.pure.} = object
prevSize: int # size of previous chunk; for coalescing
size: int # if < PageSize it is a small chunk
used: bool # later will be optimized into prevSize...
TSmallChunk = object of TBaseChunk
next, prev: PSmallChunk # chunks of the same size
freeList: ptr TFreeCell
data: float # a float for alignment purposes
free: int # how many bytes remain
acc: int # accumulator for small object allocation
data: TAlignType # start of usable memory
TBigChunk = object of TBaseChunk # not necessarily > PageSize!
next: PBigChunk # chunks of the same (or bigger) size
prev: PBigChunk
data: TAlignType # start of usable memory
template smallChunkOverhead(): expr = sizeof(TSmallChunk)-sizeof(TAlignType)
template bigChunkOverhead(): expr = sizeof(TBigChunk)-sizeof(TAlignType)
proc roundup(x, v: int): int {.inline.} = return ((-x) and (v-1)) +% x
@ -147,240 +175,325 @@ assert(roundup(15, 8) == 16)
type
PLLChunk = ptr TLLChunk
TLLChunk {.pure.} = object ## *low-level* chunk
size: int
when sizeof(int) == 4:
align: int
size: int # remaining size
acc: int # accumulator
TAllocator {.final, pure.} = object
llmem: PLLChunk
UsedPagesCount, FreePagesCount, maxPagesCount: int
freeSmallChunks: array[0..smallRequest div MemAlign-1, PChunk]
freeBigChunks: array[0..ChunkOsReturn-1, PChunk]
currMem, maxMem: int # currently and maximum used memory size (allocated from OS)
freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
chunkStarts: TIntSet
proc incCurrMem(a: var TAllocator, bytes: int) {.inline.} =
inc(a.currMem, bytes)
proc decCurrMem(a: var TAllocator, bytes: int) {.inline.} =
a.maxMem = max(a.maxMem, a.currMem)
dec(a.currMem, bytes)
proc getMaxMem(a: var TAllocator): int =
# Since we update maxPagesCount only when freeing pages,
# maxPagesCount may not be up to date. Thus we use the
# maximum of these both values here:
return max(a.currMem, a.maxMem)
var
allocator: TAllocator
proc llAlloc(a: var TAllocator, size: int): pointer =
# *low-level* alloc for the memory managers data structures. Deallocation
# is never done.
assert(size <= PageSize-8)
if a.llmem.size + size > PageSize:
a.llmem = osGetPages(PageSize)
inc(a.gUsedPages)
a.llmem.size = 8
result = cast[pointer](cast[TAddress](a.llmem) + a.llmem.size)
inc(llmem.size, size)
if a.llmem == nil or size > a.llmem.size:
var request = roundup(size+sizeof(TLLChunk), PageSize)
a.llmem = cast[PLLChunk](osAllocPages(request))
incCurrMem(a, request)
a.llmem.size = request - sizeof(TLLChunk)
a.llmem.acc = sizeof(TLLChunk)
result = cast[pointer](cast[TAddress](a.llmem) + a.llmem.acc)
dec(a.llmem.size, size)
inc(a.llmem.acc, size)
zeroMem(result, size)
proc IntSetGet(t: TIntSet, key: int): PTrunk =
var it = t.data[key and high(t.data)]
while it != nil:
if it.key == key: return it
it = it.next
result = nil
const
InitChunkSetSize = 1024 # must be a power of two!
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 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)
proc Contains(s: TIntSet, key: int): bool =
var t = IntSetGet(s, key shr TrunkShift)
if t != nil:
u = (u %% PageSize) /% MemAlignment
result = (t.bits[u /% BitsPerUnit] and (1 shl (u %% BitsPerUnit))) != 0
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 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 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 TCellSet, cell: PCell) =
var u = cast[TAddress](cell)
var t = ChunkSetGet(s, u shr PageShift)
proc Excl(s: var TIntSet, key: int) =
var t = IntSetGet(s, key shr TrunkShift)
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
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 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 pageIndex(c: PChunk): int {.inline.} =
result = cast[TAddress](c) shr PageShift
proc addChunk(a: var TAllocator, c: PChunk) {.inline.} =
var s = abs(c.size) div PageSize
proc pageIndex(p: pointer): int {.inline.} =
result = cast[TAddress](p) shr PageShift
proc pageAddr(p: pointer): PChunk {.inline.} =
result = cast[PChunk](cast[TAddress](p) and not PageMask)
assert(Contains(allocator.chunkStarts, pageIndex(result)))
var lastSize = PageSize
proc requestOsChunks(a: var TAllocator, size: int): PBigChunk =
incCurrMem(a, size)
result = cast[PBigChunk](osAllocPages(size))
assert((cast[TAddress](result) and PageMask) == 0)
result.next = nil
result.prev = nil
result.used = false
result.size = size
# update next.prevSize:
var nxt = cast[TAddress](result) +% size
assert((nxt and PageMask) == 0)
var next = cast[PChunk](nxt)
if pageIndex(next) in a.chunkStarts:
#echo("Next already allocated!")
next.prevSize = size
# set result.prevSize:
var prv = cast[TAddress](result) -% lastSize
assert((nxt and PageMask) == 0)
var prev = cast[PChunk](prv)
if pageIndex(prev) in a.chunkStarts and prev.size == lastSize:
#echo("Prev already allocated!")
result.prevSize = lastSize
else:
result.prevSize = 0 # unknown
lastSize = size # for next request
proc freeOsChunks(a: var TAllocator, p: pointer, size: int) =
# update next.prevSize:
var c = cast[PChunk](p)
var nxt = cast[TAddress](p) +% c.size
assert((nxt and PageMask) == 0)
var next = cast[PChunk](nxt)
if pageIndex(next) in a.chunkStarts:
next.prevSize = 0 # XXX used
excl(a.chunkStarts, pageIndex(p))
osDeallocPages(p, size)
decCurrMem(a, size)
proc isAccessible(p: pointer): bool {.inline.} =
result = Contains(allocator.chunkStarts, pageIndex(p))
proc ListAdd[T](head: var T, c: T) {.inline.} =
assert c.prev == nil
assert c.next == nil
c.next = head
if head != nil:
assert head.prev == nil
head.prev = c
head = c
proc ListRemove[T](head: var T, c: T) {.inline.} =
if c == head:
head = c.next
assert c.prev == nil
if head != nil: head.prev = nil
else:
assert c.prev != nil
c.prev.next = c.next
if c.next != nil: c.next.prev = c.prev
c.next = nil
c.prev = nil
c.next = a.freeChunks[s]
a.freeChunks[s] = c
proc freeChunk(a: var TAllocator, c: PChunk) =
assert(c.size > 0)
if c.size < PageSize: c.size = PageSize
var le = cast[PChunk](cast[TAddress](p) and not PageMask -% PageSize)
var ri = cast[PChunk](cast[TAddress](p) and not PageMask +%
c.size +% PageSize)
if isStartOfAChunk(ri) and ri.size < 0:
removeChunk(a, ri)
inc(c.size, -ri.size)
if isEndOfAChunk(le):
le = cast[PChunk](cast[TAddress](p) and not PageMask -%
le.chunkStart+PageSize)
if le.size < 0:
removeChunk(a, le)
inc(le.size, c.size)
addChunk(a, le)
return
c.size = -c.size
addChunk(a, c)
proc isSmallChunk(c: PChunk): bool {.inline.} =
return c.size <= SmallChunkSize-smallChunkOverhead()
#return c.size < SmallChunkSize
proc splitChunk(a: var TAllocator, c: PChunk, size: int) =
var rest = cast[PChunk](cast[TAddress](p) + size)
rest.size = size - c.size # results in negative number, because rest is free
addChunk(a, rest)
# mark pages as accessible:
ChunkTablePut(a, rest, bitAccessible)
proc 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)
if c.size < ChunkOsReturn:
ListAdd(a.freeChunksList, c)
c.used = false
else:
freeOsChunks(a, c, c.size)
proc splitChunk(a: var TAllocator, c: PBigChunk, size: int) =
var rest = cast[PBigChunk](cast[TAddress](c) +% size)
rest.size = c.size - size
rest.used = false
rest.next = nil # XXX
rest.prev = nil
rest.prevSize = size
c.size = size
incl(a.chunkStarts, pageIndex(rest))
ListAdd(a.freeChunksList, rest)
proc getChunkOfSize(a: var TAllocator, size: int): PChunk =
for i in size..high(a.freeChunks):
result = a.freeChunks[i]
if result != nil:
if i != size: splitChunk(a, result, size)
else: removeChunk(a, result)
result.prev = nil
result.next = nil
break
proc getBigChunk(a: var TAllocator, size: int): PBigChunk =
# use first fit for now:
assert((size and PageMask) == 0)
result = a.freeChunksList
block search:
while result != nil:
assert chunkUnused(result)
if result.size == size:
ListRemove(a.freeChunksList, result)
break search
elif result.size > size:
splitChunk(a, result, size)
ListRemove(a.freeChunksList, result)
break search
result = result.next
if size < InitialMemoryRequest:
result = requestOsChunks(a, InitialMemoryRequest)
splitChunk(a, result, size)
else:
result = requestOsChunks(a, size)
result.prevSize = 0
result.used = true
incl(a.chunkStarts, pageIndex(result))
proc getSmallChunk(a: var TAllocator): PSmallChunk =
var res = getBigChunk(a, PageSize)
assert res.prev == nil
assert res.next == nil
result = cast[PSmallChunk](res)
# -----------------------------------------------------------------------------
proc getChunk(p: pointer): PChunk {.inline.} =
result = cast[PChunk](cast[TAddress](p) and not PageMask)
proc getCellSize(p: pointer): int {.inline.} =
var c = getChunk(p)
result = abs(c.size)
var c = pageAddr(p)
result = c.size
proc alloc(a: var TAllocator, size: int): pointer =
if size <= smallRequest:
# allocate a small block
proc alloc(a: var TAllocator, requestedSize: int): pointer =
var size = roundup(max(requestedSize, sizeof(TFreeCell)), MemAlign)
if size <= SmallChunkSize-smallChunkOverhead():
# allocate a small block: for small chunks, we use only its next pointer
var s = size div MemAlign
var c = a.freeSmallChunks[s]
if c == nil:
c = getChunkOfSize(0)
c = getSmallChunk(a)
c.freeList = nil
assert c.size == PageSize
c.size = size
a.freeSmallChunks[s] = c
c.len = 1
c.used = 1
c.chunkStart = 0
result = addr(c.data[0])
elif c.freeList != nil:
result = c.freeList
assert(c.freeList.zeroField == nil)
c.freeList = c.freeList.next
inc(c.used)
if c.freeList == nil: removeChunk(a, c)
c.acc = size
c.free = SmallChunkSize - smallChunkOverhead() - size
c.next = nil
c.prev = nil
ListAdd(a.freeSmallChunks[s], c)
result = addr(c.data)
else:
assert(c.len*size <= high(c.data))
result = addr(c.data[c.len*size])
inc(c.len)
inc(c.used)
if c.len*size > high(c.data): removeChunk(a, c)
assert c.next != c
assert c.size == size
if c.freeList == nil:
assert(c.acc + smallChunkOverhead() + size <= SmallChunkSize)
result = cast[pointer](cast[TAddress](addr(c.data)) +% c.acc)
inc(c.acc, size)
else:
result = c.freeList
assert(c.freeList.zeroField == nil)
c.freeList = c.freeList.next
dec(c.free, size)
if c.free < size:
ListRemove(a.freeSmallChunks[s], c)
else:
size = roundup(requestedSize+bigChunkOverhead(), PageSize)
# allocate a large block
var c = getChunkOfSize(size shr PageShift)
result = addr(c.data[0])
c.freeList = nil
c.size = size
c.len = 0
c.used = 0
c.chunkStart = 0
var c = getBigChunk(a, size)
assert c.prev == nil
assert c.next == nil
assert c.size == size
result = addr(c.data)
cast[ptr TFreeCell](result).zeroField = cast[ptr TFreeCell](1) # make it != nil
#echo("setting to one: ", $cast[TAddress](addr(cast[ptr TFreeCell](result).zeroField)))
proc contains(list, x: PSmallChunk): bool =
var it = list
while it != nil:
if it == x: return true
it = it.next
proc dealloc(a: var TAllocator, p: pointer) =
var c = getChunk(p)
if c.size <= smallRequest:
# free small block:
var c = pageAddr(p)
if isSmallChunk(c):
# `p` is within a small chunk:
var c = cast[PSmallChunk](c)
var s = c.size
var f = cast[ptr TFreeCell](p)
#echo("setting to nil: ", $cast[TAddress](addr(f.zeroField)))
assert(f.zeroField != nil)
f.zeroField = nil
f.next = c.freeList
c.freeList = p
dec(c.used)
if c.used == 0: freeChunk(c)
c.freeList = f
# check if it is not in the freeSmallChunks[s] list:
if c.free < s:
assert c notin a.freeSmallChunks[s div memAlign]
# add it to the freeSmallChunks[s] array:
ListAdd(a.freeSmallChunks[s div memAlign], c)
inc(c.free, s)
else:
inc(c.free, s)
if c.free == SmallChunkSize-smallChunkOverhead():
ListRemove(a.freeSmallChunks[s div memAlign], c)
c.size = SmallChunkSize
freeBigChunk(a, cast[PBigChunk](c))
else:
# free big chunk
freeChunk(c)
freeBigChunk(a, cast[PBigChunk](c))
proc realloc(a: var TAllocator, p: pointer, size: int): pointer =
# could be made faster, but this is unnecessary, the GC does not use it anyway
@ -389,6 +502,34 @@ proc realloc(a: var TAllocator, p: pointer, size: int): pointer =
dealloc(a, p)
proc isAllocatedPtr(a: TAllocator, p: pointer): bool =
var c = getChunk(p)
if c in a.accessibleChunks and c.size > 0:
result = cast[ptr TFreeCell](p).zeroField != nil
if isAccessible(p):
var c = pageAddr(p)
if not chunkUnused(c):
if isSmallChunk(c):
result = (cast[TAddress](p) -% cast[TAddress](c) -%
smallChunkOverhead()) %% c.size == 0 and
cast[ptr TFreeCell](p).zeroField != nil
else:
var c = cast[PBigChunk](c)
result = p == addr(c.data)
when isMainModule:
const iterations = 4000_000
incl(allocator.chunkStarts, 11)
assert 11 in allocator.chunkStarts
excl(allocator.chunkStarts, 11)
assert 11 notin allocator.chunkStarts
var p: array [1..iterations, pointer]
for i in 7..7:
var x = i * 8
for j in 1.. iterations:
p[j] = alloc(allocator, x)
for j in 1..iterations:
assert isAllocatedPtr(allocator, p[j])
echo($i, " used memory: ", $(allocator.currMem))
for j in countdown(iterations, 1):
#echo("j: ", $j)
dealloc(allocator, p[j])
assert(not isAllocatedPtr(allocator, p[j]))
echo($i, " after freeing: ", $(allocator.currMem))

View file

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

View file

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

View file

@ -1,486 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- This file is generated by Nimrod. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Module cgi</title>
<style type="text/css">
span.DecNumber {color: blue}
span.BinNumber {color: blue}
span.HexNumber {color: blue}
span.OctNumber {color: blue}
span.FloatNumber {color: blue}
span.Identifier {color: black}
span.Keyword {font-weight: bold}
span.StringLit {color: blue}
span.LongStringLit {color: blue}
span.CharLit {color: blue}
span.EscapeSequence {color: black}
span.Operator {color: black}
span.Punctation {color: black}
span.Comment, span.LongComment {font-style:italic; color: green}
span.RegularExpression {color: pink}
span.TagStart {color: yellow}
span.TagEnd {color: yellow}
span.Key {color: blue}
span.Value {color: black}
span.RawData {color: blue}
span.Assembler {color: blue}
span.Preprocessor {color: yellow}
span.Directive {color: yellow}
span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference,
span.Other {color: black}
div.navigation {
float: left;
width: 27%; //25em;
margin: 0; padding: 0; /*
border: 1px dashed gold; */
outline: 3px outset #7F7F7F; //#99ff99; //gold;
background-color: #7F7F7F;
}
div.navigation ul {list-style-type: none;}
div.navigation ul li a, div.navigation ul li a:visited {
font-weight: bold;
color: #FFFFFF; // #CC0000;
text-decoration: none;
}
div.navigation ul li a:hover {
font-weight: bold;
text-decoration: none;
/*outline: 2px outset #7F7F7F;*/
color: gold;
/* background-color: #FFFFFF; // #1A1A1A; // #779977;*/
}
div.content {
margin-left: 27%; // 25em;
padding: 0 1em;
/*border: 1px dashed #1A1A1A;*/
min-width: 16em;
}
dl.item dd, dl.item dd p {
margin-top:3px;
}
dl.item dd pre {
margin-left: 15pt;
border: 0px;
}
dl.item dt, dl.item dt pre {
margin: 20pt 0 0 0;
}
pre, span.tok {
background-color:#F9F9F9;
border:1px dotted #2F6FAB;
color:black;
}
/*
:Author: David Goodger
:Contact: goodger@python.org
:Date: Date: 2006-05-21 22:44:42 +0200 (Sun, 21 May 2006)
:Revision: Revision: 4564
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th { border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first { margin-top: 0 ! important }
.last, .with-subtitle { margin-bottom: 0 ! important }
.hidden { display: none }
a.toc-backref { text-decoration: none ; color: black }
blockquote.epigraph { margin: 2em 5em ; }
dl.docutils dd { margin-bottom: 0.5em }
div.abstract { margin: 2em 5em }
div.abstract p.topic-title { font-weight: bold ; text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ; border: medium outset ; padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title { font-weight: bold ; font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title { color: red ; font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication { margin: 2em 5em ; text-align: center ; font-style: italic }
div.dedication p.topic-title { font-weight: bold ; font-style: normal }
div.figure { margin-left: 2em ; margin-right: 2em }
div.footer, div.header { clear: both; font-size: smaller }
div.line-block { display: block ; margin-top: 1em ; margin-bottom: 1em }
div.line-block div.line-block { margin-top: 0 ; margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar { margin-left: 1em ; border: medium outset ;
padding: 1em ; background-color: #ffffee ; width: 40% ; float: right ;
clear: right }
div.sidebar p.rubric { font-family: sans-serif ; font-size: medium }
div.system-messages { margin: 5em }
div.system-messages h1 { color: red }
div.system-message { border: medium outset ; padding: 1em }
div.system-message p.system-message-title { color: red ; font-weight: bold }
div.topic { margin: 2em;}
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title { text-align: center }
h2.subtitle { text-align: center }
hr.docutils { width: 75% }
img.align-left { clear: left }
img.align-right { clear: right }
ol.simple, ul.simple { margin-bottom: 1em }
ol.arabic { list-style: decimal }
ol.loweralpha { list-style: lower-alpha }
ol.upperalpha { list-style: upper-alpha }
ol.lowerroman { list-style: lower-roman }
ol.upperroman { list-style: upper-roman }
p.attribution { text-align: right ; margin-left: 50% }
p.caption { font-style: italic }
p.credits { font-style: italic ; font-size: smaller }
p.label { white-space: nowrap }
p.rubric { font-weight:bold;font-size:larger;color:maroon;text-align:center}
p.sidebar-title {font-family: sans-serif ;font-weight: bold ;font-size: larger }
p.sidebar-subtitle {font-family: sans-serif ; font-weight: bold }
p.topic-title { font-weight: bold }
pre.address { margin-bottom: 0;margin-top:0;font-family:serif;font-size:100% }
pre.literal-block, pre.doctest-block {margin-left: 2em ;margin-right: 2em }
span.classifier {font-family: sans-serif;font-style: oblique }
span.classifier-delimiter {font-family: sans-serif;font-weight: bold }
span.interpreted {font-family: sans-serif }
span.option {white-space: nowrap }
span.pre {white-space: pre }
span.problematic {color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation { border-left: solid 1px gray; margin-left: 1px }
table.docinfo {margin: 2em 4em }
table.docutils {margin-top: 0.5em;margin-bottom: 0.5em }
table.footnote {border-left: solid 1px black;margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {padding-left: 0.5em;padding-right: 0.5em;
vertical-align: top}
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold;text-align: left;white-space: nowrap;padding-left: 0 }
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {font-size: 100% }
ul.auto-toc { list-style-type: none }
/*a.reference { color: #E00000; font-weight:bold;}
a.reference:hover {color: #E00000;background-color: #ffff00;display: margin;
font-weight:bold;}*/
</style>
<script type="text/javascript">
//<![CDATA[
function toggleElem(id) {
var e = document.getElementById(id);
e.style.display = e.style.display == 'none' ? 'block' : 'none';
}
var gOpen = 'none'
function toggleAll() {
gOpen = gOpen == 'none' ? 'block' : 'none';
var i = 1
while (1) {
var e = document.getElementById("m"+i)
if (!e) break;
e.style.display = gOpen
i++;
}
document.getElementById('toggleButton').value =
gOpen == 'none' ? 'Show Details' : 'Hide Details';
}
//]]>
</script>
</head>
<body>
<div class="document" id="documentId">
<h1 class="title">Module cgi</h1>
<div class="navigation">
<p class="topic-title first">Navigation</p>
<ul class="simple">
<li>
<a class="reference" href="#6" id="56">Types</a>
<ul class="simple">
<li><a class="reference" href="#103">ECgi</a></li>
<li><a class="reference" href="#104">TRequestMethod</a></li>
</ul>
</li>
<li>
<a class="reference" href="#9" id="59">Procs</a>
<ul class="simple">
<li><a class="reference" href="#101">URLencode</a></li>
<li><a class="reference" href="#102">URLdecode</a></li>
<li><a class="reference" href="#105">readData</a></li>
<li><a class="reference" href="#106">validateData</a></li>
<li><a class="reference" href="#107">getContentLength</a></li>
<li><a class="reference" href="#108">getContentType</a></li>
<li><a class="reference" href="#109">getDocumentRoot</a></li>
<li><a class="reference" href="#110">getGatewayInterface</a></li>
<li><a class="reference" href="#111">getHttpAccept</a></li>
<li><a class="reference" href="#112">getHttpAcceptCharset</a></li>
<li><a class="reference" href="#113">getHttpAccept Encoding</a></li>
<li><a class="reference" href="#114">getHttpAccept Language</a></li>
<li><a class="reference" href="#115">getHttpConnection</a></li>
<li><a class="reference" href="#116">getHttpCookie</a></li>
<li><a class="reference" href="#117">getHttpHost</a></li>
<li><a class="reference" href="#118">getHttpReferer</a></li>
<li><a class="reference" href="#119">getHttpUserAgent</a></li>
<li><a class="reference" href="#120">getPathInfo</a></li>
<li><a class="reference" href="#121">getPathTranslated</a></li>
<li><a class="reference" href="#122">getQueryString</a></li>
<li><a class="reference" href="#123">getRemoteAddr</a></li>
<li><a class="reference" href="#124">getRemoteHost</a></li>
<li><a class="reference" href="#125">getRemoteIdent</a></li>
<li><a class="reference" href="#126">getRemotePort</a></li>
<li><a class="reference" href="#127">getRemoteUser</a></li>
<li><a class="reference" href="#128">getRequestMethod</a></li>
<li><a class="reference" href="#129">getRequestURI</a></li>
<li><a class="reference" href="#130">getScriptFilename</a></li>
<li><a class="reference" href="#131">getScriptName</a></li>
<li><a class="reference" href="#132">getServerAddr</a></li>
<li><a class="reference" href="#133">getServerAdmin</a></li>
<li><a class="reference" href="#134">getServerName</a></li>
<li><a class="reference" href="#135">getServerPort</a></li>
<li><a class="reference" href="#136">getServerProtocol</a></li>
<li><a class="reference" href="#137">getServerSignature</a></li>
<li><a class="reference" href="#138">getServerSoftware</a></li>
<li><a class="reference" href="#139">setTestData</a></li>
<li><a class="reference" href="#140">writeContentType</a></li>
</ul>
</li>
</ul>
</div>
<div class="content">
This module implements helper procs for CGI applictions. Example:<pre>
<span class="Keyword">import</span> <span class="Identifier">strtabs</span><span class="Punctation">,</span> <span class="Identifier">cgi</span>
<span class="Comment"># Fill the values when debugging:</span>
<span class="Keyword">when</span> <span class="Identifier">debug</span><span class="Punctation">:</span>
<span class="Identifier">setTestData</span><span class="Punctation">(</span><span class="StringLit">&quot;name&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;Klaus&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;password&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;123456&quot;</span><span class="Punctation">)</span>
<span class="Comment"># read the data into `myData`</span>
<span class="Keyword">var</span> <span class="Identifier">myData</span> <span class="Operator">=</span> <span class="Identifier">readData</span><span class="Punctation">(</span><span class="Punctation">)</span>
<span class="Comment"># check that the data's variable names are &quot;name&quot; or &quot;passwort&quot;</span>
<span class="Identifier">validateData</span><span class="Punctation">(</span><span class="Identifier">myData</span><span class="Punctation">,</span> <span class="StringLit">&quot;name&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;password&quot;</span><span class="Punctation">)</span>
<span class="Comment"># start generating content:</span>
<span class="Identifier">writeContentType</span><span class="Punctation">(</span><span class="Punctation">)</span>
<span class="Comment"># generate content:</span>
<span class="Identifier">write</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">&quot;&lt;!DOCTYPE HTML PUBLIC </span><span class="EscapeSequence">\&quot;</span>-//W3C//DTD HTML 4.01//EN<span class="EscapeSequence">\&quot;</span><span class="EscapeSequence">&gt;\n</span><span class="StringLit">&quot;</span><span class="Punctation">)</span>
<span class="Identifier">write</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">&quot;&lt;html&gt;&lt;head&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt;&lt;body&gt;</span><span class="EscapeSequence">\n</span><span class="StringLit">&quot;</span><span class="Punctation">)</span>
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">&quot;your name: &quot;</span> <span class="Operator">&amp;</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">&quot;name&quot;</span><span class="Punctation">]</span><span class="Punctation">)</span>
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">&quot;your password: &quot;</span> <span class="Operator">&amp;</span> <span class="Identifier">myData</span><span class="Punctation">[</span><span class="StringLit">&quot;password&quot;</span><span class="Punctation">]</span><span class="Punctation">)</span>
<span class="Identifier">writeln</span><span class="Punctation">(</span><span class="Identifier">stdout</span><span class="Punctation">,</span> <span class="StringLit">&quot;&lt;/body&gt;&lt;/html&gt;&quot;</span><span class="Punctation">)</span></pre>
<div class="section" id="6">
<h1><a class="toc-backref" href="#56">Types</a></h1>
<dl class="item">
<dt id="103"><pre><span class="Identifier">ECgi</span><span class="Operator">*</span> <span class="Other">=</span> <span class="Keyword">object</span> <span class="Keyword">of</span> <span class="Identifier">EIO</span></pre></dt>
<dd>
the exception that is raised, if a CGI error occurs
</dd>
<dt id="104"><pre><span class="Identifier">TRequestMethod</span><span class="Operator">*</span> <span class="Other">=</span> <span class="Keyword">enum</span>
<span class="Identifier">methodPost</span><span class="Other">,</span> <span class="Comment">## query uses the POST method</span>
<span class="Identifier">methodGet</span> <span class="Comment">## query uses the GET method</span></pre></dt>
<dd>
the used request method
</dd>
</dl></div>
<div class="section" id="9">
<h1><a class="toc-backref" href="#59">Procs</a></h1>
<dl class="item">
<dt id="101"><pre><span class="Keyword">proc</span> <span class="Identifier">URLencode</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">s</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
Encodes a value to be HTTP safe: This means that characters in the set <tt class="docutils literal"><span class="pre">{'A'..'Z', 'a'..'z', '0'..'9', '_'}</span></tt> are carried over to the result, a space is converted to <tt class="docutils literal"><span class="pre">'+'</span></tt> and every other character is encoded as <tt class="docutils literal"><span class="pre">'%xx'</span></tt> where <tt class="docutils literal"><span class="pre">xx</span></tt> denotes its hexadecimal value.
</dd>
<dt id="102"><pre><span class="Keyword">proc</span> <span class="Identifier">URLdecode</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">s</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
Decodes a value from its HTTP representation: This means that a <tt class="docutils literal"><span class="pre">'+'</span></tt> is converted to a space, <tt class="docutils literal"><span class="pre">'%xx'</span></tt> (where <tt class="docutils literal"><span class="pre">xx</span></tt> denotes a hexadecimal value) is converted to the character with ordinal number <tt class="docutils literal"><span class="pre">xx</span></tt>, and and every other character is carried over.
</dd>
<dt id="105"><pre><span class="Keyword">proc</span> <span class="Identifier">readData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">allowedMethods</span><span class="Other">:</span> <span class="Identifier">set</span><span class="Other">[</span><span class="Identifier">TRequestMethod</span><span class="Other">]</span> <span class="Other">=</span> <span class="Other">{</span><span class="Identifier">methodPost</span><span class="Other">,</span> <span class="Identifier">methodGet</span><span class="Other">}</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">PStringTable</span></pre></dt>
<dd>
Read CGI data. If the client does not use a method listed in the <cite>allowedMethods</cite> set, an <cite>ECgi</cite> exception is raised.
</dd>
<dt id="106"><pre><span class="Keyword">proc</span> <span class="Identifier">validateData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">data</span><span class="Other">:</span> <span class="Identifier">PStringTable</span><span class="Other">,</span> <span class="Identifier">validKeys</span><span class="Other">:</span> <span class="Identifier">openarray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span></pre></dt>
<dd>
validates data; raises <cite>ECgi</cite> if this fails. This checks that each variable name of the CGI <cite>data</cite> occurs in the <cite>validKeys</cite> array.
</dd>
<dt id="107"><pre><span class="Keyword">proc</span> <span class="Identifier">getContentLength</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">CONTENT_LENGTH</span></tt> environment variable
</dd>
<dt id="108"><pre><span class="Keyword">proc</span> <span class="Identifier">getContentType</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">CONTENT_TYPE</span></tt> environment variable
</dd>
<dt id="109"><pre><span class="Keyword">proc</span> <span class="Identifier">getDocumentRoot</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">DOCUMENT_ROOT</span></tt> environment variable
</dd>
<dt id="110"><pre><span class="Keyword">proc</span> <span class="Identifier">getGatewayInterface</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">GATEWAY_INTERFACE</span></tt> environment variable
</dd>
<dt id="111"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAccept</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT</span></tt> environment variable
</dd>
<dt id="112"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptCharset</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_CHARSET</span></tt> environment variable
</dd>
<dt id="113"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptEncoding</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_ENCODING</span></tt> environment variable
</dd>
<dt id="114"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpAcceptLanguage</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_ACCEPT_LANGUAGE</span></tt> environment variable
</dd>
<dt id="115"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpConnection</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_CONNECTION</span></tt> environment variable
</dd>
<dt id="116"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpCookie</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_COOKIE</span></tt> environment variable
</dd>
<dt id="117"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpHost</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_HOST</span></tt> environment variable
</dd>
<dt id="118"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpReferer</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_REFERER</span></tt> environment variable
</dd>
<dt id="119"><pre><span class="Keyword">proc</span> <span class="Identifier">getHttpUserAgent</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">HTTP_USER_AGENT</span></tt> environment variable
</dd>
<dt id="120"><pre><span class="Keyword">proc</span> <span class="Identifier">getPathInfo</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">PATH_INFO</span></tt> environment variable
</dd>
<dt id="121"><pre><span class="Keyword">proc</span> <span class="Identifier">getPathTranslated</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">PATH_TRANSLATED</span></tt> environment variable
</dd>
<dt id="122"><pre><span class="Keyword">proc</span> <span class="Identifier">getQueryString</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">QUERY_STRING</span></tt> environment variable
</dd>
<dt id="123"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteAddr</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_ADDR</span></tt> environment variable
</dd>
<dt id="124"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteHost</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_HOST</span></tt> environment variable
</dd>
<dt id="125"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteIdent</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_IDENT</span></tt> environment variable
</dd>
<dt id="126"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemotePort</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_PORT</span></tt> environment variable
</dd>
<dt id="127"><pre><span class="Keyword">proc</span> <span class="Identifier">getRemoteUser</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REMOTE_USER</span></tt> environment variable
</dd>
<dt id="128"><pre><span class="Keyword">proc</span> <span class="Identifier">getRequestMethod</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REQUEST_METHOD</span></tt> environment variable
</dd>
<dt id="129"><pre><span class="Keyword">proc</span> <span class="Identifier">getRequestURI</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">REQUEST_URI</span></tt> environment variable
</dd>
<dt id="130"><pre><span class="Keyword">proc</span> <span class="Identifier">getScriptFilename</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SCRIPT_FILENAME</span></tt> environment variable
</dd>
<dt id="131"><pre><span class="Keyword">proc</span> <span class="Identifier">getScriptName</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SCRIPT_NAME</span></tt> environment variable
</dd>
<dt id="132"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerAddr</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_ADDR</span></tt> environment variable
</dd>
<dt id="133"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerAdmin</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_ADMIN</span></tt> environment variable
</dd>
<dt id="134"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerName</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_NAME</span></tt> environment variable
</dd>
<dt id="135"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerPort</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_PORT</span></tt> environment variable
</dd>
<dt id="136"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerProtocol</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_PROTOCOL</span></tt> environment variable
</dd>
<dt id="137"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerSignature</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_SIGNATURE</span></tt> environment variable
</dd>
<dt id="138"><pre><span class="Keyword">proc</span> <span class="Identifier">getServerSoftware</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">string</span></pre></dt>
<dd>
returns contents of the <tt class="docutils literal"><span class="pre">SERVER_SOFTWARE</span></tt> environment variable
</dd>
<dt id="139"><pre><span class="Keyword">proc</span> <span class="Identifier">setTestData</span><span class="Operator">*</span><span class="Other">(</span><span class="Identifier">keysvalues</span><span class="Other">:</span> <span class="Identifier">openarray</span><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span></pre></dt>
<dd>
fills the appropriate environment variables to test your CGI application. This can only simulate the 'GET' 'REQUEST_METHOD'. <cite>keysvalues</cite> should provide embedded (name, value)-pairs. Example:<pre><span class="Identifier">setTestData</span><span class="Punctation">(</span><span class="StringLit">&quot;name&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;Hanz&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;password&quot;</span><span class="Punctation">,</span> <span class="StringLit">&quot;12345&quot;</span><span class="Punctation">)</span></pre>
</dd>
<dt id="140"><pre><span class="Keyword">proc</span> <span class="Identifier">writeContentType</span><span class="Operator">*</span><span class="Other">(</span><span class="Other">)</span></pre></dt>
<dd>
call this before starting to send your HTML data to <cite>stdout</cite>. This is just a shorthand for: .. code-block:: Nimrod<blockquote>write(stdout, &quot;Content-type: text/htmlnn&quot;)</blockquote>
</dd>
</dl></div>
</div>
<small>Generated: 2008-12-08 20:29:49 UTC</small>
</div>
</body>
</html>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,18 +7,6 @@
# distribution, for details about the copyright.
#
# This file was created by a complicated procedure which saved me a considerable
# amount of time: the pcre.h header was converted to modpcre.h by hand, so that
# h2pas could handle it. Then I used pas2mor to generate a Nimrod binding.
# Unfortunately, I had to fix some things later on; thus don't do all this
# again! My manual changes will be lost!
# Converted by Pas2mor v1.37
#
# Automatically converted by H2Pas 0.99.16 from modpcre.h
# The following command line parameters were used:
# -D -c -l pcre.lib -T modpcre.h
{.compile: "pcre_all.c" .}
type

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2006 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -35,7 +35,7 @@ const
proc match*(s, pattern: string, matches: var openarray[string],
start: int = 0): bool
## returns ``true`` if ``s`` matches the ``pattern[start..]`` and
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
## the captured substrings in the array ``matches``. If it does not
## match, nothing is written into ``matches`` and ``false`` is
## returned.
@ -113,3 +113,42 @@ proc match(s, pattern: string, start: int = 0): bool =
proc find(s, pattern: string, start: int = 0): bool =
return matchOrFind(s, rawCompile(pattern, PCRE_MULTILINE), start) >= 0'i32
template `=~` *(s, pattern: expr): expr =
## This calls ``match`` with an implicit declared ``matches`` array that
## can be used in the scope of the ``=~`` call:
##
## .. code-block:: nimrod
##
## if line =~ r"\s*(\w+)\s*\=\s*(\w+)":
## # matches a key=value pair:
## echo("Key: ", matches[1])
## echo("Value: ", matches[2])
## elif line =~ r"\s*(\#.*)":
## # matches a comment
## # note that the implicit ``matches`` array is different from the
## # ``matches`` array of the first branch
## echo("comment: ", matches[1])
## else:
## echo("syntax error")
##
var matches: array[0..maxSubPatterns-1, string]
match(s, pattern, matches)
const ## common regular expressions
reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b" ## describes an identifier
reNatural* = r"\b\d+\b" ## describes a natural number
reInteger* = r"\b[-+]?\d+\b" ## describes an integer
reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number
reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101)
reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777)
reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
## describes a floating point number
reEmail* = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)" &
r"*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+(?:[a-zA-Z]{2}|com|org|" &
r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b"
## describes a common email address
reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" &
r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
## describes an URL

View file

@ -1,6 +1,6 @@
=======================================================
The Nimrod Runtime Library
Copyright (C) 2004-2007 Andreas Rumpf
Copyright (C) 2004-2009 Andreas Rumpf
=======================================================
This is the file copying.txt, it applies to the Nimrod Run-Time Library

View file

@ -348,9 +348,9 @@ proc dbgStackFrame(s: string, start: int, currFrame: PExtendedFrame) =
proc CommandPrompt() =
# if we return from this routine, user code executes again
var
again: bool = True
again = True
dbgFramePtr = framePtr # for going down and up the stack
dbgDown: int = 0 # how often we did go down
dbgDown = 0 # how often we did go down
while again:
write(stdout, "*** endb| >>")
@ -478,10 +478,10 @@ proc dbgRegisterGlobal(name: cstring, address: pointer,
inc(dbgGlobalData.f.len)
proc endb(line: int) {.compilerproc.} =
# This proc is called before any Nimrod code line!
# This proc is called before every Nimrod code line!
# Thus, it must have as few parameters as possible to keep the
# code size small!
# check if we are at an enabled breakpoint or "in the mood"
# Check if we are at an enabled breakpoint or "in the mood"
framePtr.line = line # this is done here for smaller code size!
if dbgLineHook != nil: dbgLineHook()
case dbgState

View file

@ -58,7 +58,10 @@ when defined(posix):
proc nimLoadLibrary(path: string): TLibHandle =
result = dlopen(path, RTLD_NOW)
if result == nil:
raise newException(EInvalidLibrary, "could not load: " & path)
writeToStdErr("could not load: ")
writeToStdErr(path)
writeToStdErr("\n")
#raise newException(EInvalidLibrary, "could not load: " & path)
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
result = dlsym(lib, name)
@ -84,7 +87,10 @@ elif defined(windows) or defined(dos):
proc nimLoadLibrary(path: string): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(path))
if result == nil:
raise newException(EInvalidLibrary, "could not load: " & path)
writeToStdErr("could not load: ")
writeToStdErr(path)
writeToStdErr("\n")
#raise newException(EInvalidLibrary, "could not load: " & path)
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
result = GetProcAddress(cast[THINSTANCE](lib), name)
@ -118,7 +124,10 @@ elif defined(mac):
NSDestroyObjectFileImage(img)
result = TLibHandle(modul)
if result == nil:
raise newException(EInvalidLibrary, "could not load: " & path)
writeToStdErr("could not load: ")
writeToStdErr(path)
writeToStdErr("\n")
#raise newException(EInvalidLibrary, "could not load: " & path)
proc nimGetProcAddr(lib: TLibHandle, cname: string): TProcAddr =
var

View file

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

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2008 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -15,36 +15,9 @@
# * generational
# Future Improvements:
# * Both dlmalloc and TLSF lack zero-overhead object allocation. Thus, for
# small objects we should use our own allocator.
# * Support for multi-threading. However, locks for the reference counting
# might turn out to be too slow.
# ---------------------------------------------------------------------------
# Interface to TLSF:
const
useTLSF = false # benchmarking showed that *dlmalloc* is faster than *TLSF*
when useTLSF:
{.compile: "tlsf.c".}
proc tlsfUsed: int {.importc: "TLSF_GET_USED_SIZE", noconv.}
proc tlsfMax: int {.importc: "TLSF_GET_MAX_SIZE", noconv.}
proc tlsf_malloc(size: int): pointer {.importc, noconv.}
proc tlsf_free(p: pointer) {.importc, noconv.}
proc tlsf_realloc(p: pointer, size: int): pointer {.importc, noconv.}
else:
# use DL malloc
{.compile: "dlmalloc.c".}
proc tlsfUsed: int {.importc: "dlmalloc_footprint", noconv.}
proc tlsfMax: int {.importc: "dlmalloc_max_footprint", noconv.}
proc tlsf_malloc(size: int): pointer {.importc: "dlmalloc", noconv.}
proc tlsf_free(p: pointer) {.importc: "dlfree", noconv.}
proc tlsf_realloc(p: pointer, size: int): pointer {.
importc: "dlrealloc", noconv.}
# ---------------------------------------------------------------------------
proc getOccupiedMem(): int = return tlsfUsed()
@ -54,38 +27,13 @@ proc getTotalMem(): int = return tlsfMax()
# ---------------------------------------------------------------------------
const
debugGC = false # we wish to debug the GC...
logGC = false
traceGC = false # extensive debugging
reallyDealloc = true # for debugging purposes this can be set to false
cycleGC = true # (de)activate the cycle GC
stressGC = debugGC
# Guess the page size of the system; if it is the
# wrong value, performance may be worse (this is not
# for sure though), but GC still works; must be a power of two!
const
PageShift = if sizeof(pointer) == 4: 12 else: 13
PageSize = 1 shl PageShift # on 32 bit systems 4096
CycleIncrease = 2 # is a multiplicative increase
InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
ZctThreshold = 256 # we collect garbage if the ZCT's size
# reaches this threshold
# this seems to be a good value
const
MemAlignment = 8 # BUGFIX: on AMD64, dlmalloc aligns at 8 byte boundary
BitsPerUnit = sizeof(int)*8
# a "unit" is a word, i.e. 4 bytes
# on a 32 bit system; I do not use the term "word" because under 32-bit
# Windows it is sometimes only 16 bits
BitsPerPage = PageSize div MemAlignment
UnitsPerPage = BitsPerPage div BitsPerUnit
# how many units do we need to describe a page:
# on 32 bit systems this is only 16 (!)
rcIncrement = 0b1000 # so that lowest 3 bits are not touched
# NOTE: Most colors are currently unused
rcBlack = 0b000 # cell is colored black; in use or free
@ -101,40 +49,9 @@ type
TWalkOp = enum
waZctDecRef, waPush, waCycleDecRef
TCell {.pure.} = object
refcount: int # the refcount and some flags
typ: PNimType
when debugGC:
filename: cstring
line: int
PCell = ptr TCell
TFinalizer {.compilerproc.} = proc (self: pointer)
# A ref type can have a finalizer that is called before the object's
# storage is freed.
PPointer = ptr pointer
TByteArray = array[0..1000_0000, byte]
PByte = ptr TByteArray
PString = ptr string
PPageDesc = ptr TPageDesc
TBitIndex = range[0..UnitsPerPage-1]
TPageDesc {.final, pure.} = object
next: PPageDesc # all nodes are connected with this pointer
key: TAddress # start address at bit 0
bits: array[TBitIndex, int] # a bit vector
PPageDescArray = ptr array[0..1000_000, PPageDesc]
TCellSet {.final, pure.} = object
counter, max: int
head: PPageDesc
data: PPageDescArray
PCellArray = ptr array[0..100_000_000, PCell]
TCellSeq {.final, pure.} = object
len, cap: int
d: PCellArray
TGcHeap {.final, pure.} = object # this contains the zero count and
# non-zero count table
mask: TAddress # mask for fast pointer detection
@ -152,7 +69,6 @@ type
tempStack: TCellSeq # temporary stack for recursion elimination
var
gOutOfMem: ref EOutOfMemory
stackBottom: pointer
gch: TGcHeap
cycleThreshold: int = InitialCycleThreshold
@ -171,12 +87,10 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc.}
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.}
proc raiseOutOfMem() {.noreturn.} =
if gOutOfMem == nil:
writeToStdErr("out of memory; cannot even throw an exception")
quit(1)
gOutOfMem.msg = "out of memory"
raise gOutOfMem
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
if (c.refcount and colorMask) != rcZct:
c.refcount = c.refcount and not colorMask or rcZct
add(s, c)
proc cellToUsr(cell: PCell): pointer {.inline.} =
# convert object (=pointer to refcount) to pointer to userdata
@ -198,11 +112,6 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
if result > 0: result = result shr rcShift
else: result = 0
proc gcAlloc(size: int): pointer =
result = tlsf_malloc(size)
if result == nil: raiseOutOfMem()
zeroMem(result, size)
proc GC_disable() = inc(recGcLock)
proc GC_enable() =
if recGcLock > 0: dec(recGcLock)
@ -221,160 +130,10 @@ proc GC_disableMarkAndSweep() =
cycleThreshold = high(cycleThreshold)-1
# set to the max value to suppress the cycle detector
proc nextTry(h, maxHash: int): int {.inline.} =
result = ((5*h) + 1) and maxHash
# For any initial h in range(maxHash), repeating that maxHash times
# generates each int in range(maxHash) exactly once (see any text on
# random-number generation for proof).
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
when BitsPerPage mod BitsPerUnit != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
# ------------------- cell set handling ---------------------------------------
proc inOperator(s: TCellSeq, c: PCell): bool {.inline.} =
for i in 0 .. s.len-1:
if s.d[i] == c: return True
return False
proc add(s: var TCellSeq, c: PCell) {.inline.} =
if s.len >= s.cap:
s.cap = s.cap * 3 div 2
var d = cast[PCellArray](tlsf_malloc(s.cap * sizeof(PCell)))
if d == nil: raiseOutOfMem()
copyMem(d, s.d, s.len * sizeof(PCell))
tlsf_free(s.d)
s.d = d
# BUGFIX: realloc failes on AMD64, sigh...
#s.d = cast[PCellArray](tlsf_realloc(s.d, s.cap * sizeof(PCell)))
#if s.d == nil: raiseOutOfMem()
s.d[s.len] = c
inc(s.len)
proc addZCT(s: var TCellSeq, c: PCell) =
if (c.refcount and colorMask) != rcZct:
c.refcount = c.refcount and not colorMask or rcZct
add(s, c)
proc init(s: var TCellSeq, cap: int = 1024) =
s.len = 0
s.cap = cap
s.d = cast[PCellArray](gcAlloc(cap * sizeof(PCell)))
const
InitCellSetSize = 1024 # must be a power of two!
proc CellSetInit(s: var TCellSet) =
s.data = cast[PPageDescArray](gcAlloc(InitCellSetSize * sizeof(PPageDesc)))
s.max = InitCellSetSize-1
s.counter = 0
s.head = nil
proc CellSetDeinit(s: var TCellSet) =
var it = s.head
while it != nil:
var n = it.next
tlsf_free(it)
it = n
s.head = nil # play it safe here
tlsf_free(s.data)
s.data = nil
s.counter = 0
proc CellSetGet(t: TCellSet, key: TAddress): PPageDesc =
var h = cast[int](key) and t.max
while t.data[h] != nil:
if t.data[h].key == key: return t.data[h]
h = nextTry(h, t.max)
return nil
proc CellSetRawInsert(t: TCellSet, data: PPageDescArray,
desc: PPageDesc) =
var h = cast[int](desc.key) and t.max
while data[h] != nil:
assert(data[h] != desc)
h = nextTry(h, t.max)
assert(data[h] == nil)
data[h] = desc
proc CellSetEnlarge(t: var TCellSet) =
var oldMax = t.max
t.max = ((t.max+1)*2)-1
var n = cast[PPageDescArray](gcAlloc((t.max + 1) * sizeof(PPageDesc)))
for i in 0 .. oldmax:
if t.data[i] != nil:
CellSetRawInsert(t, n, t.data[i])
tlsf_free(t.data)
t.data = n
proc CellSetPut(t: var TCellSet, key: TAddress): PPageDesc =
var h = cast[int](key) and t.max
while true:
var x = t.data[h]
if x == nil: break
if x.key == key: return x
h = nextTry(h, t.max)
if ((t.max+1)*2 < t.counter*3) or ((t.max+1)-t.counter < 4):
CellSetEnlarge(t)
inc(t.counter)
h = cast[int](key) and t.max
while t.data[h] != nil: h = nextTry(h, t.max)
assert(t.data[h] == nil)
# the new page descriptor goes into result
result = cast[PPageDesc](gcAlloc(sizeof(TPageDesc)))
result.next = t.head
result.key = key
t.head = result
t.data[h] = result
# ---------- slightly higher level procs --------------------------------------
proc contains(s: TCellSet, cell: PCell): bool =
var u = cast[TAddress](cell)
var t = CellSetGet(s, u shr PageShift)
if t != nil:
u = (u %% PageSize) /% MemAlignment
result = (t.bits[u /% BitsPerUnit] and (1 shl (u %% BitsPerUnit))) != 0
else:
result = false
proc incl(s: var TCellSet, cell: PCell) =
var u = cast[TAddress](cell)
var t = CellSetPut(s, u shr PageShift)
u = (u %% PageSize) /% MemAlignment
t.bits[u /% BitsPerUnit] = t.bits[u /% BitsPerUnit] or
(1 shl (u %% BitsPerUnit))
proc excl(s: var TCellSet, cell: PCell) =
var u = cast[TAddress](cell)
var t = CellSetGet(s, u shr PageShift)
if t != nil:
u = (u %% PageSize) /% MemAlignment
t.bits[u /% BitsPerUnit] = (t.bits[u /% BitsPerUnit] and
not (1 shl (u %% BitsPerUnit)))
iterator elements(t: TCellSet): PCell {.inline.} =
# while traversing it is forbidden to add pointers to the tree!
var r = t.head
while r != nil:
var i = 0
while i <= high(r.bits):
var w = r.bits[i] # taking a copy of r.bits[i] here is correct, because
# modifying operations are not allowed during traversation
var j = 0
while w != 0: # test all remaining bits for zero
if (w and 1) != 0: # the bit is set!
yield cast[PCell]((r.key shl PageShift) or # +%
(i*%BitsPerUnit+%j) *% MemAlignment)
inc(j)
w = w shr 1
inc(i)
r = r.next
# --------------- end of Cellset routines -------------------------------------
when debugGC:
proc writeCell(msg: CString, c: PCell) =
var kind = -1
@ -450,7 +209,6 @@ proc IsOnStack(p: pointer): bool {.noinline.}
proc forAllChildren(cell: PCell, op: TWalkOp)
proc doOperation(p: pointer, op: TWalkOp)
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp)
proc reprAny(p: pointer, typ: PNimType): string {.compilerproc.}
# we need the prototype here for debugging purposes
proc prepareDealloc(cell: PCell) =
@ -479,13 +237,13 @@ proc decRef(c: PCell) {.inline.} =
if c.refcount <% rcIncrement:
addZCT(gch.zct, c)
elif canBeCycleRoot(c):
possibleRoot(gch, c)
incl(gch.cycleRoots, c)
proc incRef(c: PCell) {.inline.} =
c.refcount = c.refcount +% rcIncrement
if canBeCycleRoot(c):
# OPT: the code generator should special case this
possibleRoot(gch, c)
incl(gch.cycleRoots, c)
proc nimGCref(p: pointer) {.compilerproc, inline.} = incRef(usrToCell(p))
proc nimGCunref(p: pointer) {.compilerproc, inline.} = decRef(usrToCell(p))
@ -534,26 +292,6 @@ proc initGC() =
gch.mask = 0
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
assert(n.kind == nkCase)
var d: int
var a = cast[TAddress](aa)
case n.typ.size
of 1: d = ze(cast[ptr int8](a +% n.offset)^)
of 2: d = ze(cast[ptr int16](a +% n.offset)^)
of 4: d = int(cast[ptr int32](a +% n.offset)^)
else: assert(false)
return d
proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode =
var discr = getDiscriminant(aa, n)
if discr <% n.len:
result = n.sons[discr]
if result == nil: result = n.sons[n.len]
# n.sons[n.len] contains the ``else`` part (but may be nil)
else:
result = n.sons[n.len]
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
var d = cast[TAddress](dest)
case n.kind
@ -749,7 +487,7 @@ proc collectCycles(gch: var TGcHeap) =
CellSetDeinit(gch.cycleRoots)
gch.cycleRoots = newRoots
proc gcMark(p: pointer) = # {.fastcall.} =
proc gcMark(p: pointer) {.noinline.} =
# the addresses are not as objects on the stack, so turn them to objects:
var cell = usrToCell(p)
var c = cast[TAddress](cell)
@ -759,7 +497,7 @@ proc gcMark(p: pointer) = # {.fastcall.} =
incl(gch.stackCells, cell) # yes: mark it
# ----------------- stack management --------------------------------------
# inspired from Smart Eiffel (c)
# inspired from Smart Eiffel
proc stackSize(): int {.noinline.} =
var stackTop: array[0..1, pointer]
@ -885,7 +623,7 @@ else:
# in a platform independant way
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
when true:
when false:
# new version: several C compilers are too smart here
var
max = cast[TAddress](stackBottom)
@ -910,7 +648,7 @@ else:
max = stackBottom
registers: C_JmpBuf # The jmp_buf buffer is in the C stack.
sp: PPointer # Used to traverse the stack and registers assuming
# that `setjmp' will save registers in the C stack.
# that 'setjmp' will save registers in the C stack.
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
sp = cast[ppointer](addr(registers))
while sp <= max:
@ -937,7 +675,7 @@ proc updateZCT() =
dec(L)
d[j] = d[L]
c.refcount = c.refcount and not colorMask
# we have a new cell at position i, so don't increment i
# we have a new cell at position j, so don't increment j
else:
inc(j)
gch.zct.len = L

View file

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

View file

@ -51,6 +51,20 @@ proc classify*(x: float): TFloatClass =
# XXX: fcSubnormal is not detected!
proc binom*(n, k: int): int {.noSideEffect.} =
## computes the binomial coefficient
if k <= 0: return 1
if 2*k > n: return binom(n, n-k)
result = n
for i in countup(2, k):
result = (result * (n + 1 - i)) div i
proc fac*(n: int): int {.noSideEffect.} =
## computes the faculty function
result = 1
for i in countup(2, n):
result = result * i
proc isPowerOfTwo*(x: int): bool {.noSideEffect.} =
## returns true, if x is a power of two, false otherwise.
## Negative numbers are not a power of two.
@ -74,6 +88,25 @@ proc countBits*(n: int32): int {.noSideEffect.}
include cntbits
proc sum*[T](x: openarray[T]): T {.noSideEffect.} =
## computes the sum of the elements in `x`.
## If `x` is empty, 0 is returned.
for i in items(x): result = result + i
proc mean*(x: openarray[float]): float {.noSideEffect.} =
## computes the mean of the elements in `x`.
## If `x` is empty, NaN is returned.
result = sum(x) / toFloat(len(x))
proc variance*(x: openarray[float]): float {.noSideEffect.} =
## computes the mean of the elements in `x`.
## If `x` is empty, NaN is returned.
result = 0.0
var m = mean(x)
for i in 0 .. high(x):
var diff = x[i] - m
result = result + diff*diff
result = result / toFloat(len(x))
when not defined(ECMAScript):
proc random*(max: int): int

View file

@ -360,7 +360,7 @@ proc findSuitableBlock(t: var TLSF, fl, sl: var int): Pbhdr =
proc extractBlockHdr(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
t.matrix[fl][sl] = b.freePtr.next
if t.matrix[fl][sl] != 0:
if t.matrix[fl][sl] != nil:
t.matrix[fl][sl].freePtr.prev = nil
else:
clear_bit(sl, t.slBitmap[fl])

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2006 Andreas Rumpf
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@ -9,8 +9,7 @@
## This module contains various string utility routines.
## See the module `regexprs` for regular expression support.
## All the routines here are avaiable for the EMCAScript target
## too!
## All the routines here are avaiable for the EMCAScript target too!
{.deadCodeElim: on.}
@ -34,12 +33,65 @@ const
Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
## All the characters that count as whitespace.
Letters* = {'A'..'Z', 'a'..'z'}
## the set of letters
Digits* = {'0'..'9'}
## the set of digits
IdentChars* = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
## the set of characters an identifier can consist of
IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
## the set of characters an identifier can start with
strStart* = 0 # this is only for bootstraping
# XXX: remove this someday
nl* = "\n" # this is only for bootstraping XXX: remove this somehow
proc strip*(s: string): string {.noSideEffect.}
## Strips leading and trailing whitespace from `s`.
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
## The `substitution`:idx: operator performs string substitutions in
## `formatstr` and returns a modified `formatstr`. This is often called
## `string interpolation`:idx:.
##
## This is best explained by an example:
##
## .. code-block:: nimrod
## "$1 eats $2." % ["The cat", "fish"]
##
## Results in:
##
## .. code-block:: nimrod
## "The cat eats fish."
##
## The substitution variables (the thing after the ``$``)
## are enumerated from 1 to 9.
## Substitution variables can also be words (that is
## ``[A-Za-z_]+[A-Za-z0-9_]*``) in which case the arguments in `a` with even
## indices are keys and with odd indices are the corresponding values.
## An example:
##
## .. code-block:: nimrod
## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
##
## Results in:
##
## .. code-block:: nimrod
## "The cat eats fish."
##
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
## raised if an ill-formed format string has been passed to the `%` operator.
proc `%` *(formatstr, a: string): string {.noSideEffect.}
## This is the same as ``formatstr % [a]``.
proc addf*(s: var string, formatstr: string, a: openarray[string])
## The same as ``add(s, formatstr % a)``, but more efficient.
proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect.}
## Strips whitespace from `s` and returns the resulting string.
## If `leading` is true, leading whitespace is stripped.
## If `trailing` is true, trailing whitespace is stripped.
proc toLower*(s: string): string {.noSideEffect.}
## Converts `s` into lower case. This works only for the letters A-Z.
@ -65,15 +117,36 @@ proc normalize*(s: string): string {.noSideEffect.}
## Normalizes the string `s`. That means to convert it to lower case and
## remove any '_'. This is needed for Nimrod identifiers for example.
proc findSubStr*(sub, s: string, start: int = 0): int {.noSideEffect.}
proc findSubStr*(sub, s: string, start: int = 0): int {.
noSideEffect, deprecated.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
## this has a different parameter order.
proc findSubStr*(sub: char, s: string, start: int = 0): int {.
noSideEffect, deprecated.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
## this has a different parameter order.
proc findChars*(chars: set[char], s: string, start: int = 0): int {.
noSideEffect, deprecated.}
## Searches for `chars` in `s` starting at position `start`. If `s` contains
## none of the characters in `chars`, -1 is returned.
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
## this has a different parameter order.
proc find*(s, sub: string, start: int = 0): int {.noSideEffect.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
proc findSubStr*(sub: char, s: string, start: int = 0): int {.noSideEffect.}
proc find*(s: string, sub: char, start: int = 0): int {.noSideEffect.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
proc findChars*(chars: set[char], s: string, start: int = 0): int {.noSideEffect.}
proc find*(s: string, chars: set[char], start: int = 0): int {.noSideEffect.}
## Searches for `chars` in `s` starting at position `start`. If `s` contains
## none of the characters in `chars`, -1 is returned.
@ -95,15 +168,15 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
## Splits the string `s` into substrings.
##
## Substrings are separated by a substring containing only `seps`.
## The seperator substrings are not returned in `sub`, nor are they part
## of `sub`.
## Examples::
## Examples:
##
## .. code-block:: nimrod
## for word in split(" this is an example "):
## writeln(stdout, word)
##
## Results in::
## Results in:
##
## .. code-block:: nimrod
## "this"
## "is"
## "an"
@ -123,18 +196,54 @@ iterator split*(s: string, seps: set[char] = Whitespace): string =
while last < len(s) and s[last] not_in seps: inc(last) # BUGFIX!
yield copy(s, first, last-1)
iterator split*(s: string, sep: char): string =
## Splits the string `s` into substrings.
##
## Substrings are separated by the character `sep`.
## Example:
##
## .. code-block:: nimrod
## for word in split(";;this;is;an;;example;;;", ';'):
## writeln(stdout, word)
##
## Results in:
##
## .. code-block:: nimrod
## ""
## ""
## "this"
## "is"
## "an"
## ""
## "example"
## ""
## ""
## ""
##
var last = 0
assert('\0' != sep)
if len(s) > 0:
# `<=` is correct here for the edge cases!
while last <= len(s):
var first = last
while last < len(s) and s[last] != sep: inc(last)
yield copy(s, first, last-1)
inc(last)
iterator splitLines*(s: string): string =
## Splits the string `s` into its containing lines. Each newline
## combination (CR, LF, CR-LF) is supported. The result strings contain
## no trailing ``\n``.
##
## Example::
## Example:
##
## .. code-block:: nimrod
## for line in lines("\nthis\nis\nan\n\nexample\n"):
## writeln(stdout, line)
##
## Results in::
## Results in:
##
## .. code-block:: nimrod
## ""
## "this"
## "is"
@ -164,6 +273,11 @@ proc splitSeq*(s: string, seps: set[char] = Whitespace): seq[string] {.
noSideEffect.}
## The same as `split`, but is a proc that returns a sequence of substrings.
proc splitSeq*(s: string, sep: char): seq[string] {.noSideEffect.} =
## The same as `split`, but is a proc that returns a sequence of substrings.
result = @[]
for sub in split(s, sep): add(result, sub)
proc cmpIgnoreCase*(a, b: string): int {.noSideEffect.}
## Compares two strings in a case insensitive manner. Returns:
##
@ -207,7 +321,7 @@ proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect.}
## Parses a decimal integer value contained in `s`. If `s` is not
## a valid integer, `EInvalidValue` is raised.
proc ParseFloat*(s: string): float {.noSideEffect.}
proc ParseFloat*(s: string, start = 0): float {.noSideEffect.}
## Parses a decimal floating point value contained in `s`. If `s` is not
## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
## ``INF``, ``-INF`` are also supported (case insensitive comparison).
@ -217,37 +331,6 @@ proc ParseFloat*(s: string): float {.noSideEffect.}
proc toString*[Ty](x: Ty): string
## This generic proc is the same as the stringify operator `$`.
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
## The substitution operator performs string substitutions in `formatstr`
## and returns the modified `formatstr`.
##
## This is best explained by an example::
##
## "$1 eats $2." % ["The cat", "fish"]
##
## Results in::
##
## "The cat eats fish."
##
## The substitution variables (the thing after the ``$``)
## are enumerated from 1 to 9.
## Substitution variables can also be words (that is
## ``[A-Za-z_]+[A-Za-z0-9_]*``) in which case the arguments in `a` with even
## indices are keys and with odd indices are the corresponding values. Again
## an example::
##
## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
##
## Results in::
##
## "The cat eats fish."
##
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
## raised if an ill-formed format string has been passed to the `%` operator.
proc `%` *(formatstr, a: string): string {.noSideEffect.}
## This is the same as `formatstr % [a]`.
proc repeatChar*(count: int, c: Char = ' '): string
## Returns a string of length `count` consisting only of
## the character `c`.
@ -260,7 +343,25 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect.}
## Returns true iff ``s`` ends with ``suffix``.
## If ``suffix == ""`` true is returned.
# implementation
proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
inline.} =
## A shorthand for:
##
## .. code-block:: nimrod
## if dest.len > startLen: add(dest, sep)
##
## This is often useful for generating some code where the items need to
## be *separated* by `sep`. `sep` is only added if `dest` is longer than
## `startLen`. The following example creates a string describing
## an array of integers:
##
## .. code-block:: nimrod
## var arr = "["
## for x in items([2, 3, 5, 7, 11]):
## addSep(arr, startLen=len("["))
## add(arr, $x)
## add(arr, "]")
if dest.len > startLen: add(dest, sep)
proc allCharsInSet*(s: string, theSet: TCharSet): bool =
## returns true iff each character of `s` is in the set `theSet`.
@ -271,7 +372,7 @@ proc allCharsInSet*(s: string, theSet: TCharSet): bool =
proc quoteIfContainsWhite*(s: string): string =
## returns ``'"' & s & '"'`` if `s` contains a space and does not
## start with a quote, else returns `s`
if findChars({' ', '\t'}, s) >= 0 and s[0] != '"':
if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
result = '"' & s & '"'
else:
result = s
@ -307,10 +408,8 @@ proc intToStr(x: int, minchars: int = 1): string =
proc toString[Ty](x: Ty): string = return $x
proc toOctal(c: char): string =
var
val: int
result = newString(3)
val = ord(c)
var val = ord(c)
for i in countdown(2, 0):
result[i] = Chr(val mod 8 + ord('0'))
val = val div 8
@ -326,18 +425,15 @@ proc findNormalized(x: string, inArray: openarray[string]): int =
# security whole ...
return -1
proc `%`(formatstr: string, a: openarray[string]): string =
# the format operator
const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
result = ""
proc addf(s: var string, formatstr: string, a: openarray[string]) =
const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
var i = 0
while i < len(formatstr):
if formatstr[i] == '$':
case formatstr[i+1] # again we use the fact that strings
# are zero-terminated here
of '$':
add result, '$'
add s, '$'
inc(i, 2)
of '1'..'9':
var j = 0
@ -345,26 +441,30 @@ proc `%`(formatstr: string, a: openarray[string]): string =
while formatstr[i] in {'0'..'9'}:
j = j * 10 + ord(formatstr[i]) - ord('0')
inc(i)
add result, a[j - 1]
add s, a[j - 1]
of '{':
var j = i+1
while formatstr[j] notin {'\0', '}'}: inc(j)
var x = findNormalized(copy(formatstr, i+2, j-1), a)
if x >= 0 and x < high(a): add result, a[x+1]
if x >= 0 and x < high(a): add s, a[x+1]
else: raise newException(EInvalidValue, "invalid format string")
i = j+1
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
var j = i+1
while formatstr[j] in PatternChars: inc(j)
var x = findNormalized(copy(formatstr, i+1, j-1), a)
if x >= 0 and x < high(a): add result, a[x+1]
if x >= 0 and x < high(a): add s, a[x+1]
else: raise newException(EInvalidValue, "invalid format string")
i = j
else: raise newException(EInvalidValue, "invalid format string")
else:
add result, formatstr[i]
add s, formatstr[i]
inc(i)
proc `%`(formatstr: string, a: openarray[string]): string =
result = ""
addf(result, formatstr, a)
proc cmpIgnoreCase(a, b: string): int =
# makes usage of the fact that strings are zero-terminated
for i in 0..len(a)-1:
@ -377,9 +477,8 @@ proc cmpIgnoreCase(a, b: string): int =
# thus we compile without checks here
proc cmpIgnoreStyle(a, b: string): int =
var
i = 0
j = 0
var i = 0
var j = 0
while True:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
@ -400,14 +499,16 @@ proc splitSeq(s: string, seps: set[char]): seq[string] =
# ---------------------------------------------------------------------------
proc strip(s: string): string =
proc strip(s: string, leading = true, trailing = true): string =
const
chars: set[Char] = Whitespace
var
first = 0
last = len(s)-1
while s[first] in chars: inc(first)
while last >= 0 and s[last] in chars: dec(last)
if leading:
while s[first] in chars: inc(first)
if trailing:
while last >= 0 and s[last] in chars: dec(last)
result = copy(s, first, last)
proc toLower(c: Char): Char =
@ -451,7 +552,7 @@ proc preprocessSub(sub: string, a: var TSkipTable) =
for i in 0..0xff: a[chr(i)] = m+1
for i in 0..m-1: a[sub[i]] = m-i
proc findSubStrAux(sub, s: string, start: int, a: TSkipTable): int =
proc findSubStrAux(s, sub: string, start: int, a: TSkipTable): int =
# fast "quick search" algorithm:
var
m = len(sub)
@ -469,7 +570,7 @@ proc findSubStrAux(sub, s: string, start: int, a: TSkipTable): int =
proc findSubStr(sub, s: string, start: int = 0): int =
var a: TSkipTable
preprocessSub(sub, a)
result = findSubStrAux(sub, s, start, a)
result = findSubStrAux(s, sub, start, a)
# slow linear search:
#var
# i, j, M, N: int
@ -492,6 +593,20 @@ proc findSubStr(sub, s: string, start: int = 0): int =
# elif (i >= N):
# return -1
proc find(s, sub: string, start: int = 0): int =
var a: TSkipTable
preprocessSub(sub, a)
result = findSubStrAux(s, sub, start, a)
proc find(s: string, sub: char, start: int = 0): int =
for i in start..len(s)-1:
if sub == s[i]: return i
return -1
proc find(s: string, chars: set[char], start: int = 0): int =
for i in start..s.len-1:
if s[i] in chars: return i
return -1
proc findSubStr(sub: char, s: string, start: int = 0): int =
for i in start..len(s)-1:
@ -504,23 +619,21 @@ proc findChars(chars: set[char], s: string, start: int = 0): int =
return -1
proc contains(s: string, chars: set[char]): bool =
return findChars(chars, s) >= 0
return find(s, chars) >= 0
proc contains(s: string, c: char): bool =
return findSubStr(c, s) >= 0
return find(s, c) >= 0
proc contains(s, sub: string): bool =
return findSubStr(sub, s) >= 0
return find(s, sub) >= 0
proc replaceStr(s, sub, by: string): string =
var
i, j: int
a: TSkipTable
var a: TSkipTable
result = ""
preprocessSub(sub, a)
i = 0
var i = 0
while true:
j = findSubStrAux(sub, s, i, a)
var j = findSubStrAux(s, sub, i, a)
if j < 0: break
add result, copy(s, i, j - 1)
add result, by
@ -583,7 +696,10 @@ proc rawParseInt(s: string, index: var int): BiggestInt =
while s[i] == '_':
inc(i) # underscores are allowed and ignored
result = result * sign
index = i # store index back
if s[i] == '\0':
index = i # store index back
else:
index = -1 # BUGFIX: error!
else:
index = -1
@ -602,17 +718,17 @@ proc parseInt(s: string): int =
result = int(res) # convert to smaller integer type
proc ParseBiggestInt(s: string): biggestInt =
var
index: int = 0
var index = 0
result = rawParseInt(s, index)
if index == -1:
raise newException(EInvalidValue, "invalid integer: " & s)
proc ParseFloat(s: string): float =
proc ParseFloat(s: string, start = 0): float =
var
esign = 1.0
sign = 1.0
exponent, i: int
i = start
exponent: int
flags: int
result = 0.0
if s[i] == '+': inc(i)
@ -677,7 +793,7 @@ proc ParseFloat(s: string): float =
proc toOct*(x: BiggestInt, len: int): string =
## converts `x` into its octal representation. The resulting string is
## always `len` characters long. No leading ``0c`` prefix is generated.
## always `len` characters long. No leading ``0o`` prefix is generated.
var
mask: BiggestInt = 7
shift: BiggestInt = 0
@ -701,7 +817,7 @@ proc toBin*(x: BiggestInt, len: int): string =
shift = shift + 1
mask = mask shl 1
proc escape*(s: string, prefix, suffix = "\""): string =
proc escape*(s: string, prefix = "\"", suffix = "\""): string =
## Escapes a string `s`. This does these operations (at the same time):
## * replaces any ``\`` by ``\\``
## * replaces any ``'`` by ``\'``
@ -723,8 +839,34 @@ proc escape*(s: string, prefix, suffix = "\""): string =
else: add(result, c)
add(result, suffix)
proc validEmailAddress*(s: string): bool =
## returns true if `s` seems to be a valid e-mail address.
## The checking also uses a domain list.
const
chars = Letters + Digits + {'!','#','$','%','&',
'\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
var i = 0
if s[i] notin chars or s[i] == '.': return false
while s[i] in chars:
if s[i] == '.' and s[i+1] == '.': return false
inc(i)
if s[i] != '@': return false
var j = len(s)-1
if s[j] notin letters: return false
while j >= i and s[j] in letters: dec(j)
inc(i) # skip '@'
while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
if s[i] != '\0': return false
var x = copy(s, j+1)
if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true
case toLower(x)
of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name",
"aero", "jobs", "museum": return true
return false
proc editDistance*(a, b: string): int =
## returns the edit distance between `s` and `t`. This uses the Levenshtein
## returns the edit distance between `a` and `b`. This uses the Levenshtein
## distance algorithm with only a linear memory overhead. This implementation
## is highly optimized!
var len1 = a.len

View file

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

View file

@ -14,8 +14,8 @@
## explicitly. Because of this there can not be a user-defined module named
## ``system``.
##
## *"The good thing about reinventing the wheel is that you can get a
## round one."*
## *The good thing about reinventing the wheel is that you can get a
## round one.*
{.push hints: off.}
@ -1044,8 +1044,9 @@ proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
# Fixup some magic symbols here:
{.fixup_system.} # This is an undocumented pragma that can only be used
# once in the system module.
#{.fixup_system.}
# This is an undocumented pragma that can only be used
# once in the system module.
proc `&` *[T](x, y: seq[T]): seq[T] {.noSideEffect.} =
newSeq(result, x.len + y.len)
@ -1099,6 +1100,13 @@ proc find*[T, S](a: T, item: S): int {.inline.} =
inc(result)
result = -1
proc pop*[T](s: var seq[T]): T {.inline.} =
## returns the last item of `s` and decreases ``s.len`` by one. This treats
## `s` as a stack and implements the common *pop* operation.
var L = s.len-1
result = s[L]
setLen(s, L)
# ----------------- FPU ------------------------------------------------------
#proc disableFPUExceptions*()
@ -1401,16 +1409,95 @@ when not defined(EcmaScript) and not defined(NimrodVM):
# as it would recurse endlessly!
include arithm
{.pop.} # stack trace
include dyncalls
const
GenericSeqSize = (2 * sizeof(int))
when not defined(boehmgc) and not defined(nogc):
proc reprAny(p: pointer, typ: PNimType): string {.compilerproc.}
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
assert(n.kind == nkCase)
var d: int
var a = cast[TAddress](aa)
case n.typ.size
of 1: d = ze(cast[ptr int8](a +% n.offset)^)
of 2: d = ze(cast[ptr int16](a +% n.offset)^)
of 4: d = int(cast[ptr int32](a +% n.offset)^)
else: assert(false)
return d
proc selectBranch(aa: Pointer, n: ptr TNimNode): ptr TNimNode =
var discr = getDiscriminant(aa, n)
if discr <% n.len:
result = n.sons[discr]
if result == nil: result = n.sons[n.len]
# n.sons[n.len] contains the ``else`` part (but may be nil)
else:
result = n.sons[n.len]
when defined(boehmgc):
const
boehmLib = "/opt/lib/libgc.so"
proc boehmGC_disable {.importc: "GC_disable", dynlib: boehmLib.}
proc boehmGC_enable {.importc: "GC_enable", dynlib: boehmLib.}
proc boehmGCincremental {.
importc: "GC_enable_incremental", dynlib: boehmLib.}
proc boehmGCfullCollect {.importc: "GC_gcollect", dynlib: boehmLib.}
proc boehmAlloc(size: int): pointer {.
importc: "GC_malloc", dynlib: boehmLib.}
proc boehmAllocAtomic(size: int): pointer {.
importc: "GC_malloc_atomic", dynlib: boehmLib.}
proc boehmRealloc(p: pointer, size: int): pointer {.
importc: "GC_realloc", dynlib: boehmLib.}
proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.}
include cellsets
when defined(boehmGC):
proc initGC() = nil
#boehmGCincremental()
proc GC_disable() = boehmGC_disable()
proc GC_enable() = boehmGC_enable()
proc GC_fullCollect() = boehmGCfullCollect()
proc GC_setStrategy(strategy: TGC_Strategy) = nil
proc GC_enableMarkAndSweep() = nil
proc GC_disableMarkAndSweep() = nil
proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = return -1
proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1
proc growObj(old: pointer, newsize: int): pointer {.inline.} =
result = boehmRealloc(old, newsize)
proc newObj(size: int): pointer {.compilerproc.} =
result = boehmAlloc(size)
proc newSeq(baseSize, len: int): pointer {.compilerproc.} =
# XXX: overflow checks!
result = newObj(len * baseSize + GenericSeqSize)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).space = len
proc setStackBottom(theStackBottom: pointer) {.compilerproc.} = nil
proc nimGCref(p: pointer) {.compilerproc, inline.} = nil
proc nimGCunref(p: pointer) {.compilerproc, inline.} = nil
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
dest^ = src
proc asgnRef(dest: ppointer, src: pointer) {.compilerproc, inline.} =
dest^ = src
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerproc, inline.} =
dest^ = src
elif not defined(nogc):
include gc
include sysstr
include assign
include dyncalls
include repr
# we have to implement it here after gentostr for the cstrToNimStrDummy proc

View file

@ -1,72 +0,0 @@
# Memory handling for small objects
const
minRequestSize = 2 * sizeof(pointer) # minimal block is 16 bytes
pageSize = 1024 * sizeof(int)
pageBits = pageSize div minRequestSize
pageMask = pageSize-1
bitarraySize = pageBits div (sizeof(int)*8)
dataSize = pageSize - (bitarraySize+6) * sizeof(pointer)
type
TMinRequest {.final.} = object
next, prev: ptr TMinRequest # stores next free bit
TChunk {.pure.} = object # a chunk manages at least a page
size: int # lowest bit signals if it is a small chunk (0) or
# a big chunk (1)
typ: PNimType
next, prev: ptr TChunk
nextOfSameType: ptr TChunk
TSmallChunk = object of TChunk ## manages pageSize bytes for a type and a
## fixed size
free: int ## index of first free bit
bits: array[0..bitarraySize-1, int]
data: array[0..dataSize div minRequestSize - 1, TMinRequest]
PSmallChunk = ptr TSmallChunk
assert(sizeof(TSmallChunk) == pageSize)
proc getNewChunk(size: int, typ: PNimType): PSmallChunk =
result = cast[PSmallChunk](getPages(1))
result.size = PageSize
result.typ = typ
result.next = chunkHead
result.prev = nil
chunkHead.prev = result
chunkHead = result.next
result.nextOfSameType = cast[PSmallChunk](typ.chunk)
typ.chunk = result
result.free = addr(result.data[0])
result.data[0].next = addr(result.data[1])
result.data[0].prev = nil
result.data[high(result.data)].next = nil
result.data[high(result.data)].prev = addr(result.data[high(result.data)-1])
for i in 1..high(result.data)-1:
result.data[i].next = addr(result.data[i+1])
result.data[i].prev = addr(result.data[i-1])
proc newSmallObj(size: int, typ: PNimType): pointer =
var chunk = cast[PSmallChunk](typ.chunk)
if chunk == nil or chunk.free <= 0:
if chunk.free < 0: GC_collect()
chunk = getNewChunk(size, typ)
chunk.nextOfSameType = typ.chunk
typ.chunk = chunk
var idx = chunk.free
setBit(chunk.bits[idx /% bitarraySize], idx %% bitarraySize)
result = cast[pointer](cast[TAddress](addr(chunk.data)) +
minRequestSize * idx)
var res = cast[PMinRequest](result)
chunk.free = res.next
res.next
proc freeObj(obj: pointer) =
var chunk = cast[PChunk](cast[TAddress(obj) and not pageMask)
if size and 1 == 0: # small chunk
var idx = (cast[TAddress](obj) shr pageShift) div minRequestSize
resetBit(chunk.bits[idx /% bitarraySize], idx %% bitarraySize)

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
@ -45,7 +45,19 @@ function mangleName(s: PSym): PRope;
begin
result := s.loc.r;
if result = nil then begin
result := toRope(mangle(s.name.s));
if gCmd = cmdCompileToLLVM then begin
case s.kind of
skProc, skConverter, skConst: result := toRope('@'+'');
skVar: begin
if (sfGlobal in s.flags) then result := toRope('@'+'')
else result := toRope('%'+'');
end;
skForVar, skTemp, skParam, skType, skEnumField, skModule:
result := toRope('%'+'');
else InternalError(s.info, 'mangleName');
end;
end;
app(result, toRope(mangle(s.name.s)));
app(result, '_'+'');
app(result, toRope(s.id));
if optGenMapping in gGlobalOptions then
@ -58,15 +70,33 @@ end;
function getTypeName(typ: PType): PRope;
begin
if (typ.sym <> nil) and ([sfImportc, sfExportc] * typ.sym.flags <> []) then
if (typ.sym <> nil) and ([sfImportc, sfExportc] * typ.sym.flags <> [])
and (gCmd <> cmdCompileToLLVM) then
result := typ.sym.loc.r
else begin
if typ.loc.r = nil then typ.loc.r := con('TY', toRope(typ.id));
if typ.loc.r = nil then
typ.loc.r := ropeff('TY$1', '%TY$1', [toRope(typ.id)]);
result := typ.loc.r
end;
if result = nil then InternalError('getTypeName: ' + typeKindToStr[typ.kind]);
end;
// ----------------------------- other helpers ----------------------------
(*
function getSizeof(m: BModule; var labels: int;
var body: PRope; typ: PType): PRope;
begin
if (gCmd <> cmdCompileToLLVM) then
result := ropef('sizeof($1)', getTypeDesc(m, typ))
else begin
inc(labels, 2);
result := ropef('%UOC$1', [toRope(labels)]);
appf(body, '%UOC$1 = getelementptr $3* null, %NI 1$n' +
'$2 = cast $3* %UOC$1 to i32$n',
[toRope(labels-1), result, getTypeDesc(m, typ)]);
end
end; *)
// ------------------------------ C type generator ------------------------
function mapType(typ: PType): TCTypeKind;
@ -104,10 +134,6 @@ begin
tyPtr, tyVar, tyRef: begin
case typ.sons[0].kind of
tyOpenArray, tyArrayConstr, tyArray: result := ctArray;
(*tySet: begin
if mapType(typ.sons[0]) = ctArray then result := ctArray
else result := ctPtr
end*)
else result := ctPtr
end
end;
@ -155,6 +181,10 @@ const
// but one can //define it to what you want so there will no problem
'N_INLINE', 'N_NOINLINE', 'N_FASTCALL', 'N_CLOSURE', 'N_NOCONV');
CallingConvToStrLLVM: array [TCallingConvention] of string = ('fastcc $1',
'stdcall $1', 'ccc $1', 'safecall $1', 'syscall $1',
'$1 alwaysinline', '$1 noinline', 'fastcc $1', 'ccc $1', '$1');
function CacheGetType(const tab: TIdTable; key: PType): PRope;
begin
// returns nil if we need to declare this type
@ -165,7 +195,13 @@ end;
function getTempName(): PRope;
begin
result := con('TMP', toRope(gId));
result := ropeff('TMP$1', '%TMP$1', [toRope(gId)]);
inc(gId);
end;
function getGlobalTempName(): PRope;
begin
result := ropeff('TMP$1', '@TMP$1', [toRope(gId)]);
inc(gId);
end;
@ -194,7 +230,8 @@ end;
procedure fillResult(param: PSym);
begin
fillLoc(param.loc, locParam, param.typ, toRope('Result'), OnStack);
fillLoc(param.loc, locParam, param.typ, ropeff('Result', '%Result', []),
OnStack);
if (mapType(param.typ) <> ctArray) and IsInvalidReturnType(param.typ) then
begin
include(param.loc.flags, lfIndirect);
@ -232,7 +269,7 @@ begin
if arr.kind = tyVar then arr := arr.sons[0];
j := 0;
while arr.Kind = tyOpenArray do begin // need to pass hidden parameter:
appf(params, ', NI $1Len$2', [param.loc.r, toRope(j)]);
appff(params, ', NI $1Len$2', ', @NI $1Len$2', [param.loc.r, toRope(j)]);
inc(j);
arr := arr.sons[0]
end;
@ -241,8 +278,9 @@ begin
if (t.sons[0] <> nil) and isInvalidReturnType(t.sons[0]) then begin
if params <> nil then app(params, ', ');
app(params, getTypeDescAux(m, t.sons[0], check));
if mapType(t.sons[0]) <> ctArray then app(params, '*'+'');
app(params, ' Result');
if (mapType(t.sons[0]) <> ctArray) or (gCmd = cmdCompileToLLVM) then
app(params, '*'+'');
appff(params, ' Result', ' @Result', []);
end;
if t.callConv = ccClosure then begin
if params <> nil then app(params, ', ');
@ -252,7 +290,7 @@ begin
if params <> nil then app(params, ', ');
app(params, '...')
end;
if params = nil then
if (params = nil) and (gCmd <> cmdCompileToLLVM) then
app(params, 'void)')
else
app(params, ')'+'');
@ -621,7 +659,7 @@ begin
end
end;
function getTypeDesc(m: BModule; typ: PType): PRope;
function getTypeDesc(m: BModule; typ: PType): PRope; overload;
var
check: TIntSet;
begin
@ -629,6 +667,19 @@ begin
result := getTypeDescAux(m, typ, check);
end;
function getTypeDesc(m: BModule; const magic: string): PRope; overload;
var
sym: PSym;
begin
sym := magicsys.getCompilerProc(magic);
if sym <> nil then
result := getTypeDesc(m, sym.typ)
else begin
rawMessage(errSystemNeeds, magic);
result := nil
end
end;
procedure finishTypeDescriptions(m: BModule);
var
i: int;

View file

@ -20,6 +20,7 @@ uses
function toCChar(c: Char): string;
function makeCString(const s: string): PRope;
function makeLLVMString(const s: string): PRope;
function TableGetType(const tab: TIdTable; key: PType): PObject;
function GetUniqueType(key: PType): PType;
@ -154,6 +155,33 @@ begin
app(result, toRope(res));
end;
function makeLLVMString(const s: string): PRope;
const
MaxLineLength = 64;
var
i: int;
res: string;
begin
result := nil;
res := 'c"';
for i := strStart to length(s)+strStart-1 do begin
if (i-strStart+1) mod MaxLineLength = 0 then begin
app(result, toRope(res));
setLength(res, 0);
end;
case s[i] of
#0..#31, #128..#255, '"', '\': begin
addChar(res, '\');
add(res, toHex(ord(s[i]), 2));
end
else
addChar(res, s[i])
end;
end;
add(res, '\00"');
app(result, toRope(res));
end;
begin
InitTypeTables();
end.

View file

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

View file

@ -77,14 +77,14 @@ const
+{&} ' --debugger:on|off turn Embedded Nimrod Debugger ON|OFF' +{&} nl
+{&} ' -x, --checks:on|off code generation for all runtime checks ON|OFF' +{&} nl
+{&} ' --obj_checks:on|off code generation for obj conversion checks ON|OFF' +{&} nl
+{&} ' --field_checks:on|off code generation for case record fields ON|OFF' +{&} nl
+{&} ' --field_checks:on|off code generation for case variant fields ON|OFF' +{&} nl
+{&} ' --range_checks:on|off code generation for range checks ON|OFF' +{&} nl
+{&} ' --bound_checks:on|off code generation for bound checks ON|OFF' +{&} nl
+{&} ' --overflow_checks:on|off code generation for over-/underflow checks ON|OFF' +{&} nl
+{&} ' -a, --assertions:on|off code generation for assertions ON|OFF' +{&} nl
+{&} ' --dead_code_elim:on|off whole program dead code elimination ON|OFF' +{&} nl
+{&} ' --opt:none|speed|size optimize not at all or for speed|size' +{&} nl
+{&} ' --app:console|gui|lib generate a console|GUI application or a shared lib' +{&} nl
+{&} ' --app:console|gui generate a console|GUI application' +{&} nl
+{&} ' -r, --run run the compiled program with given arguments' +{&} nl
+{&} ' --advanced show advanced command line switches' +{&} nl
+{&} ' -h, --help show this help' +{&} nl
@ -125,7 +125,6 @@ const
+{&} ' --checkpoints:on|off turn on|off checkpoints; for debugging Nimrod' +{&} nl
+{&} ' --skip_cfg do not read the general configuration file' +{&} nl
+{&} ' --skip_proj_cfg do not read the project''s configuration file' +{&} nl
+{&} ' --import:MODULE_FILE import the given module implicitly for each module' +{&} nl
+{&} ' --index:FILE use FILE to generate a documenation index file' +{&} nl
+{&} ' --putenv:key=value set an environment variable' +{&} nl
+{&} ' --list_cmd list the commands used to execute external programs' +{&} nl
@ -506,7 +505,7 @@ begin
expectArg(switch, arg, pass, info);
gErrorMax := parseInt(arg);
end;
else if findSubStr('.', switch) >= strStart then
else if strutils.find(switch, '.') >= strStart then
options.setConfigVar(switch, arg)
else
InvalidCmdLineOption(pass, switch, info)

View file

@ -1,129 +0,0 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit debugids;
interface
{$include 'config.inc'}
uses
nsystem, nos, strutils, ast;
const
idfile = 'debugids.txt';
// This module implements debugging facilities for the ID mechanism.
procedure registerID(s: PSym);
procedure writeIDTable();
procedure loadIDTable();
implementation
type
TIdSymTuple = record{@tuple} // keep id from sym to better detect bugs
id: int;
s: PSym;
end;
TIdSymTupleSeq = array of TIdSymTuple;
TIdSymTable = record
counter: int;
data: TIdSymTupleSeq;
end;
function TableRawGet(const t: TTable; key: PObject): int;
var
h: THash;
begin
h := hashNode(key) and high(t.data); // start with real hash value
while t.data[h].key <> nil do begin
if (t.data[h].key = key) then begin
result := h; exit
end;
h := nextTry(h, high(t.data))
end;
result := -1
end;
function TableSearch(const t: TTable; key, closure: PObject;
comparator: TCmpProc): PObject;
var
h: THash;
begin
h := hashNode(key) and high(t.data); // start with real hash value
while t.data[h].key <> nil do begin
if (t.data[h].key = key) then
if comparator(t.data[h].val, closure) then begin // BUGFIX 1
result := t.data[h].val; exit
end;
h := nextTry(h, high(t.data))
end;
result := nil
end;
function TableGet(const t: TTable; key: PObject): PObject;
var
index: int;
begin
index := TableRawGet(t, key);
if index >= 0 then result := t.data[index].val
else result := nil
end;
procedure TableRawInsert(var data: TPairSeq; key, val: PObject);
var
h: THash;
begin
h := HashNode(key) and high(data);
while data[h].key <> nil do begin
assert(data[h].key <> key);
h := nextTry(h, high(data))
end;
assert(data[h].key = nil);
data[h].key := key;
data[h].val := val;
end;
procedure TableEnlarge(var t: TTable);
var
n: TPairSeq;
i: int;
begin
{@ignore}
n := emptySeq;
setLength(n, length(t.data) * growthFactor);
fillChar(n[0], length(n)*sizeof(n[0]), 0);
{@emit
newSeq(n, length(t.data) * growthFactor); }
for i := 0 to high(t.data) do
if t.data[i].key <> nil then
TableRawInsert(n, t.data[i].key, t.data[i].val);
{@ignore}
t.data := n;
{@emit
swap(t.data, n);
}
end;
procedure TablePut(var t: TTable; key, val: PObject);
var
index: int;
begin
index := TableRawGet(t, key);
if index >= 0 then
t.data[index].val := val
else begin
if mustRehash(length(t.data), t.counter) then TableEnlarge(t);
TableRawInsert(t.data, key, val);
inc(t.counter)
end;
end;
end.

View file

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

View file

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

View file

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

View file

@ -22,7 +22,7 @@ type
llsFile, // stream encapsulates a file
llsStdIn); // stream encapsulates stdin
TLLStream = object(NObject)
kind: TLLStreamKind; // exposed for low-level access (lexbase uses this)
kind: TLLStreamKind; // accessible for low-level access (lexbase uses this)
f: TBinaryFile;
s: string;
pos: int; // for string streams

View file

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

View file

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

View file

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

View file

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

View file

@ -123,7 +123,7 @@ procedure createDir(const dir: string);
var
i: int;
begin
for i := 1 to length(dir) do begin
for i := 2 to length(dir) do begin
if dir[i] in [sep, altsep] then sysutils.createDir(ncopy(dir, 1, i-1));
end;
sysutils.createDir(dir);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -12,7 +12,7 @@ unit platform;
// and operating systems.
// Note: Unfortunately if an OS or CPU is listed here this does not mean that
// Nimrod has been tested on this platform or that the RTL has been ported.
// Feel free to test for your exentric platform!
// Feel free to test for your excentric platform!
interface

View file

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

View file

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

View file

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

View file

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

View file

@ -1967,6 +1967,10 @@ begin
initParser(q, p.s);
q.filename := filename;
getTokens(readFile(path), false, q.tok);
// workaround a GCC bug:
if find(q.tok[high(q.tok)].symbol, #0#1#2) > 0 then begin
InternalError('Too many binary zeros in include file');
end;
result := parseDoc(q);
end
end

View file

@ -428,7 +428,7 @@ begin
L.bufpos := pos; // restore position
try
if (L.buf[pos] = '0') and (L.buf[pos+1] in ['x','X','b','B','o','O'])
if (L.buf[pos] = '0') and (L.buf[pos+1] in ['x','X','b','B','o','O','c','C'])
then begin
inc(pos, 2);
xi := 0;
@ -451,7 +451,7 @@ begin
end
end
end;
'o': begin
'o', 'c', 'C': begin
result.base := base8;
while true do begin
case L.buf[pos] of

View file

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

View file

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

View file

@ -1,7 +1,7 @@
//
//
// The Ethexor Morpork Compiler
// (c) Copyright 2008 Andreas Rumpf
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
@ -87,19 +87,23 @@ begin
end
end;
function isCastable(castDest, src: PType): Boolean;
function isCastable(dst, src: PType): Boolean;
//const
// castableTypeKinds = {@set}[tyInt, tyPtr, tyRef, tyCstring, tyString,
// tySequence, tyPointer, tyNil, tyOpenArray,
// tyProc, tySet, tyEnum, tyBool, tyChar];
var
ds, ss: biggestInt;
begin
// this is very unrestrictive; cast is allowed if castDest.size >= src.size
ds := computeSize(castDest);
ds := computeSize(dst);
ss := computeSize(src);
if ds < 0 then result := false
else if ss < 0 then result := false
else
result := (ds >= ss) or
(castDest.kind in [tyInt..tyFloat128]) or
(src.kind in [tyInt..tyFloat128])
(skipGeneric(dst).kind in [tyInt..tyFloat128]) or
(skipGeneric(src).kind in [tyInt..tyFloat128])
end;
function semConv(c: PContext; n: PNode; s: PSym): PNode;
@ -620,6 +624,11 @@ begin
end;
end;
procedure checkDeprecated(n: PNode; s: PSym);
begin
if sfDeprecated in s.flags then liMessage(n.info, warnDeprecated, s.name.s);
end;
function semSym(c: PContext; n: PNode; s: PSym; flags: TExprFlags): PNode;
begin
result := newSymNode(s);
@ -652,7 +661,8 @@ begin
end
end
else begin end
end
end;
checkDeprecated(n, s);
end;
function isTypeExpr(n: PNode): bool;
@ -787,6 +797,7 @@ begin
result := newSymNode(f);
result.info := n.info;
result.typ := ty;
checkDeprecated(n, f);
end
else
liMessage(n.sons[1].info, errEnumHasNoValueX, i.s);
@ -814,6 +825,7 @@ begin
n.sons[0] := makeDeref(n.sons[0]);
n.sons[1] := newSymNode(f); // we now have the correct field
n.typ := f.typ;
checkDeprecated(n, f);
if check = nil then result := n
else begin
check.sons[0] := n;
@ -831,14 +843,17 @@ begin
n.sons[1] := newSymNode(f);
n.typ := f.typ;
result := n;
checkDeprecated(n, f);
exit
end
end;
// allow things like "".replace(...)
// --> replace("", ...)
f := SymTabGet(c.tab, i);
if (f <> nil) and (f.kind = skStub) then loadStub(f);
if (f <> nil) and (f.kind in [skProc, skIterator]) then begin
//if (f <> nil) and (f.kind = skStub) then loadStub(f);
// XXX ``loadStub`` is not correct here as we don't care for ``f`` really
if (f <> nil) then begin
// BUGFIX: do not check for (f.kind in [skProc, skIterator]) here
result := newNodeI(nkDotCall, n.info);
// This special node kind is to merge with the call handler in `semExpr`.
addSon(result, newIdentNode(i, n.info));
@ -1121,6 +1136,57 @@ begin
result := semFieldAccess(c, n, flags);
end;
function isCallExpr(n: PNode): bool;
begin
result := n.kind in [nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand];
end;
function semMacroStmt(c: PContext; n: PNode): PNode;
var
s: PSym;
a: PNode;
i: int;
begin
checkMinSonsLen(n, 2);
if isCallExpr(n.sons[0]) then
a := n.sons[0].sons[0]
else
a := n.sons[0];
s := qualifiedLookup(c, a, false);
if (s <> nil) then begin
checkDeprecated(n, s);
case s.kind of
skMacro: begin
include(s.flags, sfUsed);
result := semMacroExpr(c, n, s);
end;
skTemplate: begin
include(s.flags, sfUsed);
// transform
// nkMacroStmt(nkCall(a...), stmt, b...)
// to
// nkCall(a..., stmt, b...)
result := newNodeI(nkCall, n.info);
addSon(result, a);
if isCallExpr(n.sons[0]) then begin
for i := 1 to sonsLen(n.sons[0])-1 do
addSon(result, n.sons[0].sons[i]);
end;
for i := 1 to sonsLen(n)-1 do
addSon(result, n.sons[i]);
pushInfoContext(n.info);
result := evalTemplate(c, result, s);
popInfoContext();
end;
else
liMessage(n.info, errXisNoMacroOrTemplate, s.name.s);
end
end
else
liMessage(n.info, errInvalidExpressionX,
renderTree(a, {@set}[renderNoComments]));
end;
function semExpr(c: PContext; n: PNode; flags: TExprFlags = {@set}[]): PNode;
var
s: PSym;
@ -1174,6 +1240,7 @@ begin
checkMinSonsLen(n, 1);
s := qualifiedLookup(c, n.sons[0], false);
if (s <> nil) then begin
checkDeprecated(n, s);
case s.kind of
skMacro: begin
include(s.flags, sfUsed);
@ -1200,6 +1267,9 @@ begin
end
else result := semIndirectOp(c, n);
end;
nkMacroStmt: begin
result := semMacroStmt(c, n);
end;
nkBracketExpr: begin
checkMinSonsLen(n, 1);
s := qualifiedLookup(c, n.sons[0], false);
@ -1221,7 +1291,7 @@ begin
nkPar: begin
case checkPar(n) of
paNone: result := nil;
paTuplePositions: result := semTuplePositionsConstr(c, n);
paTuplePositions: result := semTuplePositionsConstr(c, n);
paTupleFields: result := semTupleFieldsConstr(c, n);
paSingle: result := semExpr(c, n.sons[0]);
end;

View file

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

View file

@ -58,10 +58,29 @@ begin
end
end;
procedure genericToConcreteTypeKind(t: PType);
var
body: PNode;
begin
if (t.kind = tyGeneric) and (t.sym <> nil) then begin
body := t.sym.ast.sons[2];
case body.kind of
nkObjectTy: t.kind := tyObject;
nkTupleTy: t.kind := tyTuple;
nkRefTy: t.kind := tyRef;
nkPtrTy: t.kind := tyPtr;
nkVarTy: t.kind := tyVar;
nkProcTy: t.kind := tyProc;
else InternalError('genericToConcreteTypeKind');
end
end
end;
function instantiateType(c: PInstantiateClosure; typ: PType): PType;
var
i: int;
begin
if typ = nil then begin result := nil; exit end;
result := PType(idTableGet(c.typeMap, typ));
if result <> nil then exit;
//if typ.kind = tyOpenArray then
@ -73,6 +92,7 @@ begin
result.sons[i] := instantiateType(c, result.sons[i]);
if result.n <> nil then
result.n := instTypeNode(c, result.n);
genericToConcreteTypeKind(result);
end
else
result := typ;
@ -272,6 +292,20 @@ begin
result := instantiateType(c, t);
end;
function newInstantiateClosure(p: PContext;
const instantiator: TLineInfo): PInstantiateClosure;
begin
new(result);
{@ignore}
fillChar(result^, sizeof(result^), 0);
{@emit}
InitIdTable(result.typeMap);
InitIdTable(result.symMap);
result.fn := nil;
result.instantiator := instantiator;
result.module := p.module;
end;
function partialSpecialization(c: PContext; n: PNode; s: PSym): PNode;
begin
result := n;

View file

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

View file

@ -164,7 +164,7 @@ var
i, j, len, counter: int;
params, p, paramKind: PNode;
begin
if c.p.owner = nil then begin
if c.p.owner.kind = skModule then begin
s := semIdentVis(c, skTemplate, n.sons[0], {@set}[sfStar]);
include(s.flags, sfGlobal);
end

View file

@ -51,10 +51,12 @@ begin
e := newSymS(skEnumField, n.sons[i].sons[0], c);
v := semConstExpr(c, n.sons[i].sons[1]);
x := getOrdValue(v);
if (x <> counter) and (i <> 1) then
include(result.flags, tfEnumHasWholes);
if x < counter then
liMessage(n.sons[i].info, errInvalidOrderInEnumX, e.name.s);
if i <> 1 then begin
if (x <> counter) then
include(result.flags, tfEnumHasWholes);
if x < counter then
liMessage(n.sons[i].info, errInvalidOrderInEnumX, e.name.s);
end;
counter := x;
end;
nkSym: e := n.sons[i].sym;
@ -247,7 +249,7 @@ begin
end
end
end;
(*
function instGenericAux(c: PContext; templ, actual: PNode;
sym: PSym): PNode;
var
@ -271,34 +273,48 @@ begin
result.sons[i] := instGenericAux(c, templ.sons[i], actual, sym);
end
end
end;
end; *)
function semGeneric(c: PContext; n: PNode; s: PSym; prev: PType): PType;
var
i: int;
elem: PType;
inst: PNode;
cl: PInstantiateClosure;
begin
if (s.typ = nil) or (s.typ.kind <> tyGeneric) then
liMessage(n.info, errCannotInstantiateX, s.name.s);
result := newOrPrevType(tyGenericInst, prev, c); // new ID...
result.containerID := s.typ.containerID; // ... but the same containerID
result.sym := s;
if (s.typ.containerID = 0) then
InternalError(n.info, 'semtypes.semGeneric');
if (s.typ.containerID = 0) then InternalError(n.info, 'semtypes.semGeneric');
cl := newInstantiateClosure(c, n.info);
// check the number of supplied arguments suffices:
if sonsLen(n) <> sonsLen(s.typ) then begin
//MessageOut('n: ' +{&} toString(sonsLen(n)) +{&} ' s: '
// +{&} toString(sonsLen(s.typ)));
liMessage(n.info, errWrongNumberOfTypeParams);
end;
// a generic type should be instantiated with itself:
// idTablePut(cl.typeMap, s.typ, result);
// iterate over arguments:
for i := 1 to sonsLen(n)-1 do begin
elem := semTypeNode(c, n.sons[i], nil);
if elem.kind = tyGenericParam then
result.kind := tyGeneric; // prevend type from instantiation
result.kind := tyGeneric // prevend type from instantiation
else
idTablePut(cl.typeMap, s.typ.sons[i-1], elem);
addSon(result, elem);
end;
if s.ast <> nil then begin
if (result.kind = tyGenericInst) then begin
inst := instGenericAux(c, s.ast.sons[2], n, s);
// inst := instGenericAux(c, s.ast.sons[2], n, s);
internalError(n.info, 'Generic containers not implemented');
// XXX: implementation does not work this way
// we need to do the following:
// traverse and copy the type and replace any tyGenericParam type
// does checking of instantiated type for us:
elem := semTypeNode(c, inst, nil);
elem := instantiateType(cl, s.typ); //semTypeNode(c, inst, nil);
elem.id := result.containerID;
addSon(result, elem);
end

View file

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

View file

@ -269,14 +269,14 @@ begin
j := i+1;
while (j <= length(f)+strStart-1) and (f[j] <> '}') do inc(j);
key := ncopy(f, i+2+strStart-1, j-1+strStart-1);
result := result +{&} getValue(t, flags, key);
add(result, getValue(t, flags, key));
i := j+1
end;
'a'..'z', 'A'..'Z', #128..#255, '_': begin
j := i+1;
while (j <= length(f)+strStart-1) and (f[j] in PatternChars) do inc(j);
key := ncopy(f, i+1+strStart-1, j-1+strStart-1);
result := result +{&} getValue(t, flags, key);
add(result, getValue(t, flags, key));
i := j
end
else begin

View file

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

View file

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

View file

@ -1,12 +1,11 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit types;
// this module contains routines for accessing and iterating over types
@ -47,7 +46,6 @@ function mutateType(t: PType; iter: TTypeMutator; closure: PObject): PType;
// Returns result of `iter`.
function SameType(x, y: PType): Boolean;
function SameTypeOrNil(a, b: PType): Boolean;
@ -115,6 +113,8 @@ function analyseObjectWithTypeField(t: PType): TTypeFieldResult;
// made or intializing of the type field suffices or if there is no type field
// at all in this type.
function typeAllowed(t: PType; kind: TSymKind): bool;
implementation
function InvalidGenericInst(f: PType): bool;
@ -185,11 +185,13 @@ begin
n := sym.typ.n;
for i := 1 to sonsLen(n)-1 do begin
p := n.sons[i];
assert(p.kind = nkSym);
result := result +{&} p.sym.name.s +{&} ': ' +{&} typeToString(p.sym.typ);
if i <> sonsLen(n)-1 then result := result + ', ';
if (p.kind <> nkSym) then InternalError('getProcHeader');
add(result, p.sym.name.s);
add(result, ': ');
add(result, typeToString(p.sym.typ));
if i <> sonsLen(n)-1 then add(result, ', ');
end;
result := result + ')';
addChar(result, ')');
if n.sons[0].typ <> nil then
result := result +{&} ': ' +{&} typeToString(n.sons[0].typ);
end;
@ -928,6 +930,96 @@ begin
end
end;
function typeAllowedAux(var marker: TIntSet; t: PType;
kind: TSymKind): bool; forward;
function typeAllowedNode(var marker: TIntSet; n: PNode; kind: TSymKind): bool;
var
i: int;
begin
result := true;
if n <> nil then begin
result := typeAllowedAux(marker, n.typ, kind);
if result then
case n.kind of
nkNone..nkNilLit: begin end;
else begin
for i := 0 to sonsLen(n)-1 do begin
result := typeAllowedNode(marker, n.sons[i], kind);
if not result then exit
end
end
end
end
end;
function typeAllowedAux(var marker: TIntSet; t: PType; kind: TSymKind): bool;
var
i: int;
begin
assert(kind in [skVar, skConst, skParam]);
result := true;
if t = nil then exit;
// if we have already checked the type, return true, because we stop the
// evaluation if something is wrong:
if IntSetContainsOrIncl(marker, t.id) then exit;
case skipGeneric(t).kind of
tyVar: begin
case skipGeneric(t.sons[0]).kind of
tyVar: result := false; // ``var var`` is always an invalid type:
tyOpenArray: result := (kind = skParam) and
typeAllowedAux(marker, t.sons[0], kind);
else result := (kind <> skConst) and
typeAllowedAux(marker, t.sons[0], kind);
end
end;
tyProc: begin
for i := 1 to sonsLen(t)-1 do begin
result := typeAllowedAux(marker, t.sons[i], skParam);
if not result then exit;
end;
if t.sons[0] <> nil then
result := typeAllowedAux(marker, t.sons[0], skVar)
end;
tyGeneric, tyGenericParam, tyForward, tyNone: result := false;
tyEmpty, tyNil: result := kind = skConst;
tyString, tyBool, tyChar, tyEnum, tyInt..tyFloat128, tyCString, tyPointer:
result := true;
tyAnyEnum: result := kind = skParam;
tyGenericInst: result := typeAllowedAux(marker, lastSon(t), kind);
tyRange: result := skipGeneric(t.sons[0]).kind in
[tyChar, tyEnum, tyInt..tyFloat128];
tyOpenArray:
result := (kind = skParam) and typeAllowedAux(marker, t.sons[0], skVar);
tySequence: result := (kind <> skConst)
and typeAllowedAux(marker, t.sons[0], skVar)
or (t.sons[0].kind = tyEmpty);
tyArray: result := typeAllowedAux(marker, t.sons[1], skVar);
tyPtr, tyRef: result := typeAllowedAux(marker, t.sons[0], skVar);
tyArrayConstr, tyTuple, tySet: begin
for i := 0 to sonsLen(t)-1 do begin
result := typeAllowedAux(marker, t.sons[i], kind);
if not result then exit
end;
end;
tyObject: begin
for i := 0 to sonsLen(t)-1 do begin
result := typeAllowedAux(marker, t.sons[i], skVar);
if not result then exit
end;
if t.n <> nil then result := typeAllowedNode(marker, t.n, skVar)
end
end
end;
function typeAllowed(t: PType; kind: TSymKind): bool;
var
marker: TIntSet;
begin
IntSetInit(marker);
result := typeAllowedAux(marker, t, kind);
end;
function align(address, alignment: biggestInt): biggestInt;
begin
result := (address + (alignment-1)) and not (alignment-1);

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
# Test the case statment
# Test the case statement
type
tenum = enum eA, eB, eC
@ -6,6 +6,7 @@ type
var
x: string
y: Tenum = eA
i: int
case y
of eA: write(stdout, "a\n")
@ -18,3 +19,14 @@ of "aa", "bb": write(stdout, "Du bist nicht mein Meister\n")
of "cc", "hash", "when": nil
of "will", "it", "finally", "be", "generated": nil
else: write(stdout, "das sollte nicht passieren!\N")
case i
of 0..5, 8, 9: nil
of 6, 7: nil
elif x == "Ha":
nil
elif x == "Ho":
nil
else:
nil

View file

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

View file

@ -1,5 +1,14 @@
# test variables of type proc
proc pa() {.cdecl.} = write(stdout, "pa")
proc pb() {.cdecl.} = write(stdout, "pb")
proc pc() {.cdecl.} = write(stdout, "pc")
proc pd() {.cdecl.} = write(stdout, "pd")
proc pe() {.cdecl.} = write(stdout, "pe")
const
algos = [pa, pb, pc, pd, pe]
var
x: proc (a, b: int): int {.cdecl.}
@ -7,8 +16,11 @@ proc ha(c, d: int): int {.cdecl.} =
echo(c + d)
result = c + d
for a in items(algos):
a()
x = ha
discard x(3, 4)
#OUT 7
#OUT papbpcpdpe7

View file

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

View file

@ -4,9 +4,16 @@
import
regexprs
if "keyA = valueA" =~ r"\s*(\w+)\s*\=\s*(\w+)":
write(stdout, "key: ", matches[1])
elif "# comment!" =~ r"\s*(\#.*)":
echo("comment: ", matches[1])
else:
echo("Bug!")
if "Username".match("[A-Za-z]+"):
echo("Yes!")
else:
echo("Bug!")
#OUT Yes!
#OUT key: keyAYes!

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