version 0.7.8
This commit is contained in:
parent
08bc9ac03c
commit
db4f617afc
92 changed files with 3088 additions and 3477 deletions
13
boot.nim
13
boot.nim
|
|
@ -11,7 +11,10 @@ import
|
||||||
os, strutils
|
os, strutils
|
||||||
|
|
||||||
const
|
const
|
||||||
BootCmd = "nimrod cc --compile:build/platdef.c $1 rod/nimrod.nim"
|
BootCmd = [
|
||||||
|
"nimrod cc --compile:build/platdef.c $1 rod/nimrod.nim",
|
||||||
|
"bin/nimrod cc --compile:build/platdef.c $1 rod/nimrod.nim"
|
||||||
|
]
|
||||||
PlatdefCTmpl = """
|
PlatdefCTmpl = """
|
||||||
/* Generated by boot.nim */
|
/* Generated by boot.nim */
|
||||||
char* nimOS(void) { return "$1"; }
|
char* nimOS(void) { return "$1"; }
|
||||||
|
|
@ -32,7 +35,7 @@ proc writePlatdefC =
|
||||||
|
|
||||||
proc rodsrc =
|
proc rodsrc =
|
||||||
const
|
const
|
||||||
blacklist = ["nsystem", "nmath", "nos", "ntime", "strutils"]
|
blacklist = ["nsystem", "nmath", "nos", "osproc", "ntime", "strutils"]
|
||||||
cmd = "nimrod boot --skip_proj_cfg -o:rod/$1.nim nim/$1"
|
cmd = "nimrod boot --skip_proj_cfg -o:rod/$1.nim nim/$1"
|
||||||
for fi in walkFiles("nim/*.pas"):
|
for fi in walkFiles("nim/*.pas"):
|
||||||
var f = extractFileTrunk(fi)
|
var f = extractFileTrunk(fi)
|
||||||
|
|
@ -46,10 +49,10 @@ proc boot(args: string) =
|
||||||
rodsrc()
|
rodsrc()
|
||||||
var newExe = appendFileExt("rod/nimrod", ExeExt)
|
var newExe = appendFileExt("rod/nimrod", ExeExt)
|
||||||
var oldExe = appendFileExt("bin/nimrod", ExeExt)
|
var oldExe = appendFileExt("bin/nimrod", ExeExt)
|
||||||
for i in 1..2:
|
for i in 0..1:
|
||||||
Echo("iteration: ", $i)
|
Echo("iteration: ", $(i+1))
|
||||||
# use the new executable to compile the files in the bootstrap directory:
|
# use the new executable to compile the files in the bootstrap directory:
|
||||||
Exec(Bootcmd % args)
|
Exec(Bootcmd[i] % args)
|
||||||
if sameFileContent(newExe, oldExe):
|
if sameFileContent(newExe, oldExe):
|
||||||
Echo("executables are equal: SUCCESS!")
|
Echo("executables are equal: SUCCESS!")
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,20 @@ hint[LineTooLong]=off
|
||||||
passc = "-cxxlib"
|
passc = "-cxxlib"
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
# Configuration for the GNU C/C++ compiler:
|
||||||
|
@if windows:
|
||||||
|
gcc.path = r"$nimrod\dist\mingw\bin"
|
||||||
|
@end
|
||||||
|
gcc.options.debug = "-g"
|
||||||
|
@if macosx:
|
||||||
|
gcc.options.always = "-w -fasm-blocks"
|
||||||
|
@else:
|
||||||
|
gcc.options.always = "-w"
|
||||||
|
@end
|
||||||
|
gcc.options.speed = "-O3 -fno-strict-aliasing"
|
||||||
|
gcc.options.size = "-Os"
|
||||||
|
#passl = "-pg"
|
||||||
|
|
||||||
# Configuration for the LLVM GCC compiler:
|
# Configuration for the LLVM GCC compiler:
|
||||||
llvm_gcc.options.debug = "-g"
|
llvm_gcc.options.debug = "-g"
|
||||||
llvm_gcc.options.always = "-w"
|
llvm_gcc.options.always = "-w"
|
||||||
|
|
@ -66,19 +80,6 @@ vcc.options.always = "/nologo"
|
||||||
vcc.options.speed = "/Ox /arch:SSE2"
|
vcc.options.speed = "/Ox /arch:SSE2"
|
||||||
vcc.options.size = "/O1"
|
vcc.options.size = "/O1"
|
||||||
|
|
||||||
# Configuration for the GNU C/C++ compiler:
|
|
||||||
@if windows:
|
|
||||||
gcc.path = r"$nimrod\dist\mingw\bin"
|
|
||||||
@end
|
|
||||||
gcc.options.debug = "-g"
|
|
||||||
@if macosx:
|
|
||||||
gcc.options.always = "-w -fasm-blocks"
|
|
||||||
@else:
|
|
||||||
gcc.options.always = "-w"
|
|
||||||
@end
|
|
||||||
gcc.options.speed = "-O3 -fno-strict-aliasing"
|
|
||||||
gcc.options.size = "-Os"
|
|
||||||
|
|
||||||
# Configuration for the Digital Mars C/C++ compiler:
|
# Configuration for the Digital Mars C/C++ compiler:
|
||||||
@if windows:
|
@if windows:
|
||||||
dmc.path = r"$nimrod\dist\dm\bin"
|
dmc.path = r"$nimrod\dist\dm\bin"
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ Advanced options:
|
||||||
--checkpoints:on|off turn on|off checkpoints; for debugging Nimrod
|
--checkpoints:on|off turn on|off checkpoints; for debugging Nimrod
|
||||||
--skip_cfg do not read the general configuration file
|
--skip_cfg do not read the general configuration file
|
||||||
--skip_proj_cfg do not read the project's configuration file
|
--skip_proj_cfg do not read the project's configuration file
|
||||||
|
--gc:refc|boehm use Nimrod's native GC|Boehm GC
|
||||||
--index:FILE use FILE to generate a documenation index file
|
--index:FILE use FILE to generate a documenation index file
|
||||||
--putenv:key=value set an environment variable
|
--putenv:key=value set an environment variable
|
||||||
--list_cmd list the commands used to execute external programs
|
--list_cmd list the commands used to execute external programs
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,9 @@
|
||||||
'Swap',
|
'Swap',
|
||||||
'IsNil',
|
'IsNil',
|
||||||
'ArrToSeq',
|
'ArrToSeq',
|
||||||
|
'CopyStr',
|
||||||
|
'CopyStrLast',
|
||||||
|
'NewString',
|
||||||
|
|
||||||
# magic types:
|
# magic types:
|
||||||
'Array',
|
'Array',
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
{'errExceptionExpected': 'exception expected'},
|
{'errExceptionExpected': 'exception expected'},
|
||||||
{'errExceptionAlreadyHandled': 'exception already handled'},
|
{'errExceptionAlreadyHandled': 'exception already handled'},
|
||||||
{'errReturnNotAllowedHere': "'return' only allowed in routine"},
|
{'errReturnNotAllowedHere': "'return' only allowed in routine"},
|
||||||
{'errYieldNotAllowedHere': "'yield' only allowed in iterator"},
|
{'errYieldNotAllowedHere': "'yield' only allowed in a loop of an iterator"},
|
||||||
{'errInvalidNumberOfYieldExpr': "invalid number of 'yield' expresions"},
|
{'errInvalidNumberOfYieldExpr': "invalid number of 'yield' expresions"},
|
||||||
{'errReturnInvalidInIterator': "'return' not allowed in iterator"},
|
{'errReturnInvalidInIterator': "'return' not allowed in iterator"},
|
||||||
{'errCannotReturnExpr': 'current routine cannot return an expression'},
|
{'errCannotReturnExpr': 'current routine cannot return an expression'},
|
||||||
|
|
@ -54,10 +54,10 @@
|
||||||
{'errStmtInvalidAfterReturn':
|
{'errStmtInvalidAfterReturn':
|
||||||
"statement not allowed after 'return', 'break' or 'raise'"},
|
"statement not allowed after 'return', 'break' or 'raise'"},
|
||||||
{'errStmtExpected': 'statement expected'},
|
{'errStmtExpected': 'statement expected'},
|
||||||
{'errYieldOnlyInInterators': "'yield' statement is only allowed in iterators"},
|
|
||||||
{'errInvalidLabel': "'$1' is no label"},
|
{'errInvalidLabel': "'$1' is no label"},
|
||||||
{'errInvalidCmdLineOption': "invalid command line option: '$1'"},
|
{'errInvalidCmdLineOption': "invalid command line option: '$1'"},
|
||||||
{'errCmdLineArgExpected': "argument for command line option expected: '$1'"},
|
{'errCmdLineArgExpected': "argument for command line option expected: '$1'"},
|
||||||
|
{'errCmdLineNoArgExpected': "invalid argument for command line option: '$1'"},
|
||||||
{'errInvalidVarSubstitution': "invalid variable substitution in '$1'"},
|
{'errInvalidVarSubstitution': "invalid variable substitution in '$1'"},
|
||||||
{'errUnknownVar': "unknown variable: '$1'"},
|
{'errUnknownVar': "unknown variable: '$1'"},
|
||||||
{'errUnknownCcompiler': "unknown C compiler: '$1'"},
|
{'errUnknownCcompiler': "unknown C compiler: '$1'"},
|
||||||
|
|
@ -105,7 +105,7 @@
|
||||||
"argument to 'staticAssert' cannot be evaluated at compile time"},
|
"argument to 'staticAssert' cannot be evaluated at compile time"},
|
||||||
{'errDotRequiresRecordOrObjectType': "'.' requires a record or object type"},
|
{'errDotRequiresRecordOrObjectType': "'.' requires a record or object type"},
|
||||||
{'errUndeclaredFieldX': "undeclared field: '$1'"},
|
{'errUndeclaredFieldX': "undeclared field: '$1'"},
|
||||||
{'errIndexNoIntType': 'index has to be an integer type'},
|
{'errNilAccess': 'attempt to access a nil address'},
|
||||||
{'errIndexOutOfBounds': 'index out of bounds'},
|
{'errIndexOutOfBounds': 'index out of bounds'},
|
||||||
{'errIndexTypesDoNotMatch': 'index types do not match'},
|
{'errIndexTypesDoNotMatch': 'index types do not match'},
|
||||||
{'errBracketsInvalidForType': "'[]' operator invalid for this type"},
|
{'errBracketsInvalidForType': "'[]' operator invalid for this type"},
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ Module Description
|
||||||
nimrod main module: parses the command line and calls
|
nimrod main module: parses the command line and calls
|
||||||
``main.MainCommand``
|
``main.MainCommand``
|
||||||
main implements the top-level command dispatching
|
main implements the top-level command dispatching
|
||||||
|
nimconf implements the config file reader
|
||||||
|
|
||||||
lexbase buffer handling of the lexical analyser
|
lexbase buffer handling of the lexical analyser
|
||||||
scanner lexical analyser
|
scanner lexical analyser
|
||||||
pnimsyn Nimrod's parser
|
pnimsyn Nimrod's parser
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')'
|
||||||
| '[' ']' | '=' | literal)+ '`'
|
| '[' ']' | '=' | literal)+ '`'
|
||||||
| IDENT
|
| IDENT
|
||||||
primary ::= (prefixOperator optInd)* (symbol | constructor |
|
primary ::= (prefixOperator optInd)* (symbol | constructor |
|
||||||
| castExpr | addrExpr) (
|
castExpr | addrExpr) (
|
||||||
'.' optInd symbol
|
'.' optInd symbol
|
||||||
| '(' optInd namedExprList [SAD] ')'
|
| '(' optInd namedExprList [SAD] ')'
|
||||||
| '[' optInd
|
| '[' optInd
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ The Nimrod project's directory structure is:
|
||||||
Path Purpose
|
Path Purpose
|
||||||
============ ==============================================
|
============ ==============================================
|
||||||
``bin`` binary files go into here
|
``bin`` binary files go into here
|
||||||
|
``build`` generated C code for the installation
|
||||||
``nim`` Pascal sources of the Nimrod compiler; this
|
``nim`` Pascal sources of the Nimrod compiler; this
|
||||||
should be modified, not the Nimrod version in
|
should be modified, not the Nimrod version in
|
||||||
``rod``!
|
``rod``!
|
||||||
|
|
@ -28,7 +29,7 @@ Path Purpose
|
||||||
code go into here
|
code go into here
|
||||||
``doc`` the documentation lives here; it is a bunch of
|
``doc`` the documentation lives here; it is a bunch of
|
||||||
reStructuredText files
|
reStructuredText files
|
||||||
``dist`` download packages as zip archives go into here
|
``dist`` additional packages for the distribution
|
||||||
``config`` configuration files for Nimrod go into here
|
``config`` configuration files for Nimrod go into here
|
||||||
``lib`` the Nimrod library lives here; ``rod`` depends
|
``lib`` the Nimrod library lives here; ``rod`` depends
|
||||||
on it!
|
on it!
|
||||||
|
|
@ -45,8 +46,8 @@ The compiler is written in a subset of Pascal with special annotations so
|
||||||
that it can be translated to Nimrod code automatically. This conversion is
|
that it can be translated to Nimrod code automatically. This conversion is
|
||||||
done by Nimrod itself via the undocumented ``boot`` command. Thus both Nimrod
|
done by Nimrod itself via the undocumented ``boot`` command. Thus both Nimrod
|
||||||
and Free Pascal can compile the Nimrod compiler. However, the Pascal version
|
and Free Pascal can compile the Nimrod compiler. However, the Pascal version
|
||||||
has no garbage collector and leaks memory like crazy! So the Pascal version
|
has no garbage collector and leaks memory! So the Pascal version should only
|
||||||
should only be used for bootstrapping.
|
be used for bootstrapping.
|
||||||
|
|
||||||
Requirements for bootstrapping:
|
Requirements for bootstrapping:
|
||||||
|
|
||||||
|
|
@ -214,7 +215,7 @@ address within this page. So including a cell is done as follows:
|
||||||
|
|
||||||
Removing a cell is analogous - the bit has to be set to zero.
|
Removing a cell is analogous - the bit has to be set to zero.
|
||||||
Single page descriptors are never deleted from the hash table. This is not
|
Single page descriptors are never deleted from the hash table. This is not
|
||||||
needed as the data structures need to be periodically rebuilt anyway.
|
needed as the data structures needs to be rebuilt periodically anyway.
|
||||||
|
|
||||||
Complete traversal is done in this way::
|
Complete traversal is done in this way::
|
||||||
|
|
||||||
|
|
@ -288,11 +289,8 @@ The synax tree consists of nodes which may have an arbitrary number of
|
||||||
children. Types and symbols are represented by other nodes, because they
|
children. Types and symbols are represented by other nodes, because they
|
||||||
may contain cycles. The AST changes its shape after semantic checking. This
|
may contain cycles. The AST changes its shape after semantic checking. This
|
||||||
is needed to make life easier for the code generators. See the "ast" module
|
is needed to make life easier for the code generators. See the "ast" module
|
||||||
for the type definitions.
|
for the type definitions. The `macros <macros.html>`_ module contains many
|
||||||
|
examples how the AST represents each syntactic structure.
|
||||||
I use the notation ``nodeKind(fields, [sons])`` for describing
|
|
||||||
nodes. ``nodeKind[sons]`` is a short-cut for ``nodeKind([sons])``.
|
|
||||||
XXX: Description of the language's syntax and the corresponding trees.
|
|
||||||
|
|
||||||
|
|
||||||
How the RTL is compiled
|
How the RTL is compiled
|
||||||
|
|
@ -310,6 +308,21 @@ semantic checking, a ``compilerproc`` is a proc that is used by the code
|
||||||
generator.
|
generator.
|
||||||
|
|
||||||
|
|
||||||
|
Debugging Nimrod's memory management
|
||||||
|
====================================
|
||||||
|
|
||||||
|
The following paragraphs are mostly a reminder for myself. Things to keep
|
||||||
|
in mind:
|
||||||
|
|
||||||
|
* Segmentation faults can have multiple reasons: One that is frequently
|
||||||
|
forgotten is that *stack overflow* can trigger one!
|
||||||
|
* If an assertion in Nimrod's memory manager or GC fails, the stack trace
|
||||||
|
keeps allocating memory! Thus a stack overflow may happen, hiding the
|
||||||
|
real issue.
|
||||||
|
* What seem to be C code generation problems is often a bug resulting from
|
||||||
|
not producing prototypes, so that some types default to ``cint``. Testing
|
||||||
|
without the ``-w`` option helps!
|
||||||
|
|
||||||
|
|
||||||
Generation of dynamic link libraries
|
Generation of dynamic link libraries
|
||||||
====================================
|
====================================
|
||||||
|
|
|
||||||
13
doc/lib.txt
13
doc/lib.txt
|
|
@ -22,6 +22,9 @@ Pure libraries
|
||||||
implicitly by the compiler. Do not import it directly. It relies on compiler
|
implicitly by the compiler. Do not import it directly. It relies on compiler
|
||||||
magic to work.
|
magic to work.
|
||||||
|
|
||||||
|
* `macros <macros.html>`_
|
||||||
|
Contains the AST API and documentation of Nimrod for writing macros.
|
||||||
|
|
||||||
* `strutils <strutils.html>`_
|
* `strutils <strutils.html>`_
|
||||||
This module contains common string handling operations like converting a
|
This module contains common string handling operations like converting a
|
||||||
string into uppercase, splitting a string into substrings, searching for
|
string into uppercase, splitting a string into substrings, searching for
|
||||||
|
|
@ -33,6 +36,9 @@ Pure libraries
|
||||||
commands, etc. This module is -- like any other basic library --
|
commands, etc. This module is -- like any other basic library --
|
||||||
platform independant.
|
platform independant.
|
||||||
|
|
||||||
|
* `osproc <osproc.html>`_
|
||||||
|
Module for process communication beyond ``os.executeShellCommand``.
|
||||||
|
|
||||||
* `math <math.html>`_
|
* `math <math.html>`_
|
||||||
Mathematical operations like cosine, square root.
|
Mathematical operations like cosine, square root.
|
||||||
|
|
||||||
|
|
@ -60,6 +66,9 @@ Pure libraries
|
||||||
to be somewhat error correcting, so that even some "wild HTML" found on the
|
to be somewhat error correcting, so that even some "wild HTML" found on the
|
||||||
web can be parsed with it.
|
web can be parsed with it.
|
||||||
|
|
||||||
|
* `parsecsv <parsecsv.html>`_
|
||||||
|
The ``parsecsv`` module implements a simple high performance CSV parser.
|
||||||
|
|
||||||
* `strtabs <strtabs.html>`_
|
* `strtabs <strtabs.html>`_
|
||||||
The ``strtabs`` module implements an efficient hash table that is a mapping
|
The ``strtabs`` module implements an efficient hash table that is a mapping
|
||||||
from strings to strings. Supports a case-sensitive, case-insensitive and
|
from strings to strings. Supports a case-sensitive, case-insensitive and
|
||||||
|
|
@ -98,6 +107,10 @@ Pure libraries
|
||||||
* `md5 <md5.html>`_
|
* `md5 <md5.html>`_
|
||||||
This module implements the MD5 checksum algorithm.
|
This module implements the MD5 checksum algorithm.
|
||||||
|
|
||||||
|
* `xmlgen <xmlgen.html>`_
|
||||||
|
This module implements macros for HTML code generation.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Impure libraries
|
Impure libraries
|
||||||
================
|
================
|
||||||
|
|
|
||||||
|
|
@ -1649,7 +1649,7 @@ possible within a single ``type`` section.
|
||||||
Generics
|
Generics
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
|
|
||||||
`Version 0.7.6: Generic types like in the example do not work.`:red:
|
`Version 0.7.8: Generic types like in the example do not work.`:red:
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
|
||||||
500
doc/theindex.txt
500
doc/theindex.txt
File diff suppressed because it is too large
Load diff
|
|
@ -396,7 +396,7 @@ is not executed (if an exception occurs).
|
||||||
Generics
|
Generics
|
||||||
========
|
========
|
||||||
|
|
||||||
`Version 0.7.6: Complex generic types like in the example do not work.`:red:
|
`Version 0.7.8: Complex generic types like in the example do not work.`:red:
|
||||||
|
|
||||||
`Generics`:idx: are Nimrod's means to parametrize procs, iterators or types
|
`Generics`:idx: are Nimrod's means to parametrize procs, iterators or types
|
||||||
with `type parameters`:idx:. They are most useful for efficient type safe
|
with `type parameters`:idx:. They are most useful for efficient type safe
|
||||||
|
|
@ -596,7 +596,8 @@ Nimrod's syntax is flexible enough anyway.
|
||||||
`Macros`:idx: can be used to implement `domain specific languages`:idx:.
|
`Macros`:idx: can be used to implement `domain specific languages`:idx:.
|
||||||
|
|
||||||
To write macros, one needs to know how the Nimrod concrete syntax is converted
|
To write macros, one needs to know how the Nimrod concrete syntax is converted
|
||||||
to an abstract syntax tree (AST). (Unfortunately the AST is not documented yet.)
|
to an abstract syntax tree (AST). The AST is documented in the
|
||||||
|
`macros <macros.html>`_ module.
|
||||||
|
|
||||||
There are two ways to invoke a macro:
|
There are two ways to invoke a macro:
|
||||||
(1) invoking a macro like a procedure call (`expression macros`:idx:)
|
(1) invoking a macro like a procedure call (`expression macros`:idx:)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
# Hallo world program
|
# Hallo world program
|
||||||
|
|
||||||
import strutils
|
echo "Hallo world!"
|
||||||
|
|
||||||
echo($(parseFloat("0.5")* toFloat(5000) / toFloat(parseInt("5000"))))
|
|
||||||
|
|
||||||
echo("Hallo world!")
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# Example program to show the new parsexml module
|
# Example program to show the new parsexml module
|
||||||
# This program reads an HTML file and writes all its used links to stdout.
|
# This program reads an HTML file and writes all its used links to stdout.
|
||||||
# Errors and whitespace are ignored.
|
# Errors and whitespace are ignored.
|
||||||
# (c) 2009 Andreas Rumpf
|
|
||||||
|
|
||||||
import os, streams, parsexml, strutils
|
import os, streams, parsexml, strutils
|
||||||
|
|
||||||
|
|
@ -15,7 +14,7 @@ if paramCount() < 1:
|
||||||
var links = 0 # count the number of links
|
var links = 0 # count the number of links
|
||||||
var filename = appendFileExt(ParamStr(1), "html")
|
var filename = appendFileExt(ParamStr(1), "html")
|
||||||
var s = newFileStream(filename, fmRead)
|
var s = newFileStream(filename, fmRead)
|
||||||
if s == nil: quit("cannot open the file" & filename)
|
if s == nil: quit("cannot open the file " & filename)
|
||||||
var x: TXmlParser
|
var x: TXmlParser
|
||||||
open(x, s, filename)
|
open(x, s, filename)
|
||||||
next(x) # get first event
|
next(x) # get first event
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
# Example program to show the new parsexml module
|
# Example program to show the new parsexml module
|
||||||
# This program reads an HTML file and writes its title to stdout.
|
# This program reads an HTML file and writes its title to stdout.
|
||||||
# Errors and whitespace are ignored.
|
# Errors and whitespace are ignored.
|
||||||
# (c) 2009 Andreas Rumpf
|
|
||||||
|
|
||||||
import os, streams, parsexml, strutils
|
import os, streams, parsexml, strutils
|
||||||
|
|
||||||
|
|
@ -10,7 +9,7 @@ if paramCount() < 1:
|
||||||
|
|
||||||
var filename = appendFileExt(ParamStr(1), "html")
|
var filename = appendFileExt(ParamStr(1), "html")
|
||||||
var s = newFileStream(filename, fmRead)
|
var s = newFileStream(filename, fmRead)
|
||||||
if s == nil: quit("cannot open the file" & filename)
|
if s == nil: quit("cannot open the file " & filename)
|
||||||
var x: TXmlParser
|
var x: TXmlParser
|
||||||
open(x, s, filename)
|
open(x, s, filename)
|
||||||
while true:
|
while true:
|
||||||
|
|
|
||||||
60
examples/statcsv.nim
Normal file
60
examples/statcsv.nim
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Example program to show the parsecsv module
|
||||||
|
# This program reads a CSV file and computes sum, mean, minimum, maximum and
|
||||||
|
# the standard deviation of its columns.
|
||||||
|
# The CSV file can have a header which is then used for the output.
|
||||||
|
|
||||||
|
import os, streams, parsecsv, strutils, math
|
||||||
|
|
||||||
|
if paramCount() < 1:
|
||||||
|
quit("Usage: sumcsv filename[.csv]")
|
||||||
|
|
||||||
|
var filename = appendFileExt(ParamStr(1), "csv")
|
||||||
|
var s = newFileStream(filename, fmRead)
|
||||||
|
if s == nil: quit("cannot open the file " & filename)
|
||||||
|
|
||||||
|
var
|
||||||
|
x: TCsvParser
|
||||||
|
header: seq[string]
|
||||||
|
res: seq[TRunningStat]
|
||||||
|
open(x, s, filename, separator=';', skipInitialSpace = true)
|
||||||
|
while readRow(x):
|
||||||
|
if processedRows(x) == 1:
|
||||||
|
newSeq(res, x.row.len) # allocate space for the result
|
||||||
|
if validIdentifier(x.row[0]):
|
||||||
|
# header line:
|
||||||
|
header = x.row
|
||||||
|
else:
|
||||||
|
newSeq(header, x.row.len)
|
||||||
|
for i in 0..x.row.len-1: header[i] = "Col " & $(i+1)
|
||||||
|
else:
|
||||||
|
# data line:
|
||||||
|
for i in 0..x.row.len-1:
|
||||||
|
push(res[i], parseFloat(x.row[i]))
|
||||||
|
x.close()
|
||||||
|
|
||||||
|
# Write results:
|
||||||
|
for i in 0..header.len-1:
|
||||||
|
stdout.write("\t")
|
||||||
|
stdout.write(header[i])
|
||||||
|
stdout.write("\nSum")
|
||||||
|
for i in 0..header.len-1:
|
||||||
|
stdout.write("\t")
|
||||||
|
stdout.write(res[i].sum)
|
||||||
|
stdout.write("\nMean")
|
||||||
|
for i in 0..header.len-1:
|
||||||
|
stdout.write("\t")
|
||||||
|
stdout.write(res[i].mean)
|
||||||
|
stdout.write("\nMin")
|
||||||
|
for i in 0..header.len-1:
|
||||||
|
stdout.write("\t")
|
||||||
|
stdout.write(res[i].min)
|
||||||
|
stdout.write("\nMax")
|
||||||
|
for i in 0..header.len-1:
|
||||||
|
stdout.write("\t")
|
||||||
|
stdout.write(res[i].max)
|
||||||
|
stdout.write("\nStdDev")
|
||||||
|
for i in 0..header.len-1:
|
||||||
|
stdout.write("\t")
|
||||||
|
stdout.write(res[i].standardDeviation)
|
||||||
|
stdout.write("\n")
|
||||||
|
|
||||||
140
ide/nimide.nim
140
ide/nimide.nim
|
|
@ -26,14 +26,14 @@ type
|
||||||
currTab: int
|
currTab: int
|
||||||
PEditor = ptr TEditor
|
PEditor = ptr TEditor
|
||||||
|
|
||||||
proc on_window_destroy(obj: PGtkObject, event: PGdkEvent,
|
proc onWindowDestroy(obj: PGtkObject, event: PGdkEvent,
|
||||||
data: pointer): gboolean {.cdecl.} =
|
data: pointer): gboolean {.cdecl.} =
|
||||||
gtk_main_quit()
|
gtkMainQuit()
|
||||||
|
|
||||||
proc on_about_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
proc onAboutMenuItemActivate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
||||||
gtk_show_about_dialog(e.window,
|
gtkShowAboutDialog(e.window,
|
||||||
"comments", "A fast and leight-weight IDE for Nimrod",
|
"comments", "A fast and leight-weight IDE for Nimrod",
|
||||||
"copyright", "Copyright \xc2\xa9 2008 Andreas Rumpf",
|
"copyright", "Copyright \xc2\xa9 2009 Andreas Rumpf",
|
||||||
"version", "0.1",
|
"version", "0.1",
|
||||||
"website", "http://nimrod.ethexor.com",
|
"website", "http://nimrod.ethexor.com",
|
||||||
"program-name", "Nimrod IDE",
|
"program-name", "Nimrod IDE",
|
||||||
|
|
@ -42,9 +42,9 @@ proc on_about_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
||||||
proc getTabIndex(e: PEditor, tab: PTab): int =
|
proc getTabIndex(e: PEditor, tab: PTab): int =
|
||||||
var i = 0
|
var i = 0
|
||||||
while true:
|
while true:
|
||||||
var w = gtk_notebook_get_nth_page(e.notebook, i)
|
var w = gtkNotebookGetNthPage(e.notebook, i)
|
||||||
if w == nil: return -1
|
if w == nil: return -1
|
||||||
var v = gtk_notebook_get_tab_label(e.notebook, w)
|
var v = gtkNotebookGetTabLabel(e.notebook, w)
|
||||||
if tab.hbox == v: return i
|
if tab.hbox == v: return i
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
|
|
@ -56,12 +56,11 @@ type
|
||||||
answerYes, answerNo, answerCancel
|
answerYes, answerNo, answerCancel
|
||||||
|
|
||||||
proc askWhetherToSave(e: PEditor): TAnswer =
|
proc askWhetherToSave(e: PEditor): TAnswer =
|
||||||
var dialog = gtk_dialog_new_with_buttons("Should the changes be saved?",
|
var dialog = gtkDialogNewWithButtons("Should the changes be saved?",
|
||||||
e.window, GTK_DIALOG_MODAL, GTK_STOCK_SAVE, 1,
|
e.window, GTK_DIALOG_MODAL, GTK_STOCK_SAVE, 1, "gtk-discard", 2,
|
||||||
"gtk-discard", 2,
|
GTK_STOCK_CANCEL, 3, nil)
|
||||||
GTK_STOCK_CANCEL, 3, nil)
|
result = TAnswer(gtkDialogRun(dialog)+1)
|
||||||
result = TAnswer(gtk_dialog_run(dialog)+1)
|
gtkWidgetDestroy(dialog)
|
||||||
gtk_widget_destroy(dialog)
|
|
||||||
|
|
||||||
proc saveTab(tab: PTab) =
|
proc saveTab(tab: PTab) =
|
||||||
if tab.untitled:
|
if tab.untitled:
|
||||||
|
|
@ -76,13 +75,13 @@ proc OnCloseTab(button: PGtkButton, tab: PTab) {.cdecl.} =
|
||||||
idx = i
|
idx = i
|
||||||
break
|
break
|
||||||
if idx >= 0:
|
if idx >= 0:
|
||||||
if gtk_text_buffer_get_modified(gtk_text_view_get_buffer(tab.textView)):
|
if gtkTextBufferGetModified(gtkTextViewGetBuffer(tab.textView)):
|
||||||
case askWhetherToSave(tab.e)
|
case askWhetherToSave(tab.e)
|
||||||
of answerCancel: return
|
of answerCancel: return
|
||||||
of answerYes: saveTab(tab)
|
of answerYes: saveTab(tab)
|
||||||
of answerNo: nil
|
of answerNo: nil
|
||||||
|
|
||||||
gtk_notebook_remove_page(tab.e.notebook, idx)
|
gtkNotebookRemovePage(tab.e.notebook, idx)
|
||||||
if idx < high(tab.e.tabs):
|
if idx < high(tab.e.tabs):
|
||||||
for i in idx..high(tab.e.tabs)-1:
|
for i in idx..high(tab.e.tabs)-1:
|
||||||
tab.e.tabs[i] = tab.e.tabs[i+1]
|
tab.e.tabs[i] = tab.e.tabs[i+1]
|
||||||
|
|
@ -95,95 +94,96 @@ proc OnCloseTab(button: PGtkButton, tab: PTab) {.cdecl.} =
|
||||||
|
|
||||||
proc createTab(e: PEditor, filename: string, untitled: bool) =
|
proc createTab(e: PEditor, filename: string, untitled: bool) =
|
||||||
var t = cast[PTab](alloc0(sizeof(TTab)))
|
var t = cast[PTab](alloc0(sizeof(TTab)))
|
||||||
t.textview = gtk_text_view_new_with_buffer(gtk_text_buffer_new(nil))
|
t.textview = gtkTextViewNewWithBuffer(gtkTextBufferNew(nil))
|
||||||
var font_desc = pango_font_description_from_string("monospace 10")
|
var fontDesc = pangoFontDescriptionFromString("monospace 10")
|
||||||
gtk_widget_modify_font(t.textview, font_desc)
|
gtkWidgetModifyFont(t.textview, fontDesc)
|
||||||
pango_font_description_free(font_desc)
|
pangoFontDescriptionFree(fontDesc)
|
||||||
t.filename = filename
|
t.filename = filename
|
||||||
t.untitled = untitled
|
t.untitled = untitled
|
||||||
gtk_widget_show(t.textview)
|
gtkWidgetShow(t.textview)
|
||||||
var scroll = gtk_scrolled_window_new(nil, nil)
|
var scroll = gtkScrolledWindowNew(nil, nil)
|
||||||
gtk_container_add(scroll, t.textview)
|
gtkContainerAdd(scroll, t.textview)
|
||||||
gtk_widget_show(scroll)
|
gtkWidgetShow(scroll)
|
||||||
|
|
||||||
t.e = e
|
t.e = e
|
||||||
t.hbox = gtk_hbox_new(false, 0)
|
t.hbox = gtkHboxNew(false, 0)
|
||||||
var image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU)
|
var image = gtkImageNewFromStock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU)
|
||||||
var button = gtk_button_new()
|
var button = gtkButtonNew()
|
||||||
var lab = gtk_label_new(filename)
|
var lab = gtkLabelNew(filename)
|
||||||
gtk_button_set_image(button, image)
|
gtkButtonSetImage(button, image)
|
||||||
gtk_button_set_relief(button, GTK_RELIEF_NONE)
|
gtkButtonSetRelief(button, GTK_RELIEF_NONE)
|
||||||
gtk_box_pack_start(t.hbox, lab, false, false, 2)
|
gtkBoxPackStart(t.hbox, lab, false, false, 2)
|
||||||
gtk_box_pack_end(t.hbox, button, false, false, 0)
|
gtkBoxPackEnd(t.hbox, button, false, false, 0)
|
||||||
|
|
||||||
discard g_signal_connect(button, "clicked", G_Callback(onCloseTab), t)
|
discard gSignalConnect(button, "clicked", G_Callback(onCloseTab), t)
|
||||||
gtk_widget_show(button)
|
gtkWidgetShow(button)
|
||||||
gtk_widget_show(lab)
|
gtkWidgetShow(lab)
|
||||||
|
|
||||||
var idx = gtk_notebook_append_page(e.notebook, scroll, t.hbox)
|
var idx = gtkNotebookAppendPage(e.notebook, scroll, t.hbox)
|
||||||
e.currTab = idx
|
e.currTab = idx
|
||||||
add(e.tabs, t)
|
add(e.tabs, t)
|
||||||
gtk_notebook_set_current_page(e.notebook, idx)
|
gtkNotebookSetCurrentPage(e.notebook, idx)
|
||||||
|
|
||||||
|
|
||||||
proc on_open_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
proc onOpenMenuItemActivate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
||||||
var files = ChooseFilesToOpen(e.window, getRoot(getActiveTab(e)))
|
var files = ChooseFilesToOpen(e.window, getRoot(getActiveTab(e)))
|
||||||
for f in items(files): createTab(e, f, untitled=false)
|
for f in items(files): createTab(e, f, untitled=false)
|
||||||
|
|
||||||
proc on_save_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
proc onSaveMenuItemActivate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
||||||
var cp = gtk_notebook_get_current_page(e.notebook)
|
var cp = gtkNotebookGetCurrentPage(e.notebook)
|
||||||
|
|
||||||
|
|
||||||
proc on_save_as_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
proc onSaveAsMenuItemActivate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
||||||
nil
|
nil
|
||||||
|
|
||||||
proc on_new_menu_item_activate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
proc onNewMenuItemActivate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} =
|
||||||
inc(e.tabname)
|
inc(e.tabname)
|
||||||
createTab(e, "untitled-" & $e.tabname, true)
|
createTab(e, "untitled-" & $e.tabname, true)
|
||||||
|
|
||||||
proc main(e: PEditor) =
|
proc main(e: PEditor) =
|
||||||
var builder = glade_xml_new(getApplicationDir() / GuiTemplate, nil, nil)
|
var builder = gladeXmlNew(getApplicationDir() / GuiTemplate, nil, nil)
|
||||||
if builder == nil: quit("cannot open: " & GuiTemplate)
|
if builder == nil: quit("cannot open: " & GuiTemplate)
|
||||||
# get the components:
|
# get the components:
|
||||||
e.window = GTK_WINDOW(glade_xml_get_widget(builder, "window"))
|
e.window = GTK_WINDOW(gladeXmlGetWidget(builder, "window"))
|
||||||
e.statusbar = GTK_STATUSBAR(glade_xml_get_widget(builder, "statusbar"))
|
e.statusbar = GTK_STATUSBAR(gladeXmlGetWidget(builder, "statusbar"))
|
||||||
e.notebook = GTK_NOTEBOOK(glade_xml_get_widget(builder, "notebook"))
|
e.notebook = GTK_NOTEBOOK(gladeXmlGetWidget(builder, "notebook"))
|
||||||
e.tabs = @[]
|
e.tabs = @[]
|
||||||
e.currTab = -1
|
e.currTab = -1
|
||||||
setHomogeneous(e.notebook^, 1)
|
setHomogeneous(e.notebook^, 1)
|
||||||
|
|
||||||
# connect the signal handlers:
|
# connect the signal handlers:
|
||||||
glade_xml_signal_connect(builder, "on_window_destroy",
|
gladeXmlSignalConnect(builder, "on_window_destroy",
|
||||||
GCallback(on_window_destroy))
|
GCallback(onWindowDestroy))
|
||||||
var about = GTK_MENU_ITEM(glade_xml_get_widget(builder, "about_menu_item"))
|
var about = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "about_menu_item"))
|
||||||
discard g_signal_connect(about, "activate",
|
discard gSignalConnect(about, "activate",
|
||||||
G_CALLBACK(on_about_menu_item_activate), e)
|
G_CALLBACK(onAboutMenuItemActivate), e)
|
||||||
|
|
||||||
var newItem = GTK_MENU_ITEM(glade_xml_get_widget(builder, "new_menu_item"))
|
var newItem = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "new_menu_item"))
|
||||||
discard g_signal_connect(newItem, "activate",
|
discard gSignalConnect(newItem, "activate",
|
||||||
G_CALLBACK(on_new_menu_item_activate), e)
|
G_CALLBACK(onNewMenuItemActivate), e)
|
||||||
|
|
||||||
var quitItem = GTK_MENU_ITEM(glade_xml_get_widget(builder, "quit_menu_item"))
|
var quitItem = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "quit_menu_item"))
|
||||||
discard g_signal_connect(quitItem, "activate",
|
discard gSignalConnect(quitItem, "activate",
|
||||||
G_CALLBACK(on_window_destroy), e)
|
G_CALLBACK(onWindowDestroy), e)
|
||||||
|
|
||||||
var openItem = GTK_MENU_ITEM(glade_xml_get_widget(builder, "open_menu_item"))
|
var openItem = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "open_menu_item"))
|
||||||
discard g_signal_connect(openItem, "activate",
|
discard gSignalConnect(openItem, "activate",
|
||||||
G_CALLBACK(on_open_menu_item_activate), e)
|
G_CALLBACK(onOpenMenuItemActivate), e)
|
||||||
|
|
||||||
var saveItem = GTK_MENU_ITEM(glade_xml_get_widget(builder, "save_menu_item"))
|
var saveItem = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "save_menu_item"))
|
||||||
discard g_signal_connect(saveItem, "activate",
|
discard gSignalConnect(saveItem, "activate",
|
||||||
G_CALLBACK(on_save_menu_item_activate), e)
|
G_CALLBACK(onSaveMenuItemActivate), e)
|
||||||
|
|
||||||
var saveAsItem = GTK_MENU_ITEM(glade_xml_get_widget(builder, "save_as_menu_item"))
|
var saveAsItem = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "save_as_menu_item"))
|
||||||
discard g_signal_connect(saveAsItem, "activate",
|
discard gSignalConnect(saveAsItem, "activate",
|
||||||
G_CALLBACK(on_save_as_menu_item_activate), e)
|
G_CALLBACK(onSaveAsMenuItemActivate), e)
|
||||||
|
|
||||||
gtk_window_set_default_icon_name(GTK_STOCK_EDIT)
|
gtkWindowSetDefaultIconName(GTK_STOCK_EDIT)
|
||||||
gtk_widget_show(e.window)
|
gtkWidgetShow(e.window)
|
||||||
gtk_main()
|
gtkMain()
|
||||||
|
|
||||||
|
|
||||||
gtk_nimrod_init()
|
gtkNimrodInit()
|
||||||
var e = cast[PEditor](alloc0(sizeof(TEditor)))
|
var e = cast[PEditor](alloc0(sizeof(TEditor)))
|
||||||
main(e)
|
main(e)
|
||||||
|
|
||||||
|
|
|
||||||
13
install.txt
13
install.txt
|
|
@ -16,7 +16,7 @@ install it.
|
||||||
Install Nimrod by downloading the appropriate ``.zip`` file and extracting it
|
Install Nimrod by downloading the appropriate ``.zip`` file and extracting it
|
||||||
to a directory of your choice. The Nimrod Compiler will stay in this
|
to a directory of your choice. The Nimrod Compiler will stay in this
|
||||||
directory (unless you copy it somewhere else). The compiler does not need
|
directory (unless you copy it somewhere else). The compiler does not need
|
||||||
write access to its directory anymore, so copying the nimrod folder to ``/opt``
|
write access to its directory, so copying the nimrod folder to ``/opt``
|
||||||
does work.
|
does work.
|
||||||
|
|
||||||
Then run the following command::
|
Then run the following command::
|
||||||
|
|
@ -44,16 +44,17 @@ Installation on Windows
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
Install Nimrod by downloading and running
|
Install Nimrod by downloading and running
|
||||||
the ``nimrod_$version.exe`` file. As default, the ``LLVM-GCC``
|
the ``nimrod_$version.exe`` file. As default, the ``GCC``
|
||||||
compiler is used that is bundled with this installer. You can change
|
compiler is used that is bundled with this installer. **You can change
|
||||||
the configuration file to use another C compiler.
|
the configuration file** ``config/nimrod.cfg`` **to use another C compiler
|
||||||
|
or change the path to GCC.**
|
||||||
|
|
||||||
Currently, the following C compilers are supported under Windows:
|
Currently, the following C compilers are supported under Windows:
|
||||||
|
|
||||||
- | Microsoft's Visual C++
|
- | Microsoft's Visual C++
|
||||||
| http://msdn.microsoft.com/visualc
|
| http://msdn.microsoft.com/visualc
|
||||||
| (You need the SDK too - but not the full one: Essential are only
|
| (You need the SDK too - but not the full one: Only
|
||||||
the win32api header files and import libraries.)
|
the win32api header files and import libraries are essential.)
|
||||||
- | Gnu C Compiler (the mingw version; the cygwin version has not been tested!)
|
- | Gnu C Compiler (the mingw version; the cygwin version has not been tested!)
|
||||||
| http://www.mingw.org/download.shtml
|
| http://www.mingw.org/download.shtml
|
||||||
- | LLVM with GNU C/C++ frontend
|
- | LLVM with GNU C/C++ frontend
|
||||||
|
|
|
||||||
80
koch.py
80
koch.py
|
|
@ -12,7 +12,7 @@ from pycompab import *
|
||||||
|
|
||||||
# --------------------- constants ----------------------------------------
|
# --------------------- constants ----------------------------------------
|
||||||
|
|
||||||
NIMROD_VERSION = '0.7.6'
|
NIMROD_VERSION = '0.7.8'
|
||||||
# This string contains Nimrod's version. It is the only place
|
# This string contains Nimrod's version. It is the only place
|
||||||
# where the version needs to be updated. The rest is done by
|
# where the version needs to be updated. The rest is done by
|
||||||
# the build process automatically. It is replaced **everywhere**!
|
# the build process automatically. It is replaced **everywhere**!
|
||||||
|
|
@ -34,6 +34,46 @@ USE_FPC = true
|
||||||
BOOTCMD = "$1 cc --compile:build/platdef.c $2 rod/nimrod.nim"
|
BOOTCMD = "$1 cc --compile:build/platdef.c $2 rod/nimrod.nim"
|
||||||
# the command used for bootstrapping
|
# the command used for bootstrapping
|
||||||
|
|
||||||
|
# ---------------------- compiler detection --------------------------------
|
||||||
|
|
||||||
|
def detect(cmd, lookFor="version"):
|
||||||
|
try:
|
||||||
|
pipe = os.popen4(cmd)[1]
|
||||||
|
except AttributeError:
|
||||||
|
pipe = os.popen(cmd)
|
||||||
|
result = None
|
||||||
|
for line in pipe.readlines():
|
||||||
|
if find(lower(line), lookFor) >= 0:
|
||||||
|
result = line[:-1]
|
||||||
|
break
|
||||||
|
pipe.close()
|
||||||
|
if not result:
|
||||||
|
# don't give up yet; it may have written to stderr
|
||||||
|
if os.system(cmd) == 0:
|
||||||
|
result = cmd
|
||||||
|
return result
|
||||||
|
|
||||||
|
def detectNimrod():
|
||||||
|
if detect("nimrod"):
|
||||||
|
return "nimrod"
|
||||||
|
elif detect("bin/nimrod"):
|
||||||
|
return "bin/nimrod"
|
||||||
|
else:
|
||||||
|
Error("could not find a working nimrod executable")
|
||||||
|
|
||||||
|
def detectNim():
|
||||||
|
if USE_FPC and detect("fpc -h"):
|
||||||
|
# workaround a bug in the macosx version of nim:
|
||||||
|
if getHost() == "macosx": return "nim"
|
||||||
|
else: return "bin/nim"
|
||||||
|
return detectNimrod()
|
||||||
|
|
||||||
|
def detectAndCompileNim():
|
||||||
|
result = detectNim()
|
||||||
|
if result == "bin/nim" or result == "nim":
|
||||||
|
cmd_nim()
|
||||||
|
return result
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
def Error(msg): sys.exit("[Koch] *** ERROR: " + msg)
|
def Error(msg): sys.exit("[Koch] *** ERROR: " + msg)
|
||||||
|
|
@ -453,12 +493,8 @@ def genBootDiff(genA, genB):
|
||||||
|
|
||||||
def cmd_rodsrc():
|
def cmd_rodsrc():
|
||||||
"converts the src/*.pas files into Nimrod syntax"
|
"converts the src/*.pas files into Nimrod syntax"
|
||||||
PAS_FILES_BLACKLIST = split("""nsystem nmath nos ntime strutils""")
|
PAS_FILES_BLACKLIST = split("""nsystem nmath nos osproc ntime strutils""")
|
||||||
if USE_FPC and detect("fpc -h"):
|
compiler = detectAndCompileNim()
|
||||||
cmd_nim()
|
|
||||||
compiler = "nim"
|
|
||||||
else:
|
|
||||||
compiler = "nimrod"
|
|
||||||
CMD = "$1 boot --skip_proj_cfg -o:rod/$2.nim nim/$3"
|
CMD = "$1 boot --skip_proj_cfg -o:rod/$2.nim nim/$3"
|
||||||
result = false
|
result = false
|
||||||
for fi in Glob("nim/*.pas"):
|
for fi in Glob("nim/*.pas"):
|
||||||
|
|
@ -484,14 +520,7 @@ def cmd_boot(args):
|
||||||
Move(ExeExt("rod/nimcache/nimrod"), ExeExt("rod/nimrod"))
|
Move(ExeExt("rod/nimcache/nimrod"), ExeExt("rod/nimrod"))
|
||||||
|
|
||||||
writePlatdefC(getNimrodPath())
|
writePlatdefC(getNimrodPath())
|
||||||
d = detect("fpc -h")
|
compiler = detectAndCompileNim()
|
||||||
if USE_FPC and d:
|
|
||||||
Echo(Subs("'$1' detected", d))
|
|
||||||
cmd_nim()
|
|
||||||
compiler = "nim"
|
|
||||||
else:
|
|
||||||
compiler = "nimrod"
|
|
||||||
|
|
||||||
cmd_rodsrc() # regenerate nimrod version of the files
|
cmd_rodsrc() # regenerate nimrod version of the files
|
||||||
|
|
||||||
# move the new executable to bin directory (is done by cmd_rod())
|
# move the new executable to bin directory (is done by cmd_rod())
|
||||||
|
|
@ -501,7 +530,7 @@ def cmd_boot(args):
|
||||||
# move the new executable to bin directory:
|
# move the new executable to bin directory:
|
||||||
moveExes()
|
moveExes()
|
||||||
# compile again and compare:
|
# compile again and compare:
|
||||||
myExec("nimrod")
|
myExec("bin/nimrod") # here we always use the new executable
|
||||||
genB = readCFiles() # second generation of generated C files
|
genB = readCFiles() # second generation of generated C files
|
||||||
diff = genBootDiff(genA, genB)
|
diff = genBootDiff(genA, genB)
|
||||||
if diff:
|
if diff:
|
||||||
|
|
@ -517,7 +546,7 @@ def cmd_boot(args):
|
||||||
# move the new executable to bin directory:
|
# move the new executable to bin directory:
|
||||||
moveExes()
|
moveExes()
|
||||||
# use the new executable to compile Nimrod:
|
# use the new executable to compile Nimrod:
|
||||||
myExec("nimrod")
|
myExec("bin/nimrod")
|
||||||
if SameFileContent(Path(ExeExt("rod/nimrod")),
|
if SameFileContent(Path(ExeExt("rod/nimrod")),
|
||||||
Path(ExeExt("bin/nimrod"))):
|
Path(ExeExt("bin/nimrod"))):
|
||||||
Echo("executables are equal: SUCCESS!")
|
Echo("executables are equal: SUCCESS!")
|
||||||
|
|
@ -614,23 +643,6 @@ def writePlatdefC(nimrodpath):
|
||||||
'\n', host, processor))
|
'\n', host, processor))
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def detect(cmd, lookFor="version"):
|
|
||||||
try:
|
|
||||||
pipe = os.popen4(cmd)[1]
|
|
||||||
except AttributeError:
|
|
||||||
pipe = os.popen(cmd)
|
|
||||||
result = None
|
|
||||||
for line in pipe.readlines():
|
|
||||||
if find(lower(line), lookFor) >= 0:
|
|
||||||
result = line[:-1]
|
|
||||||
break
|
|
||||||
pipe.close()
|
|
||||||
if not result:
|
|
||||||
# don't give up yet; it may have written to stderr
|
|
||||||
if os.system(cmd) == 0:
|
|
||||||
result = cmd
|
|
||||||
return result
|
|
||||||
|
|
||||||
def getNimrodPath():
|
def getNimrodPath():
|
||||||
if os.name == "posix":
|
if os.name == "posix":
|
||||||
# Does not work 100% reliably. It is the best solution though.
|
# Does not work 100% reliably. It is the best solution though.
|
||||||
|
|
|
||||||
217
lib/alloc.nim
217
lib/alloc.nim
|
|
@ -7,29 +7,27 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
# Low level allocator for Nimrod.
|
# Low level allocator for Nimrod. Has been designed to support the GC.
|
||||||
# TODO:
|
# TODO:
|
||||||
# - eliminate "used" field
|
# - eliminate "used" field
|
||||||
# - make searching for block O(1)
|
# - make searching for block O(1)
|
||||||
|
|
||||||
proc raiseOutOfMem {.noinline.} =
|
|
||||||
assert false
|
|
||||||
quit(1)
|
|
||||||
|
|
||||||
# ------------ platform specific chunk allocation code -----------------------
|
# ------------ platform specific chunk allocation code -----------------------
|
||||||
|
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
const # XXX: make these variables for portability?
|
const
|
||||||
PROT_READ = 1 # page can be read
|
PROT_READ = 1 # page can be read
|
||||||
PROT_WRITE = 2 # page can be written
|
PROT_WRITE = 2 # page can be written
|
||||||
MAP_PRIVATE = 2 # Changes are private
|
MAP_PRIVATE = 2 # Changes are private
|
||||||
|
|
||||||
when defined(linux):
|
when defined(linux) or defined(aix):
|
||||||
const MAP_ANONYMOUS = 0x20 # don't use a file
|
const MAP_ANONYMOUS = 0x20 # don't use a file
|
||||||
elif defined(macosx):
|
elif defined(macosx) or defined(bsd):
|
||||||
const MAP_ANONYMOUS = 0x1000
|
const MAP_ANONYMOUS = 0x1000
|
||||||
|
elif defined(solaris):
|
||||||
|
const MAP_ANONYMOUS = 0x100
|
||||||
else:
|
else:
|
||||||
const MAP_ANONYMOUS = 0 # other operating systems may not know about this
|
{.error: "Port memory manager to your platform".}
|
||||||
|
|
||||||
proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
|
proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
|
||||||
off: int): pointer {.header: "<sys/mman.h>".}
|
off: int): pointer {.header: "<sys/mman.h>".}
|
||||||
|
|
@ -43,7 +41,7 @@ when defined(posix):
|
||||||
raiseOutOfMem()
|
raiseOutOfMem()
|
||||||
|
|
||||||
proc osDeallocPages(p: pointer, size: int) {.inline} =
|
proc osDeallocPages(p: pointer, size: int) {.inline} =
|
||||||
munmap(p, size)
|
when reallyOsDealloc: munmap(p, size)
|
||||||
|
|
||||||
elif defined(windows):
|
elif defined(windows):
|
||||||
const
|
const
|
||||||
|
|
@ -69,7 +67,7 @@ elif defined(windows):
|
||||||
|
|
||||||
proc osDeallocPages(p: pointer, size: int) {.inline.} =
|
proc osDeallocPages(p: pointer, size: int) {.inline.} =
|
||||||
# according to Microsoft, 0 is the only correct value here:
|
# according to Microsoft, 0 is the only correct value here:
|
||||||
VirtualFree(p, 0, MEM_RELEASE)
|
when reallyOsDealloc: VirtualFree(p, 0, MEM_RELEASE)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
{.error: "Port memory manager to your platform".}
|
{.error: "Port memory manager to your platform".}
|
||||||
|
|
@ -77,48 +75,14 @@ else:
|
||||||
# --------------------- end of non-portable code -----------------------------
|
# --------------------- end of non-portable code -----------------------------
|
||||||
|
|
||||||
# We manage *chunks* of memory. Each chunk is a multiple of the page size.
|
# We manage *chunks* of memory. Each chunk is a multiple of the page size.
|
||||||
# The page size may or may not the operating system's page size. Each chunk
|
# Each chunk starts at an address that is divisible by the page size. Chunks
|
||||||
# starts at an address that is divisible by the page size. Chunks that are
|
# that are bigger than ``ChunkOsReturn`` are returned back to the operating
|
||||||
# bigger than ``ChunkOsReturn`` are returned back to the operating system
|
# system immediately.
|
||||||
# immediately.
|
|
||||||
|
|
||||||
|
|
||||||
# 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
|
const
|
||||||
PageMask = PageSize-1
|
ChunkOsReturn = 256 * PageSize
|
||||||
|
|
||||||
SmallChunkSize = PageSize # * 4
|
|
||||||
|
|
||||||
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 (!)
|
|
||||||
|
|
||||||
ChunkOsReturn = 64 * PageSize
|
|
||||||
InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn!
|
InitialMemoryRequest = ChunkOsReturn div 2 # < ChunkOsReturn!
|
||||||
|
SmallChunkSize = PageSize
|
||||||
# Compile time options:
|
|
||||||
coalescRight = true
|
|
||||||
coalescLeft = true
|
|
||||||
|
|
||||||
const
|
|
||||||
TrunkShift = 9
|
|
||||||
BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and divisible by 64
|
|
||||||
TrunkMask = BitsPerTrunk - 1
|
|
||||||
IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8)
|
|
||||||
IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width
|
|
||||||
IntMask = 1 shl IntShift - 1
|
|
||||||
|
|
||||||
type
|
type
|
||||||
PTrunk = ptr TTrunk
|
PTrunk = ptr TTrunk
|
||||||
|
|
@ -132,10 +96,11 @@ type
|
||||||
data: TTrunkBuckets
|
data: TTrunkBuckets
|
||||||
|
|
||||||
type
|
type
|
||||||
TAlignType = float
|
TAlignType = biggestFloat
|
||||||
TFreeCell {.final, pure.} = object
|
TFreeCell {.final, pure.} = object
|
||||||
next: ptr TFreeCell # next free cell in chunk (overlaid with refcount)
|
next: ptr TFreeCell # next free cell in chunk (overlaid with refcount)
|
||||||
zeroField: pointer # nil means cell is not used (overlaid with typ field)
|
zeroField: int # 0 means cell is not used (overlaid with typ field)
|
||||||
|
# 1 means cell is manually managed pointer
|
||||||
|
|
||||||
PChunk = ptr TBaseChunk
|
PChunk = ptr TBaseChunk
|
||||||
PBigChunk = ptr TBigChunk
|
PBigChunk = ptr TBigChunk
|
||||||
|
|
@ -155,15 +120,20 @@ type
|
||||||
TBigChunk = object of TBaseChunk # not necessarily > PageSize!
|
TBigChunk = object of TBaseChunk # not necessarily > PageSize!
|
||||||
next: PBigChunk # chunks of the same (or bigger) size
|
next: PBigChunk # chunks of the same (or bigger) size
|
||||||
prev: PBigChunk
|
prev: PBigChunk
|
||||||
|
align: int
|
||||||
data: TAlignType # start of usable memory
|
data: TAlignType # start of usable memory
|
||||||
|
|
||||||
template smallChunkOverhead(): expr = sizeof(TSmallChunk)-sizeof(TAlignType)
|
template smallChunkOverhead(): expr = sizeof(TSmallChunk)-sizeof(TAlignType)
|
||||||
template bigChunkOverhead(): expr = sizeof(TBigChunk)-sizeof(TAlignType)
|
template bigChunkOverhead(): expr = sizeof(TBigChunk)-sizeof(TAlignType)
|
||||||
|
|
||||||
proc roundup(x, v: int): int {.inline.} = return ((-x) and (v-1)) +% x
|
proc roundup(x, v: int): int {.inline.} =
|
||||||
|
result = (x + (v-1)) and not (v-1)
|
||||||
|
assert(result >= x)
|
||||||
|
#return ((-x) and (v-1)) +% x
|
||||||
|
|
||||||
assert(roundup(14, PageSize) == PageSize)
|
assert(roundup(14, PageSize) == PageSize)
|
||||||
assert(roundup(15, 8) == 16)
|
assert(roundup(15, 8) == 16)
|
||||||
|
assert(roundup(65, 8) == 72)
|
||||||
|
|
||||||
# ------------- chunk table ---------------------------------------------------
|
# ------------- chunk table ---------------------------------------------------
|
||||||
# We use a PtrSet of chunk starts and a table[Page, chunksize] for chunk
|
# We use a PtrSet of chunk starts and a table[Page, chunksize] for chunk
|
||||||
|
|
@ -180,7 +150,7 @@ type
|
||||||
|
|
||||||
TAllocator {.final, pure.} = object
|
TAllocator {.final, pure.} = object
|
||||||
llmem: PLLChunk
|
llmem: PLLChunk
|
||||||
currMem, maxMem: int # currently and maximum used memory size (allocated from OS)
|
currMem, maxMem, freeMem: int # memory sizes (allocated from OS)
|
||||||
freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
|
freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
|
||||||
freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
|
freeChunksList: PBigChunk # XXX make this a datastructure with O(1) access
|
||||||
chunkStarts: TIntSet
|
chunkStarts: TIntSet
|
||||||
|
|
@ -277,8 +247,10 @@ var lastSize = PageSize
|
||||||
|
|
||||||
proc requestOsChunks(a: var TAllocator, size: int): PBigChunk =
|
proc requestOsChunks(a: var TAllocator, size: int): PBigChunk =
|
||||||
incCurrMem(a, size)
|
incCurrMem(a, size)
|
||||||
|
inc(a.freeMem, size)
|
||||||
result = cast[PBigChunk](osAllocPages(size))
|
result = cast[PBigChunk](osAllocPages(size))
|
||||||
assert((cast[TAddress](result) and PageMask) == 0)
|
assert((cast[TAddress](result) and PageMask) == 0)
|
||||||
|
#zeroMem(result, size)
|
||||||
result.next = nil
|
result.next = nil
|
||||||
result.prev = nil
|
result.prev = nil
|
||||||
result.used = false
|
result.used = false
|
||||||
|
|
@ -312,11 +284,28 @@ proc freeOsChunks(a: var TAllocator, p: pointer, size: int) =
|
||||||
excl(a.chunkStarts, pageIndex(p))
|
excl(a.chunkStarts, pageIndex(p))
|
||||||
osDeallocPages(p, size)
|
osDeallocPages(p, size)
|
||||||
decCurrMem(a, size)
|
decCurrMem(a, size)
|
||||||
|
dec(a.freeMem, size)
|
||||||
|
#c_fprintf(c_stdout, "[Alloc] back to OS: %ld\n", size)
|
||||||
|
|
||||||
proc isAccessible(p: pointer): bool {.inline.} =
|
proc isAccessible(p: pointer): bool {.inline.} =
|
||||||
result = Contains(allocator.chunkStarts, pageIndex(p))
|
result = Contains(allocator.chunkStarts, pageIndex(p))
|
||||||
|
|
||||||
|
proc contains[T](list, x: T): bool =
|
||||||
|
var it = list
|
||||||
|
while it != nil:
|
||||||
|
if it == x: return true
|
||||||
|
it = it.next
|
||||||
|
|
||||||
|
proc writeFreeList(a: TAllocator) =
|
||||||
|
var it = a.freeChunksList
|
||||||
|
c_fprintf(c_stdout, "freeChunksList: %p\n", it)
|
||||||
|
while it != nil:
|
||||||
|
c_fprintf(c_stdout, "it: %p, next: %p, prev: %p\n",
|
||||||
|
it, it.next, it.prev)
|
||||||
|
it = it.next
|
||||||
|
|
||||||
proc ListAdd[T](head: var T, c: T) {.inline.} =
|
proc ListAdd[T](head: var T, c: T) {.inline.} =
|
||||||
|
assert(c notin head)
|
||||||
assert c.prev == nil
|
assert c.prev == nil
|
||||||
assert c.next == nil
|
assert c.next == nil
|
||||||
c.next = head
|
c.next = head
|
||||||
|
|
@ -326,6 +315,7 @@ proc ListAdd[T](head: var T, c: T) {.inline.} =
|
||||||
head = c
|
head = c
|
||||||
|
|
||||||
proc ListRemove[T](head: var T, c: T) {.inline.} =
|
proc ListRemove[T](head: var T, c: T) {.inline.} =
|
||||||
|
assert(c in head)
|
||||||
if c == head:
|
if c == head:
|
||||||
head = c.next
|
head = c.next
|
||||||
assert c.prev == nil
|
assert c.prev == nil
|
||||||
|
|
@ -344,13 +334,22 @@ proc isSmallChunk(c: PChunk): bool {.inline.} =
|
||||||
proc chunkUnused(c: PChunk): bool {.inline.} =
|
proc chunkUnused(c: PChunk): bool {.inline.} =
|
||||||
result = not c.used
|
result = not c.used
|
||||||
|
|
||||||
|
proc updatePrevSize(a: var TAllocator, c: PBigChunk,
|
||||||
|
prevSize: int) {.inline.} =
|
||||||
|
var ri = cast[PChunk](cast[TAddress](c) +% c.size)
|
||||||
|
assert((cast[TAddress](ri) and PageMask) == 0)
|
||||||
|
if isAccessible(ri):
|
||||||
|
ri.prevSize = prevSize
|
||||||
|
|
||||||
proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
|
proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
|
||||||
var c = c
|
var c = c
|
||||||
assert(c.size >= PageSize)
|
assert(c.size >= PageSize)
|
||||||
|
inc(a.freeMem, c.size)
|
||||||
when coalescRight:
|
when coalescRight:
|
||||||
var ri = cast[PChunk](cast[TAddress](c) +% c.size)
|
var ri = cast[PChunk](cast[TAddress](c) +% c.size)
|
||||||
assert((cast[TAddress](ri) and PageMask) == 0)
|
assert((cast[TAddress](ri) and PageMask) == 0)
|
||||||
if isAccessible(ri) and chunkUnused(ri):
|
if isAccessible(ri) and chunkUnused(ri):
|
||||||
|
assert(not isSmallChunk(ri))
|
||||||
if not isSmallChunk(ri):
|
if not isSmallChunk(ri):
|
||||||
ListRemove(a.freeChunksList, cast[PBigChunk](ri))
|
ListRemove(a.freeChunksList, cast[PBigChunk](ri))
|
||||||
inc(c.size, ri.size)
|
inc(c.size, ri.size)
|
||||||
|
|
@ -360,6 +359,7 @@ proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
|
||||||
var le = cast[PChunk](cast[TAddress](c) -% c.prevSize)
|
var le = cast[PChunk](cast[TAddress](c) -% c.prevSize)
|
||||||
assert((cast[TAddress](le) and PageMask) == 0)
|
assert((cast[TAddress](le) and PageMask) == 0)
|
||||||
if isAccessible(le) and chunkUnused(le):
|
if isAccessible(le) and chunkUnused(le):
|
||||||
|
assert(not isSmallChunk(le))
|
||||||
if not isSmallChunk(le):
|
if not isSmallChunk(le):
|
||||||
ListRemove(a.freeChunksList, cast[PBigChunk](le))
|
ListRemove(a.freeChunksList, cast[PBigChunk](le))
|
||||||
inc(le.size, c.size)
|
inc(le.size, c.size)
|
||||||
|
|
@ -367,6 +367,8 @@ proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
|
||||||
c = cast[PBigChunk](le)
|
c = cast[PBigChunk](le)
|
||||||
|
|
||||||
if c.size < ChunkOsReturn:
|
if c.size < ChunkOsReturn:
|
||||||
|
incl(a.chunkStarts, pageIndex(c))
|
||||||
|
updatePrevSize(a, c, c.size)
|
||||||
ListAdd(a.freeChunksList, c)
|
ListAdd(a.freeChunksList, c)
|
||||||
c.used = false
|
c.used = false
|
||||||
else:
|
else:
|
||||||
|
|
@ -374,11 +376,16 @@ proc freeBigChunk(a: var TAllocator, c: PBigChunk) =
|
||||||
|
|
||||||
proc splitChunk(a: var TAllocator, c: PBigChunk, size: int) =
|
proc splitChunk(a: var TAllocator, c: PBigChunk, size: int) =
|
||||||
var rest = cast[PBigChunk](cast[TAddress](c) +% size)
|
var rest = cast[PBigChunk](cast[TAddress](c) +% size)
|
||||||
|
if rest in a.freeChunksList:
|
||||||
|
c_fprintf(c_stdout, "to add: %p\n", rest)
|
||||||
|
writeFreeList(allocator)
|
||||||
|
assert false
|
||||||
rest.size = c.size - size
|
rest.size = c.size - size
|
||||||
rest.used = false
|
rest.used = false
|
||||||
rest.next = nil # XXX
|
rest.next = nil
|
||||||
rest.prev = nil
|
rest.prev = nil
|
||||||
rest.prevSize = size
|
rest.prevSize = size
|
||||||
|
updatePrevSize(a, c, rest.size)
|
||||||
c.size = size
|
c.size = size
|
||||||
incl(a.chunkStarts, pageIndex(rest))
|
incl(a.chunkStarts, pageIndex(rest))
|
||||||
ListAdd(a.freeChunksList, rest)
|
ListAdd(a.freeChunksList, rest)
|
||||||
|
|
@ -386,26 +393,32 @@ proc splitChunk(a: var TAllocator, c: PBigChunk, size: int) =
|
||||||
proc getBigChunk(a: var TAllocator, size: int): PBigChunk =
|
proc getBigChunk(a: var TAllocator, size: int): PBigChunk =
|
||||||
# use first fit for now:
|
# use first fit for now:
|
||||||
assert((size and PageMask) == 0)
|
assert((size and PageMask) == 0)
|
||||||
|
assert(size > 0)
|
||||||
result = a.freeChunksList
|
result = a.freeChunksList
|
||||||
block search:
|
block search:
|
||||||
while result != nil:
|
while result != nil:
|
||||||
|
#if not chunkUnused(result):
|
||||||
|
# c_fprintf(c_stdout, "%lld\n", int(result.used))
|
||||||
assert chunkUnused(result)
|
assert chunkUnused(result)
|
||||||
if result.size == size:
|
if result.size == size:
|
||||||
ListRemove(a.freeChunksList, result)
|
ListRemove(a.freeChunksList, result)
|
||||||
break search
|
break search
|
||||||
elif result.size > size:
|
elif result.size > size:
|
||||||
splitChunk(a, result, size)
|
#c_fprintf(c_stdout, "res size: %lld; size: %lld\n", result.size, size)
|
||||||
ListRemove(a.freeChunksList, result)
|
ListRemove(a.freeChunksList, result)
|
||||||
|
splitChunk(a, result, size)
|
||||||
break search
|
break search
|
||||||
result = result.next
|
result = result.next
|
||||||
|
assert result != a.freeChunksList
|
||||||
if size < InitialMemoryRequest:
|
if size < InitialMemoryRequest:
|
||||||
result = requestOsChunks(a, InitialMemoryRequest)
|
result = requestOsChunks(a, InitialMemoryRequest)
|
||||||
splitChunk(a, result, size)
|
splitChunk(a, result, size)
|
||||||
else:
|
else:
|
||||||
result = requestOsChunks(a, size)
|
result = requestOsChunks(a, size)
|
||||||
result.prevSize = 0
|
result.prevSize = 0 # XXX why is this needed?
|
||||||
result.used = true
|
result.used = true
|
||||||
incl(a.chunkStarts, pageIndex(result))
|
incl(a.chunkStarts, pageIndex(result))
|
||||||
|
dec(a.freeMem, size)
|
||||||
|
|
||||||
proc getSmallChunk(a: var TAllocator): PSmallChunk =
|
proc getSmallChunk(a: var TAllocator): PSmallChunk =
|
||||||
var res = getBigChunk(a, PageSize)
|
var res = getBigChunk(a, PageSize)
|
||||||
|
|
@ -419,8 +432,11 @@ proc getCellSize(p: pointer): int {.inline.} =
|
||||||
var c = pageAddr(p)
|
var c = pageAddr(p)
|
||||||
result = c.size
|
result = c.size
|
||||||
|
|
||||||
proc alloc(a: var TAllocator, requestedSize: int): pointer =
|
proc rawAlloc(a: var TAllocator, requestedSize: int): pointer =
|
||||||
var size = roundup(max(requestedSize, sizeof(TFreeCell)), MemAlign)
|
assert(roundup(65, 8) == 72)
|
||||||
|
assert requestedSize >= sizeof(TFreeCell)
|
||||||
|
var size = roundup(requestedSize, MemAlign)
|
||||||
|
#c_fprintf(c_stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
|
||||||
if size <= SmallChunkSize-smallChunkOverhead():
|
if size <= SmallChunkSize-smallChunkOverhead():
|
||||||
# allocate a small block: for small chunks, we use only its next pointer
|
# allocate a small block: for small chunks, we use only its next pointer
|
||||||
var s = size div MemAlign
|
var s = size div MemAlign
|
||||||
|
|
@ -436,8 +452,11 @@ proc alloc(a: var TAllocator, requestedSize: int): pointer =
|
||||||
c.prev = nil
|
c.prev = nil
|
||||||
ListAdd(a.freeSmallChunks[s], c)
|
ListAdd(a.freeSmallChunks[s], c)
|
||||||
result = addr(c.data)
|
result = addr(c.data)
|
||||||
|
assert((cast[TAddress](result) and (MemAlign-1)) == 0)
|
||||||
else:
|
else:
|
||||||
assert c.next != c
|
assert c.next != c
|
||||||
|
#if c.size != size:
|
||||||
|
# c_fprintf(c_stdout, "csize: %lld; size %lld\n", c.size, size)
|
||||||
assert c.size == size
|
assert c.size == size
|
||||||
if c.freeList == nil:
|
if c.freeList == nil:
|
||||||
assert(c.acc + smallChunkOverhead() + size <= SmallChunkSize)
|
assert(c.acc + smallChunkOverhead() + size <= SmallChunkSize)
|
||||||
|
|
@ -445,9 +464,10 @@ proc alloc(a: var TAllocator, requestedSize: int): pointer =
|
||||||
inc(c.acc, size)
|
inc(c.acc, size)
|
||||||
else:
|
else:
|
||||||
result = c.freeList
|
result = c.freeList
|
||||||
assert(c.freeList.zeroField == nil)
|
assert(c.freeList.zeroField == 0)
|
||||||
c.freeList = c.freeList.next
|
c.freeList = c.freeList.next
|
||||||
dec(c.free, size)
|
dec(c.free, size)
|
||||||
|
assert((cast[TAddress](result) and (MemAlign-1)) == 0)
|
||||||
if c.free < size:
|
if c.free < size:
|
||||||
ListRemove(a.freeSmallChunks[s], c)
|
ListRemove(a.freeSmallChunks[s], c)
|
||||||
else:
|
else:
|
||||||
|
|
@ -458,16 +478,10 @@ proc alloc(a: var TAllocator, requestedSize: int): pointer =
|
||||||
assert c.next == nil
|
assert c.next == nil
|
||||||
assert c.size == size
|
assert c.size == size
|
||||||
result = addr(c.data)
|
result = addr(c.data)
|
||||||
cast[ptr TFreeCell](result).zeroField = cast[ptr TFreeCell](1) # make it != nil
|
assert((cast[TAddress](result) and (MemAlign-1)) == 0)
|
||||||
#echo("setting to one: ", $cast[TAddress](addr(cast[ptr TFreeCell](result).zeroField)))
|
assert(isAccessible(result))
|
||||||
|
|
||||||
proc contains(list, x: PSmallChunk): bool =
|
proc rawDealloc(a: var TAllocator, p: pointer) =
|
||||||
var it = list
|
|
||||||
while it != nil:
|
|
||||||
if it == x: return true
|
|
||||||
it = it.next
|
|
||||||
|
|
||||||
proc dealloc(a: var TAllocator, p: pointer) =
|
|
||||||
var c = pageAddr(p)
|
var c = pageAddr(p)
|
||||||
if isSmallChunk(c):
|
if isSmallChunk(c):
|
||||||
# `p` is within a small chunk:
|
# `p` is within a small chunk:
|
||||||
|
|
@ -475,8 +489,8 @@ proc dealloc(a: var TAllocator, p: pointer) =
|
||||||
var s = c.size
|
var s = c.size
|
||||||
var f = cast[ptr TFreeCell](p)
|
var f = cast[ptr TFreeCell](p)
|
||||||
#echo("setting to nil: ", $cast[TAddress](addr(f.zeroField)))
|
#echo("setting to nil: ", $cast[TAddress](addr(f.zeroField)))
|
||||||
assert(f.zeroField != nil)
|
assert(f.zeroField != 0)
|
||||||
f.zeroField = nil
|
f.zeroField = 0
|
||||||
f.next = c.freeList
|
f.next = c.freeList
|
||||||
c.freeList = f
|
c.freeList = f
|
||||||
# check if it is not in the freeSmallChunks[s] list:
|
# check if it is not in the freeSmallChunks[s] list:
|
||||||
|
|
@ -495,23 +509,64 @@ proc dealloc(a: var TAllocator, p: pointer) =
|
||||||
# free big chunk
|
# free big chunk
|
||||||
freeBigChunk(a, cast[PBigChunk](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
|
|
||||||
result = alloc(a, size)
|
|
||||||
copyMem(result, p, getCellSize(p))
|
|
||||||
dealloc(a, p)
|
|
||||||
|
|
||||||
proc isAllocatedPtr(a: TAllocator, p: pointer): bool =
|
proc isAllocatedPtr(a: TAllocator, p: pointer): bool =
|
||||||
if isAccessible(p):
|
if isAccessible(p):
|
||||||
var c = pageAddr(p)
|
var c = pageAddr(p)
|
||||||
if not chunkUnused(c):
|
if not chunkUnused(c):
|
||||||
if isSmallChunk(c):
|
if isSmallChunk(c):
|
||||||
result = (cast[TAddress](p) -% cast[TAddress](c) -%
|
var c = cast[PSmallChunk](c)
|
||||||
smallChunkOverhead()) %% c.size == 0 and
|
var offset = (cast[TAddress](p) and (PageSize-1)) -%
|
||||||
cast[ptr TFreeCell](p).zeroField != nil
|
smallChunkOverhead()
|
||||||
|
result = (c.acc >% offset) and (offset %% c.size == 0) and
|
||||||
|
(cast[ptr TFreeCell](p).zeroField >% 1)
|
||||||
else:
|
else:
|
||||||
var c = cast[PBigChunk](c)
|
var c = cast[PBigChunk](c)
|
||||||
result = p == addr(c.data)
|
result = p == addr(c.data) and cast[ptr TFreeCell](p).zeroField >% 1
|
||||||
|
|
||||||
|
# ---------------------- interface to programs -------------------------------
|
||||||
|
|
||||||
|
proc alloc(size: int): pointer =
|
||||||
|
result = rawAlloc(allocator, size+sizeof(TFreeCell))
|
||||||
|
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
|
||||||
|
assert(not isAllocatedPtr(allocator, result))
|
||||||
|
result = cast[pointer](cast[TAddress](result) +% sizeof(TFreeCell))
|
||||||
|
|
||||||
|
proc alloc0(size: int): pointer =
|
||||||
|
result = alloc(size)
|
||||||
|
zeroMem(result, size)
|
||||||
|
|
||||||
|
proc dealloc(p: pointer) =
|
||||||
|
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||||
|
assert(cast[ptr TFreeCell](x).zeroField == 1)
|
||||||
|
rawDealloc(allocator, x)
|
||||||
|
assert(not isAllocatedPtr(allocator, x))
|
||||||
|
|
||||||
|
proc ptrSize(p: pointer): int =
|
||||||
|
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||||
|
result = pageAddr(x).size - sizeof(TFreeCell)
|
||||||
|
|
||||||
|
proc realloc(p: pointer, newsize: int): pointer =
|
||||||
|
if newsize > 0:
|
||||||
|
result = alloc(newsize)
|
||||||
|
if p != nil:
|
||||||
|
copyMem(result, p, ptrSize(p))
|
||||||
|
dealloc(p)
|
||||||
|
elif p != nil:
|
||||||
|
dealloc(p)
|
||||||
|
|
||||||
|
proc countFreeMem(): int =
|
||||||
|
# only used for assertions
|
||||||
|
var it = allocator.freeChunksList
|
||||||
|
while it != nil:
|
||||||
|
inc(result, it.size)
|
||||||
|
it = it.next
|
||||||
|
|
||||||
|
proc getFreeMem(): int =
|
||||||
|
result = allocator.freeMem
|
||||||
|
#assert(result == countFreeMem())
|
||||||
|
|
||||||
|
proc getTotalMem(): int = return allocator.currMem
|
||||||
|
proc getOccupiedMem(): int = return getTotalMem() - getFreeMem()
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
const iterations = 4000_000
|
const iterations = 4000_000
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2006 Andreas Rumpf
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -82,3 +82,7 @@ proc c_ferror(stream: C_TextFileStar): bool {.importc: "ferror", nodecl.}
|
||||||
proc c_fflush(stream: C_TextFileStar) {.importc: "fflush", nodecl.}
|
proc c_fflush(stream: C_TextFileStar) {.importc: "fflush", nodecl.}
|
||||||
proc c_abort() {.importc: "abort", nodecl.}
|
proc c_abort() {.importc: "abort", nodecl.}
|
||||||
proc c_feof(stream: C_TextFileStar): bool {.importc: "feof", nodecl.}
|
proc c_feof(stream: C_TextFileStar): bool {.importc: "feof", nodecl.}
|
||||||
|
|
||||||
|
proc c_malloc(size: int): pointer {.importc: "malloc", nodecl.}
|
||||||
|
proc c_free(p: pointer) {.importc: "free", nodecl.}
|
||||||
|
proc c_realloc(p: pointer, newsize: int): pointer {.importc: "realloc", nodecl.}
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,8 @@ proc genericAssign(dest, src: Pointer, mt: PNimType) =
|
||||||
x^ = nil
|
x^ = nil
|
||||||
return
|
return
|
||||||
assert(dest != nil)
|
assert(dest != nil)
|
||||||
when defined(boehmGC):
|
unsureAsgnRef(cast[ppointer](dest),
|
||||||
unsureAsgnRef(cast[ppointer](dest),
|
newObj(mt, seq.len * mt.base.size + GenericSeqSize))
|
||||||
newObj(seq.len * mt.base.size + GenericSeqSize))
|
|
||||||
else:
|
|
||||||
unsureAsgnRef(cast[ppointer](dest),
|
|
||||||
newObj(mt, seq.len * mt.base.size + GenericSeqSize))
|
|
||||||
var dst = cast[taddress](cast[ppointer](dest)^)
|
var dst = cast[taddress](cast[ppointer](dest)^)
|
||||||
for i in 0..seq.len-1:
|
for i in 0..seq.len-1:
|
||||||
genericAssign(
|
genericAssign(
|
||||||
|
|
|
||||||
129
lib/base/cgi.nim
129
lib/base/cgi.nim
|
|
@ -103,70 +103,76 @@ proc cgiError*(msg: string) {.noreturn.} =
|
||||||
e.msg = msg
|
e.msg = msg
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
proc getEncodedData(allowedMethods: set[TRequestMethod]): string =
|
||||||
|
case getenv("REQUEST_METHOD")
|
||||||
|
of "POST":
|
||||||
|
if methodPost notin allowedMethods:
|
||||||
|
cgiError("'REQUEST_METHOD' 'POST' is not supported")
|
||||||
|
var L = parseInt(getenv("CONTENT_LENGTH"))
|
||||||
|
result = newString(L)
|
||||||
|
if readBuffer(stdin, addr(result[0]), L) != L:
|
||||||
|
cgiError("cannot read from stdin")
|
||||||
|
of "GET":
|
||||||
|
if methodGet notin allowedMethods:
|
||||||
|
cgiError("'REQUEST_METHOD' 'GET' is not supported")
|
||||||
|
result = getenv("QUERY_STRING")
|
||||||
|
else:
|
||||||
|
if methodNone notin allowedMethods:
|
||||||
|
cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
|
||||||
|
|
||||||
|
iterator decodeData*(allowedMethods: set[TRequestMethod] =
|
||||||
|
{methodNone, methodPost, methodGet}): tuple[key, value: string] =
|
||||||
|
## Reads and decodes CGI data and yields the (name, value) pairs the
|
||||||
|
## data consists of. If the client does not use a method listed in the
|
||||||
|
## `allowedMethods` set, an `ECgi` exception is raised.
|
||||||
|
var enc = getEncodedData(allowedMethods)
|
||||||
|
if not isNil(enc):
|
||||||
|
# decode everything in one pass:
|
||||||
|
var i = 0
|
||||||
|
var name = ""
|
||||||
|
var value = ""
|
||||||
|
while enc[i] != '\0':
|
||||||
|
setLen(name, 0) # reuse memory
|
||||||
|
while true:
|
||||||
|
case enc[i]
|
||||||
|
of '\0': break
|
||||||
|
of '%':
|
||||||
|
var x = 0
|
||||||
|
handleHexChar(enc[i+1], x)
|
||||||
|
handleHexChar(enc[i+2], x)
|
||||||
|
inc(i, 2)
|
||||||
|
add(name, chr(x))
|
||||||
|
of '+': add(name, ' ')
|
||||||
|
of '=', '&': break
|
||||||
|
else: add(name, enc[i])
|
||||||
|
inc(i)
|
||||||
|
if enc[i] != '=': cgiError("'=' expected")
|
||||||
|
inc(i) # skip '='
|
||||||
|
setLen(value, 0) # reuse memory
|
||||||
|
while true:
|
||||||
|
case enc[i]
|
||||||
|
of '%':
|
||||||
|
var x = 0
|
||||||
|
handleHexChar(enc[i+1], x)
|
||||||
|
handleHexChar(enc[i+2], x)
|
||||||
|
inc(i, 2)
|
||||||
|
add(value, chr(x))
|
||||||
|
of '+': add(value, ' ')
|
||||||
|
of '&', '\0': break
|
||||||
|
else: add(value, enc[i])
|
||||||
|
inc(i)
|
||||||
|
yield (name, value)
|
||||||
|
if enc[i] == '&': inc(i)
|
||||||
|
elif enc[i] == '\0': break
|
||||||
|
else: cgiError("'&' expected")
|
||||||
|
|
||||||
proc readData*(allowedMethods: set[TRequestMethod] =
|
proc readData*(allowedMethods: set[TRequestMethod] =
|
||||||
{methodNone, methodPost, methodGet}): PStringTable =
|
{methodNone, methodPost, methodGet}): PStringTable =
|
||||||
## Read CGI data. If the client does not use a method listed in the
|
## Read CGI data. If the client does not use a method listed in the
|
||||||
## `allowedMethods` set, an `ECgi` exception is raised.
|
## `allowedMethods` set, an `ECgi` exception is raised.
|
||||||
result = newStringTable()
|
result = newStringTable()
|
||||||
var enc: string # encoded data
|
for name, value in decodeData(allowedMethods):
|
||||||
case getenv("REQUEST_METHOD")
|
|
||||||
of "POST":
|
|
||||||
if methodPost notin allowedMethods:
|
|
||||||
cgiError("'REQUEST_METHOD' 'POST' is not supported")
|
|
||||||
# read from stdin:
|
|
||||||
var L = parseInt(getenv("CONTENT_LENGTH"))
|
|
||||||
enc = newString(L)
|
|
||||||
if readBuffer(stdin, addr(enc[0]), L) != L:
|
|
||||||
cgiError("cannot read from stdin")
|
|
||||||
of "GET":
|
|
||||||
if methodGet notin allowedMethods:
|
|
||||||
cgiError("'REQUEST_METHOD' 'GET' is not supported")
|
|
||||||
# read from the QUERY_STRING environment variable:
|
|
||||||
enc = getenv("QUERY_STRING")
|
|
||||||
else:
|
|
||||||
if methodNone in allowedMethods:
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
cgiError("'REQUEST_METHOD' must be 'POST' or 'GET'")
|
|
||||||
|
|
||||||
# decode everything in one pass:
|
|
||||||
var i = 0
|
|
||||||
var name = ""
|
|
||||||
var value = ""
|
|
||||||
while true:
|
|
||||||
setLen(name, 0) # reuse memory
|
|
||||||
while true:
|
|
||||||
case enc[i]
|
|
||||||
of '\0': return
|
|
||||||
of '%':
|
|
||||||
var x = 0
|
|
||||||
handleHexChar(enc[i+1], x)
|
|
||||||
handleHexChar(enc[i+2], x)
|
|
||||||
inc(i, 2)
|
|
||||||
add(name, chr(x))
|
|
||||||
of '+': add(name, ' ')
|
|
||||||
of '=', '&': break
|
|
||||||
else: add(name, enc[i])
|
|
||||||
inc(i)
|
|
||||||
if enc[i] != '=': cgiError("'=' expected")
|
|
||||||
inc(i) # skip '='
|
|
||||||
setLen(value, 0) # reuse memory
|
|
||||||
while true:
|
|
||||||
case enc[i]
|
|
||||||
of '%':
|
|
||||||
var x = 0
|
|
||||||
handleHexChar(enc[i+1], x)
|
|
||||||
handleHexChar(enc[i+2], x)
|
|
||||||
inc(i, 2)
|
|
||||||
add(value, chr(x))
|
|
||||||
of '+': add(value, ' ')
|
|
||||||
of '&', '\0': break
|
|
||||||
else: add(value, enc[i])
|
|
||||||
inc(i)
|
|
||||||
result[name] = value
|
result[name] = value
|
||||||
if enc[i] == '&': inc(i)
|
|
||||||
elif enc[i] == '\0': break
|
|
||||||
else: cgiError("'&' expected")
|
|
||||||
|
|
||||||
proc validateData*(data: PStringTable, validKeys: openarray[string]) =
|
proc validateData*(data: PStringTable, validKeys: openarray[string]) =
|
||||||
## validates data; raises `ECgi` if this fails. This checks that each variable
|
## validates data; raises `ECgi` if this fails. This checks that each variable
|
||||||
|
|
@ -323,8 +329,13 @@ proc setTestData*(keysvalues: openarray[string]) =
|
||||||
|
|
||||||
proc writeContentType*() =
|
proc writeContentType*() =
|
||||||
## call this before starting to send your HTML data to `stdout`. This
|
## call this before starting to send your HTML data to `stdout`. This
|
||||||
## is just a shorthand for:
|
## implements this part of the CGI protocol:
|
||||||
##
|
##
|
||||||
## .. code-block:: Nimrod
|
## .. code-block:: Nimrod
|
||||||
## write(stdout, "Content-type: text/html\n\n")
|
## write(stdout, "Content-type: text/html\n\n")
|
||||||
|
##
|
||||||
|
## It also modifies the debug stack traces so that they contain
|
||||||
|
## ``<br />`` and are easily readable in a browser.
|
||||||
write(stdout, "Content-type: text/html\n\n")
|
write(stdout, "Content-type: text/html\n\n")
|
||||||
|
system.stackTraceNewLine = "<br />\n"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,8 @@ var
|
||||||
|
|
||||||
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
|
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
|
||||||
|
|
||||||
|
stackTraceNewLine* = "\n" ## undocumented feature
|
||||||
|
|
||||||
proc auxWriteStackTrace(f: PFrame, s: var string) =
|
proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||||
const
|
const
|
||||||
firstCalls = 32
|
firstCalls = 32
|
||||||
|
|
@ -101,19 +103,21 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||||
if tempFrames[j] == nil:
|
if tempFrames[j] == nil:
|
||||||
add(s, "(")
|
add(s, "(")
|
||||||
add(s, $(total-i-1))
|
add(s, $(total-i-1))
|
||||||
add(s, " calls omitted) ...\n")
|
add(s, " calls omitted) ...")
|
||||||
else:
|
else:
|
||||||
add(s, $tempFrames[j].procname)
|
add(s, $tempFrames[j].procname)
|
||||||
if tempFrames[j].line > 0:
|
if tempFrames[j].line > 0:
|
||||||
add(s, ", line: ")
|
add(s, ", line: ")
|
||||||
add(s, $tempFrames[j].line)
|
add(s, $tempFrames[j].line)
|
||||||
add(s, "\n")
|
add(s, stackTraceNewLine)
|
||||||
|
|
||||||
proc rawWriteStackTrace(s: var string) =
|
proc rawWriteStackTrace(s: var string) =
|
||||||
if framePtr == nil:
|
if framePtr == nil:
|
||||||
add(s, "No stack traceback available\n")
|
add(s, "No stack traceback available")
|
||||||
|
add(s, stackTraceNewLine)
|
||||||
else:
|
else:
|
||||||
add(s, "Traceback (most recent call last)\n")
|
add(s, "Traceback (most recent call last)")
|
||||||
|
add(s, stackTraceNewLine)
|
||||||
auxWriteStackTrace(framePtr, s)
|
auxWriteStackTrace(framePtr, s)
|
||||||
|
|
||||||
proc quitOrDebug() {.inline.} =
|
proc quitOrDebug() {.inline.} =
|
||||||
|
|
@ -151,6 +155,8 @@ var
|
||||||
|
|
||||||
proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
|
proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
|
||||||
if not cond:
|
if not cond:
|
||||||
|
#c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
|
||||||
|
#quit(1)
|
||||||
GC_disable() # BUGFIX: `$` allocates a new string object!
|
GC_disable() # BUGFIX: `$` allocates a new string object!
|
||||||
if not isNil(assertBuf):
|
if not isNil(assertBuf):
|
||||||
# BUGFIX: when debugging the GC, assertBuf may be nil
|
# BUGFIX: when debugging the GC, assertBuf may be nil
|
||||||
|
|
@ -162,7 +168,11 @@ proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
|
||||||
add(assertBuf, "\n")
|
add(assertBuf, "\n")
|
||||||
gAssertionFailed.msg = assertBuf
|
gAssertionFailed.msg = assertBuf
|
||||||
GC_enable()
|
GC_enable()
|
||||||
raise gAssertionFailed # newException(EAssertionFailed, assertBuf)
|
if gAssertionFailed != nil:
|
||||||
|
raise gAssertionFailed
|
||||||
|
else:
|
||||||
|
c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line)
|
||||||
|
quit(1)
|
||||||
|
|
||||||
proc WriteStackTrace() =
|
proc WriteStackTrace() =
|
||||||
var s = ""
|
var s = ""
|
||||||
|
|
|
||||||
360
lib/gc.nim
360
lib/gc.nim
|
|
@ -9,23 +9,17 @@
|
||||||
|
|
||||||
|
|
||||||
# Garbage Collector
|
# Garbage Collector
|
||||||
# Current Features:
|
#
|
||||||
# * incremental
|
# The basic algorithm is *Deferrent Reference Counting* with cycle detection.
|
||||||
# * non-recursive
|
# Special care has been taken to avoid recursion as far as possible to avoid
|
||||||
# * generational
|
# stack overflows when traversing deep datastructures. This is comparable to
|
||||||
|
# an incremental and generational GC. It should be well-suited for soft real
|
||||||
|
# time applications (like games).
|
||||||
|
#
|
||||||
# Future Improvements:
|
# Future Improvements:
|
||||||
# * Support for multi-threading. However, locks for the reference counting
|
# * Support for multi-threading. However, locks for the reference counting
|
||||||
# might turn out to be too slow.
|
# might turn out to be too slow.
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
proc getOccupiedMem(): int = return tlsfUsed()
|
|
||||||
proc getFreeMem(): int = return tlsfMax() - tlsfUsed()
|
|
||||||
proc getTotalMem(): int = return tlsfMax()
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const
|
const
|
||||||
CycleIncrease = 2 # is a multiplicative increase
|
CycleIncrease = 2 # is a multiplicative increase
|
||||||
InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
|
InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow
|
||||||
|
|
@ -36,14 +30,14 @@ const
|
||||||
const
|
const
|
||||||
rcIncrement = 0b1000 # so that lowest 3 bits are not touched
|
rcIncrement = 0b1000 # so that lowest 3 bits are not touched
|
||||||
# NOTE: Most colors are currently unused
|
# NOTE: Most colors are currently unused
|
||||||
rcBlack = 0b000 # cell is colored black; in use or free
|
rcBlack = 0b000 # cell is colored black; in use or free
|
||||||
rcGray = 0b001 # possible member of a cycle
|
rcGray = 0b001 # possible member of a cycle
|
||||||
rcWhite = 0b010 # member of a garbage cycle
|
rcWhite = 0b010 # member of a garbage cycle
|
||||||
rcPurple = 0b011 # possible root of a cycle
|
rcPurple = 0b011 # possible root of a cycle
|
||||||
rcZct = 0b100 # in ZCT
|
rcZct = 0b100 # in ZCT
|
||||||
rcRed = 0b101 # Candidate cycle undergoing sigma-computation
|
rcRed = 0b101 # Candidate cycle undergoing sigma-computation
|
||||||
rcOrange = 0b110 # Candidate cycle awaiting epoch boundary
|
rcOrange = 0b110 # Candidate cycle awaiting epoch boundary
|
||||||
rcShift = 3 # shift by rcShift to get the reference counter
|
rcShift = 3 # shift by rcShift to get the reference counter
|
||||||
colorMask = 0b111
|
colorMask = 0b111
|
||||||
type
|
type
|
||||||
TWalkOp = enum
|
TWalkOp = enum
|
||||||
|
|
@ -52,21 +46,22 @@ type
|
||||||
TFinalizer {.compilerproc.} = proc (self: pointer)
|
TFinalizer {.compilerproc.} = proc (self: pointer)
|
||||||
# A ref type can have a finalizer that is called before the object's
|
# A ref type can have a finalizer that is called before the object's
|
||||||
# storage is freed.
|
# storage is freed.
|
||||||
TGcHeap {.final, pure.} = object # this contains the zero count and
|
|
||||||
# non-zero count table
|
|
||||||
mask: TAddress # mask for fast pointer detection
|
|
||||||
zct: TCellSeq # the zero count table
|
|
||||||
stackCells: TCellSet # cells and addresses that look like a cell but
|
|
||||||
# aren't of the hardware stack
|
|
||||||
|
|
||||||
|
TGcStat {.final, pure.} = object
|
||||||
stackScans: int # number of performed stack scans (for statistics)
|
stackScans: int # number of performed stack scans (for statistics)
|
||||||
cycleCollections: int # number of performed full collections
|
cycleCollections: int # number of performed full collections
|
||||||
maxThreshold: int # max threshold that has been set
|
maxThreshold: int # max threshold that has been set
|
||||||
maxStackSize: int # max stack size
|
maxStackSize: int # max stack size
|
||||||
maxStackPages: int # max number of pages in stack
|
maxStackCells: int # max stack cells in ``decStack``
|
||||||
cycleTableSize: int # max entries in cycle table
|
cycleTableSize: int # max entries in cycle table
|
||||||
|
|
||||||
|
TGcHeap {.final, pure.} = object # this contains the zero count and
|
||||||
|
# non-zero count table
|
||||||
|
zct: TCellSeq # the zero count table
|
||||||
|
decStack: TCellSeq # cells in the stack that are to decref again
|
||||||
cycleRoots: TCellSet
|
cycleRoots: TCellSet
|
||||||
tempStack: TCellSeq # temporary stack for recursion elimination
|
tempStack: TCellSeq # temporary stack for recursion elimination
|
||||||
|
stat: TGcStat
|
||||||
|
|
||||||
var
|
var
|
||||||
stackBottom: pointer
|
stackBottom: pointer
|
||||||
|
|
@ -82,13 +77,13 @@ var
|
||||||
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc.}
|
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerproc.}
|
||||||
# unsureAsgnRef updates the reference counters only if dest is not on the
|
# unsureAsgnRef updates the reference counters only if dest is not on the
|
||||||
# stack. It is used by the code generator if it cannot decide wether a
|
# stack. It is used by the code generator if it cannot decide wether a
|
||||||
# reference is in the stack or not (this can happen for out/var parameters).
|
# reference is in the stack or not (this can happen for var parameters).
|
||||||
#proc growObj(old: pointer, newsize: int): pointer {.compilerproc.}
|
#proc growObj(old: pointer, newsize: int): pointer {.compilerproc.}
|
||||||
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.}
|
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.}
|
||||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.}
|
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.}
|
||||||
|
|
||||||
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
|
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
|
||||||
if (c.refcount and colorMask) != rcZct:
|
if (c.refcount and rcZct) == 0:
|
||||||
c.refcount = c.refcount and not colorMask or rcZct
|
c.refcount = c.refcount and not colorMask or rcZct
|
||||||
add(s, c)
|
add(s, c)
|
||||||
|
|
||||||
|
|
@ -131,7 +126,7 @@ proc GC_disableMarkAndSweep() =
|
||||||
# set to the max value to suppress the cycle detector
|
# set to the max value to suppress the cycle detector
|
||||||
|
|
||||||
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
|
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
|
||||||
when BitsPerPage mod BitsPerUnit != 0:
|
when BitsPerPage mod (sizeof(int)*8) != 0:
|
||||||
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
||||||
|
|
||||||
when debugGC:
|
when debugGC:
|
||||||
|
|
@ -203,8 +198,7 @@ template gcTrace(cell, state: expr): stmt =
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
# forward declarations:
|
# forward declarations:
|
||||||
proc updateZCT()
|
proc collectCT(gch: var TGcHeap)
|
||||||
proc collectCT(gch: var TGcHeap, zctUpdated: bool)
|
|
||||||
proc IsOnStack(p: pointer): bool {.noinline.}
|
proc IsOnStack(p: pointer): bool {.noinline.}
|
||||||
proc forAllChildren(cell: PCell, op: TWalkOp)
|
proc forAllChildren(cell: PCell, op: TWalkOp)
|
||||||
proc doOperation(p: pointer, op: TWalkOp)
|
proc doOperation(p: pointer, op: TWalkOp)
|
||||||
|
|
@ -232,7 +226,7 @@ proc decRef(c: PCell) {.inline.} =
|
||||||
when stressGC:
|
when stressGC:
|
||||||
if c.refcount <% rcIncrement:
|
if c.refcount <% rcIncrement:
|
||||||
writeCell("broken cell", c)
|
writeCell("broken cell", c)
|
||||||
assert(c.refcount >% rcIncrement)
|
assert(c.refcount >=% rcIncrement)
|
||||||
c.refcount = c.refcount -% rcIncrement
|
c.refcount = c.refcount -% rcIncrement
|
||||||
if c.refcount <% rcIncrement:
|
if c.refcount <% rcIncrement:
|
||||||
addZCT(gch.zct, c)
|
addZCT(gch.zct, c)
|
||||||
|
|
@ -242,7 +236,6 @@ proc decRef(c: PCell) {.inline.} =
|
||||||
proc incRef(c: PCell) {.inline.} =
|
proc incRef(c: PCell) {.inline.} =
|
||||||
c.refcount = c.refcount +% rcIncrement
|
c.refcount = c.refcount +% rcIncrement
|
||||||
if canBeCycleRoot(c):
|
if canBeCycleRoot(c):
|
||||||
# OPT: the code generator should special case this
|
|
||||||
incl(gch.cycleRoots, c)
|
incl(gch.cycleRoots, c)
|
||||||
|
|
||||||
proc nimGCref(p: pointer) {.compilerproc, inline.} = incRef(usrToCell(p))
|
proc nimGCref(p: pointer) {.compilerproc, inline.} = incRef(usrToCell(p))
|
||||||
|
|
@ -277,19 +270,18 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) =
|
||||||
|
|
||||||
proc initGC() =
|
proc initGC() =
|
||||||
when traceGC:
|
when traceGC:
|
||||||
for i in low(TCellState)..high(TCellState): CellSetInit(states[i])
|
for i in low(TCellState)..high(TCellState): Init(states[i])
|
||||||
gch.stackScans = 0
|
gch.stat.stackScans = 0
|
||||||
gch.cycleCollections = 0
|
gch.stat.cycleCollections = 0
|
||||||
gch.maxThreshold = 0
|
gch.stat.maxThreshold = 0
|
||||||
gch.maxStackSize = 0
|
gch.stat.maxStackSize = 0
|
||||||
gch.maxStackPages = 0
|
gch.stat.maxStackCells = 0
|
||||||
gch.cycleTableSize = 0
|
gch.stat.cycleTableSize = 0
|
||||||
# init the rt
|
# init the rt
|
||||||
init(gch.zct)
|
init(gch.zct)
|
||||||
init(gch.tempStack)
|
init(gch.tempStack)
|
||||||
CellSetInit(gch.cycleRoots)
|
Init(gch.cycleRoots)
|
||||||
CellSetInit(gch.stackCells)
|
Init(gch.decStack)
|
||||||
gch.mask = 0
|
|
||||||
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
|
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
|
||||||
|
|
||||||
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
|
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
|
||||||
|
|
@ -333,22 +325,18 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
|
||||||
of tyString: nil
|
of tyString: nil
|
||||||
else: assert(false)
|
else: assert(false)
|
||||||
|
|
||||||
proc checkCollection(zctUpdated: bool) {.inline.} =
|
proc checkCollection {.inline.} =
|
||||||
# checks if a collection should be done
|
# checks if a collection should be done
|
||||||
if recGcLock == 0:
|
if recGcLock == 0:
|
||||||
collectCT(gch, zctUpdated)
|
collectCT(gch)
|
||||||
|
|
||||||
proc newObj(typ: PNimType, size: int): pointer =
|
proc newObj(typ: PNimType, size: int): pointer =
|
||||||
# generates a new object and sets its reference counter to 0
|
# generates a new object and sets its reference counter to 0
|
||||||
assert(typ.kind in {tyRef, tyString, tySequence})
|
assert(typ.kind in {tyRef, tyString, tySequence})
|
||||||
var zctUpdated = false
|
checkCollection()
|
||||||
if gch.zct.len >= ZctThreshold:
|
var res = cast[PCell](rawAlloc(allocator, size + sizeof(TCell)))
|
||||||
updateZCT()
|
zeroMem(res, size+sizeof(TCell))
|
||||||
zctUpdated = true
|
assert((cast[TAddress](res) and (MemAlign-1)) == 0)
|
||||||
# check if we have to collect:
|
|
||||||
checkCollection(zctUpdated)
|
|
||||||
var res = cast[PCell](gcAlloc(size + sizeof(TCell)))
|
|
||||||
when stressGC: assert((cast[TAddress](res) and (MemAlignment-1)) == 0)
|
|
||||||
# now it is buffered in the ZCT
|
# now it is buffered in the ZCT
|
||||||
res.typ = typ
|
res.typ = typ
|
||||||
when debugGC:
|
when debugGC:
|
||||||
|
|
@ -356,31 +344,44 @@ proc newObj(typ: PNimType, size: int): pointer =
|
||||||
res.filename = framePtr.prev.filename
|
res.filename = framePtr.prev.filename
|
||||||
res.line = framePtr.prev.line
|
res.line = framePtr.prev.line
|
||||||
res.refcount = rcZct # refcount is zero, but mark it to be in the ZCT
|
res.refcount = rcZct # refcount is zero, but mark it to be in the ZCT
|
||||||
add(gch.zct, res) # its refcount is zero, so add it to the ZCT
|
assert(isAllocatedPtr(allocator, res))
|
||||||
gch.mask = gch.mask or cast[TAddress](res)
|
# its refcount is zero, so add it to the ZCT:
|
||||||
|
block addToZCT:
|
||||||
|
# we check the last 8 entries (cache line) for a slot
|
||||||
|
# that could be reused
|
||||||
|
var L = gch.zct.len
|
||||||
|
var d = gch.zct.d
|
||||||
|
for i in countdown(L-1, max(0, L-8)):
|
||||||
|
var c = d[i]
|
||||||
|
if c.refcount >=% rcIncrement:
|
||||||
|
c.refcount = c.refcount and not colorMask
|
||||||
|
d[i] = res
|
||||||
|
break addToZCT
|
||||||
|
add(gch.zct, res)
|
||||||
when logGC: writeCell("new cell", res)
|
when logGC: writeCell("new cell", res)
|
||||||
gcTrace(res, csAllocated)
|
gcTrace(res, csAllocated)
|
||||||
result = cellToUsr(res)
|
result = cellToUsr(res)
|
||||||
|
|
||||||
proc newSeq(typ: PNimType, len: int): pointer =
|
proc newSeq(typ: PNimType, len: int): pointer =
|
||||||
# XXX: overflow checks!
|
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
|
||||||
result = newObj(typ, len * typ.base.size + GenericSeqSize)
|
|
||||||
cast[PGenericSeq](result).len = len
|
cast[PGenericSeq](result).len = len
|
||||||
cast[PGenericSeq](result).space = len
|
cast[PGenericSeq](result).space = len
|
||||||
|
|
||||||
proc growObj(old: pointer, newsize: int): pointer =
|
proc growObj(old: pointer, newsize: int): pointer =
|
||||||
checkCollection(false)
|
checkCollection()
|
||||||
var ol = usrToCell(old)
|
var ol = usrToCell(old)
|
||||||
assert(ol.typ != nil)
|
assert(ol.typ != nil)
|
||||||
assert(ol.typ.kind in {tyString, tySequence})
|
assert(ol.typ.kind in {tyString, tySequence})
|
||||||
var res = cast[PCell](gcAlloc(newsize + sizeof(TCell)))
|
var res = cast[PCell](rawAlloc(allocator, newsize + sizeof(TCell)))
|
||||||
var elemSize = 1
|
var elemSize = 1
|
||||||
if ol.typ.kind != tyString:
|
if ol.typ.kind != tyString:
|
||||||
elemSize = ol.typ.base.size
|
elemSize = ol.typ.base.size
|
||||||
copyMem(res, ol, cast[PGenericSeq](old).len*elemSize +
|
|
||||||
GenericSeqSize + sizeof(TCell))
|
|
||||||
|
|
||||||
assert((cast[TAddress](res) and (MemAlignment-1)) == 0)
|
var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize
|
||||||
|
copyMem(res, ol, oldsize + sizeof(TCell))
|
||||||
|
zeroMem(cast[pointer](cast[TAddress](res)+% oldsize +% sizeof(TCell)),
|
||||||
|
newsize-oldsize)
|
||||||
|
assert((cast[TAddress](res) and (MemAlign-1)) == 0)
|
||||||
assert(res.refcount shr rcShift <=% 1)
|
assert(res.refcount shr rcShift <=% 1)
|
||||||
#if res.refcount <% rcIncrement:
|
#if res.refcount <% rcIncrement:
|
||||||
# add(gch.zct, res)
|
# add(gch.zct, res)
|
||||||
|
|
@ -395,13 +396,12 @@ proc growObj(old: pointer, newsize: int): pointer =
|
||||||
break
|
break
|
||||||
dec(j)
|
dec(j)
|
||||||
if canBeCycleRoot(ol): excl(gch.cycleRoots, ol)
|
if canBeCycleRoot(ol): excl(gch.cycleRoots, ol)
|
||||||
gch.mask = gch.mask or cast[TAddress](res)
|
|
||||||
when logGC:
|
when logGC:
|
||||||
writeCell("growObj old cell", ol)
|
writeCell("growObj old cell", ol)
|
||||||
writeCell("growObj new cell", res)
|
writeCell("growObj new cell", res)
|
||||||
gcTrace(ol, csZctFreed)
|
gcTrace(ol, csZctFreed)
|
||||||
gcTrace(res, csAllocated)
|
gcTrace(res, csAllocated)
|
||||||
when reallyDealloc: tlsf_free(ol)
|
when reallyDealloc: rawDealloc(allocator, ol)
|
||||||
else:
|
else:
|
||||||
assert(ol.typ != nil)
|
assert(ol.typ != nil)
|
||||||
zeroMem(ol, sizeof(TCell))
|
zeroMem(ol, sizeof(TCell))
|
||||||
|
|
@ -409,13 +409,6 @@ proc growObj(old: pointer, newsize: int): pointer =
|
||||||
|
|
||||||
# ---------------- cycle collector -------------------------------------------
|
# ---------------- cycle collector -------------------------------------------
|
||||||
|
|
||||||
# When collecting cycles, we have to consider the following:
|
|
||||||
# * there may still be references in the stack
|
|
||||||
# * some cells may still be in the ZCT, because they are referenced from
|
|
||||||
# the stack (!), so their refcounts are zero
|
|
||||||
# the ZCT is a subset of stackCells here, so we only need to care
|
|
||||||
# for stackcells
|
|
||||||
|
|
||||||
proc doOperation(p: pointer, op: TWalkOp) =
|
proc doOperation(p: pointer, op: TWalkOp) =
|
||||||
if p == nil: return
|
if p == nil: return
|
||||||
var c: PCell = usrToCell(p)
|
var c: PCell = usrToCell(p)
|
||||||
|
|
@ -438,36 +431,25 @@ proc collectCycles(gch: var TGcHeap) =
|
||||||
for c in elements(gch.cycleRoots):
|
for c in elements(gch.cycleRoots):
|
||||||
inc(tabSize)
|
inc(tabSize)
|
||||||
forallChildren(c, waCycleDecRef)
|
forallChildren(c, waCycleDecRef)
|
||||||
gch.cycleTableSize = max(gch.cycleTableSize, tabSize)
|
gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize)
|
||||||
|
|
||||||
# restore reference counts (a depth-first traversal is needed):
|
# restore reference counts (a depth-first traversal is needed):
|
||||||
var marker, newRoots: TCellSet
|
var marker: TCellSet
|
||||||
CellSetInit(marker)
|
Init(marker)
|
||||||
CellSetInit(newRoots)
|
|
||||||
for c in elements(gch.cycleRoots):
|
for c in elements(gch.cycleRoots):
|
||||||
var needsRestore = false
|
if c.refcount >=% rcIncrement:
|
||||||
if c in gch.stackCells:
|
if not containsOrIncl(marker, c):
|
||||||
needsRestore = true
|
|
||||||
incl(newRoots, c)
|
|
||||||
# we need to scan this later again; maybe stack changes
|
|
||||||
# NOTE: adding to ZCT here does NOT work
|
|
||||||
elif c.refcount >=% rcIncrement:
|
|
||||||
needsRestore = true
|
|
||||||
if needsRestore:
|
|
||||||
if c notin marker:
|
|
||||||
incl(marker, c)
|
|
||||||
gch.tempStack.len = 0
|
gch.tempStack.len = 0
|
||||||
forAllChildren(c, waPush)
|
forAllChildren(c, waPush)
|
||||||
while gch.tempStack.len > 0:
|
while gch.tempStack.len > 0:
|
||||||
dec(gch.tempStack.len)
|
dec(gch.tempStack.len)
|
||||||
var d = gch.tempStack.d[gch.tempStack.len]
|
var d = gch.tempStack.d[gch.tempStack.len]
|
||||||
d.refcount = d.refcount +% rcIncrement
|
d.refcount = d.refcount +% rcIncrement
|
||||||
if d notin marker and d in gch.cycleRoots:
|
if d in gch.cycleRoots and not containsOrIncl(marker, d):
|
||||||
incl(marker, d)
|
|
||||||
forAllChildren(d, waPush)
|
forAllChildren(d, waPush)
|
||||||
# remove cycles:
|
# remove cycles:
|
||||||
for c in elements(gch.cycleRoots):
|
for c in elements(gch.cycleRoots):
|
||||||
if c.refcount <% rcIncrement and c notin gch.stackCells:
|
if c.refcount <% rcIncrement:
|
||||||
gch.tempStack.len = 0
|
gch.tempStack.len = 0
|
||||||
forAllChildren(c, waPush)
|
forAllChildren(c, waPush)
|
||||||
while gch.tempStack.len > 0:
|
while gch.tempStack.len > 0:
|
||||||
|
|
@ -480,21 +462,23 @@ proc collectCycles(gch: var TGcHeap) =
|
||||||
prepareDealloc(c)
|
prepareDealloc(c)
|
||||||
gcTrace(c, csCycFreed)
|
gcTrace(c, csCycFreed)
|
||||||
when logGC: writeCell("cycle collector dealloc cell", c)
|
when logGC: writeCell("cycle collector dealloc cell", c)
|
||||||
when reallyDealloc: tlsf_free(c)
|
when reallyDealloc: rawDealloc(allocator, c)
|
||||||
else:
|
else:
|
||||||
assert(c.typ != nil)
|
assert(c.typ != nil)
|
||||||
zeroMem(c, sizeof(TCell))
|
zeroMem(c, sizeof(TCell))
|
||||||
CellSetDeinit(gch.cycleRoots)
|
Deinit(gch.cycleRoots)
|
||||||
gch.cycleRoots = newRoots
|
Init(gch.cycleRoots)
|
||||||
|
|
||||||
proc gcMark(p: pointer) {.noinline.} =
|
proc gcMark(p: pointer) {.inline.} =
|
||||||
# the addresses are not as objects on the stack, so turn them to objects:
|
# the addresses are not as objects on the stack, so turn them to objects:
|
||||||
var cell = usrToCell(p)
|
var cell = usrToCell(p)
|
||||||
var c = cast[TAddress](cell)
|
var c = cast[TAddress](cell)
|
||||||
if ((c and gch.mask) == c) and c >% 1024:
|
if c >% PageSize:
|
||||||
# fast check: does it look like a cell?
|
# fast check: does it look like a cell?
|
||||||
when logGC: cfprintf(cstdout, "in stackcells %p\n", cell)
|
if isAllocatedPtr(allocator, cell):
|
||||||
incl(gch.stackCells, cell) # yes: mark it
|
# mark the cell:
|
||||||
|
cell.refcount = cell.refcount +% rcIncrement
|
||||||
|
add(gch.decStack, cell)
|
||||||
|
|
||||||
# ----------------- stack management --------------------------------------
|
# ----------------- stack management --------------------------------------
|
||||||
# inspired from Smart Eiffel
|
# inspired from Smart Eiffel
|
||||||
|
|
@ -560,53 +544,6 @@ elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
|
||||||
gcMark(sp^)
|
gcMark(sp^)
|
||||||
sp = cast[ppointer](cast[TAddress](sp) -% sizeof(pointer))
|
sp = cast[ppointer](cast[TAddress](sp) -% sizeof(pointer))
|
||||||
|
|
||||||
elif defined(I386) and asmVersion:
|
|
||||||
# addresses decrease as the stack grows:
|
|
||||||
proc isOnStack(p: pointer): bool =
|
|
||||||
var
|
|
||||||
stackTop: array [0..1, pointer]
|
|
||||||
result = p >= addr(stackTop[0]) and p <= stackBottom
|
|
||||||
|
|
||||||
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
|
|
||||||
# This code should be safe even for aggressive optimizers. The try
|
|
||||||
# statement safes all registers into the safepoint, which we
|
|
||||||
# scan additionally to the stack.
|
|
||||||
type
|
|
||||||
TPtrArray = array[0..0xffffff, pointer]
|
|
||||||
try:
|
|
||||||
var pa = cast[ptr TPtrArray](excHandler)
|
|
||||||
for i in 0 .. sizeof(TSafePoint) - 1:
|
|
||||||
gcMark(pa[i])
|
|
||||||
finally:
|
|
||||||
# iterate over the stack:
|
|
||||||
var max = cast[TAddress](stackBottom)
|
|
||||||
var stackTop{.volatile.}: array [0..15, pointer]
|
|
||||||
var sp {.volatile.} = cast[TAddress](addr(stackTop[0]))
|
|
||||||
while sp <= max:
|
|
||||||
gcMark(cast[ppointer](sp)^)
|
|
||||||
sp = sp +% sizeof(pointer)
|
|
||||||
when false:
|
|
||||||
var counter = 0
|
|
||||||
#mov ebx, OFFSET `stackBottom`
|
|
||||||
#mov ebx, [ebx]
|
|
||||||
asm """
|
|
||||||
pusha
|
|
||||||
mov edi, esp
|
|
||||||
call `getStackBottom`
|
|
||||||
mov ebx, eax
|
|
||||||
L1:
|
|
||||||
cmp edi, ebx
|
|
||||||
ja L2
|
|
||||||
mov eax, [edi]
|
|
||||||
call `gcMark`
|
|
||||||
add edi, 4
|
|
||||||
inc [`counter`]
|
|
||||||
jmp L1
|
|
||||||
L2:
|
|
||||||
popa
|
|
||||||
"""
|
|
||||||
cfprintf(cstdout, "stack %ld\n", counter)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Generic code for architectures where addresses decrease as the stack grows.
|
# Generic code for architectures where addresses decrease as the stack grows.
|
||||||
|
|
@ -617,82 +554,47 @@ else:
|
||||||
result = p >= addr(stackTop[0]) and p <= stackBottom
|
result = p >= addr(stackTop[0]) and p <= stackBottom
|
||||||
|
|
||||||
var
|
var
|
||||||
gRegisters: C_JmpBuf
|
|
||||||
jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
|
jmpbufSize {.importc: "sizeof(jmp_buf)", nodecl.}: int
|
||||||
# a little hack to get the size of a TJmpBuf in the generated C code
|
# a little hack to get the size of a TJmpBuf in the generated C code
|
||||||
# in a platform independant way
|
# in a platform independant way
|
||||||
|
|
||||||
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
|
proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} =
|
||||||
when false:
|
var
|
||||||
# new version: several C compilers are too smart here
|
max = stackBottom
|
||||||
var
|
registers: C_JmpBuf # The jmp_buf buffer is in the C stack.
|
||||||
max = cast[TAddress](stackBottom)
|
sp: PPointer # Used to traverse the stack and registers assuming
|
||||||
stackTop: array [0..15, pointer]
|
# that 'setjmp' will save registers in the C stack.
|
||||||
if c_setjmp(gregisters) == 0'i32: # To fill the C stack with registers.
|
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
|
||||||
# iterate over the registers:
|
sp = cast[ppointer](addr(registers))
|
||||||
var sp = cast[TAddress](addr(gregisters))
|
while sp <= max:
|
||||||
while sp < cast[TAddress](addr(gregisters))+%jmpbufSize:
|
gcMark(sp^)
|
||||||
gcMark(cast[ppointer](sp)^)
|
sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer))
|
||||||
sp = sp +% sizeof(pointer)
|
|
||||||
# iterate over the stack:
|
|
||||||
sp = cast[TAddress](addr(stackTop[0]))
|
|
||||||
while sp <= max:
|
|
||||||
gcMark(cast[ppointer](sp)^)
|
|
||||||
sp = sp +% sizeof(pointer)
|
|
||||||
else:
|
|
||||||
c_longjmp(gregisters, 42)
|
|
||||||
# this can never happen, but should trick any compiler that is
|
|
||||||
# not as smart as a human
|
|
||||||
else:
|
|
||||||
var
|
|
||||||
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.
|
|
||||||
if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
|
|
||||||
sp = cast[ppointer](addr(registers))
|
|
||||||
while sp <= max:
|
|
||||||
gcMark(sp^)
|
|
||||||
sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer))
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# end of non-portable code
|
# end of non-portable code
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
proc updateZCT() =
|
|
||||||
# We have to make an additional pass over the ZCT unfortunately, because
|
|
||||||
# the ZCT may be out of date, which means it contains cells with a
|
|
||||||
# refcount > 0. The reason is that ``incRef`` does not bother to remove
|
|
||||||
# the cell from the ZCT as this might be too slow.
|
|
||||||
var j = 0
|
|
||||||
var L = gch.zct.len # because globals make it hard for the optimizer
|
|
||||||
var d = gch.zct.d
|
|
||||||
while j < L:
|
|
||||||
var c = d[j]
|
|
||||||
if c.refcount >=% rcIncrement:
|
|
||||||
when logGC: writeCell("remove from ZCT", c)
|
|
||||||
# remove from ZCT:
|
|
||||||
dec(L)
|
|
||||||
d[j] = d[L]
|
|
||||||
c.refcount = c.refcount and not colorMask
|
|
||||||
# we have a new cell at position j, so don't increment j
|
|
||||||
else:
|
|
||||||
inc(j)
|
|
||||||
gch.zct.len = L
|
|
||||||
|
|
||||||
proc CollectZCT(gch: var TGcHeap) =
|
proc CollectZCT(gch: var TGcHeap) =
|
||||||
var i = 0
|
# Note: Freeing may add child objects to the ZCT! So essentially we do
|
||||||
while i < gch.zct.len:
|
# deep freeing, which is bad for incremental operation. In order to
|
||||||
var c = gch.zct.d[i]
|
# avoid a deep stack, we move objects to keep the ZCT small.
|
||||||
assert(c.refcount <% rcIncrement)
|
# This is performance critical!
|
||||||
|
var L = addr(gch.zct.len)
|
||||||
|
while L^ > 0:
|
||||||
|
var c = gch.zct.d[0]
|
||||||
|
# remove from ZCT:
|
||||||
assert((c.refcount and colorMask) == rcZct)
|
assert((c.refcount and colorMask) == rcZct)
|
||||||
if canBeCycleRoot(c): excl(gch.cycleRoots, c)
|
c.refcount = c.refcount and not colorMask
|
||||||
if c notin gch.stackCells:
|
gch.zct.d[0] = gch.zct.d[L^ - 1]
|
||||||
# remove from ZCT:
|
dec(L^)
|
||||||
c.refcount = c.refcount and not colorMask
|
if c.refcount <% rcIncrement:
|
||||||
gch.zct.d[i] = gch.zct.d[gch.zct.len-1]
|
# It may have a RC > 0, if it is in the hardware stack or
|
||||||
# we have a new cell at position i, so don't increment i
|
# it has not been removed yet from the ZCT. This is because
|
||||||
dec(gch.zct.len)
|
# ``incref`` does not bother to remove the cell from the ZCT
|
||||||
|
# as this might be too slow.
|
||||||
|
# In any case, it should be removed from the ZCT. But not
|
||||||
|
# freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!**
|
||||||
|
if canBeCycleRoot(c): excl(gch.cycleRoots, c)
|
||||||
when logGC: writeCell("zct dealloc cell", c)
|
when logGC: writeCell("zct dealloc cell", c)
|
||||||
gcTrace(c, csZctFreed)
|
gcTrace(c, csZctFreed)
|
||||||
# We are about to free the object, call the finalizer BEFORE its
|
# We are about to free the object, call the finalizer BEFORE its
|
||||||
|
|
@ -700,51 +602,53 @@ proc CollectZCT(gch: var TGcHeap) =
|
||||||
# access invalid memory. This is done by prepareDealloc():
|
# access invalid memory. This is done by prepareDealloc():
|
||||||
prepareDealloc(c)
|
prepareDealloc(c)
|
||||||
forAllChildren(c, waZctDecRef)
|
forAllChildren(c, waZctDecRef)
|
||||||
when reallyDealloc: tlsf_free(c)
|
when reallyDealloc: rawDealloc(allocator, c)
|
||||||
else:
|
else:
|
||||||
assert(c.typ != nil)
|
assert(c.typ != nil)
|
||||||
zeroMem(c, sizeof(TCell))
|
zeroMem(c, sizeof(TCell))
|
||||||
else:
|
|
||||||
inc(i)
|
|
||||||
when stressGC:
|
|
||||||
for j in 0..gch.zct.len-1: assert(gch.zct.d[j] in gch.stackCells)
|
|
||||||
|
|
||||||
proc collectCT(gch: var TGcHeap, zctUpdated: bool) =
|
proc unmarkStackAndRegisters(gch: var TGcHeap) =
|
||||||
|
var d = gch.decStack.d
|
||||||
|
for i in 0..gch.decStack.len-1:
|
||||||
|
assert isAllocatedPtr(allocator, d[i])
|
||||||
|
decRef(d[i]) # OPT: cannot create a cycle!
|
||||||
|
gch.decStack.len = 0
|
||||||
|
|
||||||
|
proc collectCT(gch: var TGcHeap) =
|
||||||
if gch.zct.len >= ZctThreshold or (cycleGC and
|
if gch.zct.len >= ZctThreshold or (cycleGC and
|
||||||
getOccupiedMem() >= cycleThreshold) or stressGC:
|
getOccupiedMem() >= cycleThreshold) or stressGC:
|
||||||
if not zctUpdated: updateZCT()
|
gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
|
||||||
gch.maxStackSize = max(gch.maxStackSize, stackSize())
|
assert(gch.decStack.len == 0)
|
||||||
CellSetInit(gch.stackCells)
|
|
||||||
markStackAndRegisters(gch)
|
markStackAndRegisters(gch)
|
||||||
gch.maxStackPages = max(gch.maxStackPages, gch.stackCells.counter)
|
gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
|
||||||
inc(gch.stackScans)
|
inc(gch.stat.stackScans)
|
||||||
collectZCT(gch)
|
collectZCT(gch)
|
||||||
when cycleGC:
|
when cycleGC:
|
||||||
if getOccupiedMem() >= cycleThreshold or stressGC:
|
if getOccupiedMem() >= cycleThreshold or stressGC:
|
||||||
collectCycles(gch)
|
collectCycles(gch)
|
||||||
collectZCT(gch)
|
collectZCT(gch)
|
||||||
inc(gch.cycleCollections)
|
inc(gch.stat.cycleCollections)
|
||||||
cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
|
cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
|
||||||
cycleIncrease)
|
cycleIncrease)
|
||||||
gch.maxThreshold = max(gch.maxThreshold, cycleThreshold)
|
gch.stat.maxThreshold = max(gch.stat.maxThreshold, cycleThreshold)
|
||||||
CellSetDeinit(gch.stackCells)
|
unmarkStackAndRegisters(gch)
|
||||||
|
|
||||||
proc GC_fullCollect() =
|
proc GC_fullCollect() =
|
||||||
var oldThreshold = cycleThreshold
|
var oldThreshold = cycleThreshold
|
||||||
cycleThreshold = 0 # forces cycle collection
|
cycleThreshold = 0 # forces cycle collection
|
||||||
collectCT(gch, false)
|
collectCT(gch)
|
||||||
cycleThreshold = oldThreshold
|
cycleThreshold = oldThreshold
|
||||||
|
|
||||||
proc GC_getStatistics(): string =
|
proc GC_getStatistics(): string =
|
||||||
GC_disable()
|
GC_disable()
|
||||||
result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
|
result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
|
||||||
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
|
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
|
||||||
"[GC] stack scans: " & $gch.stackScans & "\n" &
|
"[GC] stack scans: " & $gch.stat.stackScans & "\n" &
|
||||||
"[GC] stack pages: " & $gch.maxStackPages & "\n" &
|
"[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
|
||||||
"[GC] cycle collections: " & $gch.cycleCollections & "\n" &
|
"[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
|
||||||
"[GC] max threshold: " & $gch.maxThreshold & "\n" &
|
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
|
||||||
"[GC] zct capacity: " & $gch.zct.cap & "\n" &
|
"[GC] zct capacity: " & $gch.zct.cap & "\n" &
|
||||||
"[GC] max cycle table size: " & $gch.cycleTableSize & "\n" &
|
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
||||||
"[GC] max stack size: " & $gch.maxStackSize
|
"[GC] max stack size: " & $gch.stat.maxStackSize
|
||||||
when traceGC: writeLeakage()
|
when traceGC: writeLeakage()
|
||||||
GC_enable()
|
GC_enable()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# The Nimrod Compiler
|
# The Nimrod Compiler
|
||||||
# (c) Copyright 2008 Andreas Rumpf
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,17 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2008 Andreas Rumpf
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
## This module contains the interface to the compiler's abstract syntax tree.
|
## This module contains the interface to the compiler's abstract syntax
|
||||||
## Abstract syntax trees should be modified in macros.
|
## tree (`AST`:idx:). Macros operate on this tree.
|
||||||
|
|
||||||
|
## .. include:: ../doc/astspec.txt
|
||||||
|
|
||||||
#[[[cog
|
#[[[cog
|
||||||
#def toEnum(name, elems):
|
#def toEnum(name, elems):
|
||||||
|
|
@ -235,3 +237,13 @@ proc newCall*(theProc: string,
|
||||||
result.add(newIdentNode(theProc))
|
result.add(newIdentNode(theProc))
|
||||||
result.add(args)
|
result.add(args)
|
||||||
|
|
||||||
|
proc nestList*(theProc: TNimrodIdent,
|
||||||
|
x: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## nests the list `x` into a tree of call expressions:
|
||||||
|
## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``
|
||||||
|
var L = x.len
|
||||||
|
result = newCall(theProc, x[L-2], x[L-1])
|
||||||
|
var a = result
|
||||||
|
for i in countdown(L-3, 0):
|
||||||
|
a = newCall(theProc, x[i], copyNimTree(a))
|
||||||
|
|
||||||
|
|
|
||||||
35
lib/math.nim
35
lib/math.nim
|
|
@ -208,5 +208,40 @@ else:
|
||||||
var y = exp(2.0*x)
|
var y = exp(2.0*x)
|
||||||
return (y-1.0)/(y+1.0)
|
return (y-1.0)/(y+1.0)
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
TRunningStat* = object ## an accumulator for statistical data
|
||||||
|
n*: int ## number of pushed data
|
||||||
|
sum*, min*, max*, mean*: float ## self-explaining
|
||||||
|
oldM, oldS, newS: float
|
||||||
|
|
||||||
|
proc push*(s: var TRunningStat, x: float) =
|
||||||
|
## pushes a value `x` for processing
|
||||||
|
inc(s.n)
|
||||||
|
# See Knuth TAOCP vol 2, 3rd edition, page 232
|
||||||
|
if s.n == 1:
|
||||||
|
s.oldM = x
|
||||||
|
s.mean = x
|
||||||
|
s.oldS = 0.0
|
||||||
|
else:
|
||||||
|
s.mean = s.oldM + (x - s.oldM)/toFloat(s.n)
|
||||||
|
s.newS = s.oldS + (x - s.oldM)*(x - s.mean)
|
||||||
|
|
||||||
|
# set up for next iteration:
|
||||||
|
s.oldM = s.mean
|
||||||
|
s.oldS = s.newS
|
||||||
|
|
||||||
|
s.sum = s.sum + x
|
||||||
|
if s.min > x: s.min = x
|
||||||
|
if s.max < x: s.max = x
|
||||||
|
|
||||||
|
proc variance*(s: TRunningStat): float =
|
||||||
|
## computes the current variance of `s`
|
||||||
|
if s.n > 1: result = s.newS / (toFloat(s.n - 1))
|
||||||
|
|
||||||
|
proc standardDeviation*(s: TRunningStat): float =
|
||||||
|
## computes the current standard deviation of `s`
|
||||||
|
result = sqrt(variance(s))
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
698
lib/memman.nim
698
lib/memman.nim
|
|
@ -1,698 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nimrod's Runtime Library
|
|
||||||
# (c) Copyright 2008 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
# Memory manager. Based on:
|
|
||||||
# Two Levels Segregate Fit memory allocator (TLSF)
|
|
||||||
# Version 2.4.2
|
|
||||||
#
|
|
||||||
# Written by Miguel Masmano Tello <mimastel@doctor.upv.es>
|
|
||||||
#
|
|
||||||
# Thanks to Ismael Ripoll for his suggestions and reviews
|
|
||||||
#
|
|
||||||
# Copyright (C) 2008, 2007, 2006, 2005, 2004
|
|
||||||
#
|
|
||||||
# This code is released using a dual license strategy: GPL/LGPL
|
|
||||||
# You can choose the licence that better fits your requirements.
|
|
||||||
#
|
|
||||||
# Released under the terms of the GNU General Public License Version 2.0
|
|
||||||
# Released under the terms of the GNU Lesser General Public License Version 2.1
|
|
||||||
|
|
||||||
|
|
||||||
# Some IMPORTANT TLSF parameters
|
|
||||||
const
|
|
||||||
blockAlign = sizeof(pointer) * 2
|
|
||||||
maxFli = 30
|
|
||||||
maxLog2Sli = 5
|
|
||||||
maxSli = 1 shl maxLog2Sli
|
|
||||||
|
|
||||||
fliOffset = 6 # tlsf structure just will manage blocks bigger than 128 bytes
|
|
||||||
smallBlock = 128
|
|
||||||
realFli = MaxFli - fliOffset
|
|
||||||
|
|
||||||
type
|
|
||||||
TFreePtr {.final.} = object
|
|
||||||
prev, next: PBhdr
|
|
||||||
Pbhdr = ptr Tbhdr
|
|
||||||
Tbhdr {.final.} = object
|
|
||||||
prevHdr: Pbhdr # this is just valid if the first bit of size is set
|
|
||||||
size: int # the size is stored in bytes
|
|
||||||
# bit 0 indicates whether the block is used and
|
|
||||||
# bit 1 allows to know whether the previous block is free
|
|
||||||
freePtr: TFreePtr # at this offset bhdr.buffer starts (was a union in the
|
|
||||||
# C version)
|
|
||||||
TAreaInfo {.final.} = object # This structure is embedded at the beginning
|
|
||||||
# of each area, giving us enough information
|
|
||||||
# to cope with a set of areas
|
|
||||||
theEnd: Pbhdr
|
|
||||||
next: PAreaInfo
|
|
||||||
PAreaInfo = ptr TAreaInfo
|
|
||||||
|
|
||||||
TLSF {.final.} = object
|
|
||||||
tlsf_signature: int32 # the TLSF's structure signature
|
|
||||||
usedSize, maxSize: int
|
|
||||||
areaHead: PAreaInfo # A linked list holding all the existing areas
|
|
||||||
|
|
||||||
flBitmap: int32 # the first-level bitmap
|
|
||||||
# This array should have a size of REAL_FLI bits
|
|
||||||
slBitmap: array[0..realFli, int32] # the second-level bitmap
|
|
||||||
matrix: array [0..realFli, array[0..maxSli, PBhdr]]
|
|
||||||
|
|
||||||
const
|
|
||||||
minBlockSize = sizeof(TFreePtr)
|
|
||||||
bhdrOverhead = sizeof(Tbhdr) - minBlockSize
|
|
||||||
tlsfSignature = 0x2A59FA59
|
|
||||||
ptrMask = sizeof(pointer) - 1
|
|
||||||
blockSize = 0xFFFFFFFF - ptrMask
|
|
||||||
memAlign = blockAlign - 1
|
|
||||||
blockState = 0x1
|
|
||||||
prevState = 0x2
|
|
||||||
|
|
||||||
freeBlock = 0x1 # bit 0 of the block size
|
|
||||||
usedBlock = 0x0
|
|
||||||
|
|
||||||
prevFree = 0x2 # bit 1 of the block size
|
|
||||||
prevUsed = 0x0
|
|
||||||
|
|
||||||
defaultAreaSize = 64*1024 # 1024*10
|
|
||||||
pageSize = if defined(cpu32): 4096 else: 4096*2
|
|
||||||
|
|
||||||
|
|
||||||
proc getNextBlock(adr: pointer, r: int): PBhdr {.inline.} =
|
|
||||||
return cast[PBhdr](cast[TAddress](adr) +% r)
|
|
||||||
|
|
||||||
proc roundupSize(r: int): int = return (r +% memAlign) and not memAlign
|
|
||||||
proc rounddownSize(r: int): int = return r and not memAlign
|
|
||||||
proc roundup(x, v: int): int = return (((not x)+%1) and (v-%1)) +% x
|
|
||||||
|
|
||||||
proc addSize(s: PTLSF, b: Pbhdr) =
|
|
||||||
inc(s.usedSize, (b.size and blockSize) + bhdrOverhead)
|
|
||||||
s.maxSize = max(s.maxSize, s.usedSize)
|
|
||||||
|
|
||||||
proc removeSize(s: PTLSF, b: Pbhdr) =
|
|
||||||
dec(s.usedSize, (b.size and blockSize) + bhdrOverhead)
|
|
||||||
|
|
||||||
# ------------ platform specific code -----------------------------------------
|
|
||||||
|
|
||||||
when defined(posix):
|
|
||||||
const # XXX: make these variables for portability?
|
|
||||||
PROT_READ = 1 # page can be read
|
|
||||||
PROT_WRITE = 2 # page can be written
|
|
||||||
PROT_EXEC = 4 # page can be executed
|
|
||||||
PROT_NONE = 0 # page can not be accessed
|
|
||||||
|
|
||||||
MAP_SHARED = 1 # Share changes
|
|
||||||
MAP_PRIVATE = 2 # Changes are private
|
|
||||||
MAP_TYPE = 0xf # Mask for type of mapping
|
|
||||||
MAP_FIXED = 0x10 # Interpret addr exactly
|
|
||||||
MAP_ANONYMOUS = 0x20 # don't use a file
|
|
||||||
|
|
||||||
MAP_GROWSDOWN = 0x100 # stack-like segment
|
|
||||||
MAP_DENYWRITE = 0x800 # ETXTBSY
|
|
||||||
MAP_EXECUTABLE = 0x1000 # mark it as an executable
|
|
||||||
MAP_LOCKED = 0x2000 # pages are locked
|
|
||||||
MAP_NORESERVE = 0x4000 # don't check for reservations
|
|
||||||
|
|
||||||
proc mmap(adr: pointer, len: int, prot, flags, fildes: cint,
|
|
||||||
off: int): pointer {.header: "<sys/mman.h>".}
|
|
||||||
|
|
||||||
proc getNewArea(size: var int): pointer {.inline.} =
|
|
||||||
size = roundup(size, PageSize)
|
|
||||||
result = mmap(0, size, PROT_READ or PROT_WRITE,
|
|
||||||
MAP_PRIVATE or MAP_ANONYMOUS, -1, 0)
|
|
||||||
if result == nil or result == cast[pointer](-1):
|
|
||||||
raiseOutOfMem()
|
|
||||||
|
|
||||||
elif defined(windows):
|
|
||||||
const
|
|
||||||
MEM_RESERVE = 0x2000
|
|
||||||
MEM_COMMIT = 0x1000
|
|
||||||
MEM_TOP_DOWN = 0x100000
|
|
||||||
PAGE_READWRITE = 0x04
|
|
||||||
|
|
||||||
proc VirtualAlloc(lpAddress: pointer, dwSize: int, flAllocationType,
|
|
||||||
flProtect: int32): pointer {.
|
|
||||||
header: "<windows.h>", stdcall.}
|
|
||||||
|
|
||||||
proc getNewArea(size: var int): pointer {.inline.} =
|
|
||||||
size = roundup(size, PageSize)
|
|
||||||
result = VirtualAlloc(nil, size, MEM_RESERVE or MEM_COMMIT or MEM_TOP_DOWN,
|
|
||||||
PAGE_READWRITE)
|
|
||||||
if result == nil: raiseOutOfMem()
|
|
||||||
|
|
||||||
else:
|
|
||||||
{.warning: "Generic code for allocating pages is used".}
|
|
||||||
# generic implementation relying on malloc:
|
|
||||||
proc malloc(size: int): pointer {.nodecl, importc.}
|
|
||||||
|
|
||||||
proc getNewArea(size: var int): pointer {.inline.} =
|
|
||||||
size = roundup(size, PageSize)
|
|
||||||
result = malloc(size)
|
|
||||||
if result == nil: raiseOutOfMem()
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# small fixed size allocator:
|
|
||||||
|
|
||||||
# Design: We manage pages. A page is of constant size, but not necessarily
|
|
||||||
# the OS's page size. Pages are managed in a hash table taking advantage of
|
|
||||||
# the fact that the OS is likely to give us pages with contingous numbers.
|
|
||||||
# A page contains either small fixed size objects of the same size or
|
|
||||||
# variable length objects. An object's size is always aligned at 16 byte
|
|
||||||
# boundary. Huge objects are dealt with the TLSF algorithm.
|
|
||||||
# The design supports iterating over any object in a fast way.
|
|
||||||
|
|
||||||
# A bitset contains any page that starts an allocated page. The page may be
|
|
||||||
# empty however. This bitset can be used to quickly determine if a given
|
|
||||||
# page belongs to the GC heap. The design of the memory allocator makes it
|
|
||||||
# simple to return unused pages back to the OS.
|
|
||||||
|
|
||||||
|
|
||||||
# Small bocks
|
|
||||||
# -----------
|
|
||||||
#
|
|
||||||
# If we use a list in the free object's space. Allocation and deallocation are
|
|
||||||
# O(1). Since any object is of the same size, iteration is quite efficient too.
|
|
||||||
# However, pointer detection is easy too: Just check if the type-field is nil.
|
|
||||||
# Deallocation sets it to nil.
|
|
||||||
# Algorithm:
|
|
||||||
|
|
||||||
# i = 0
|
|
||||||
# f = b.f # first free address
|
|
||||||
# while i < max:
|
|
||||||
# if a[i] == f: # not a valid block
|
|
||||||
# f = f.next # next free address
|
|
||||||
# else:
|
|
||||||
# a[i] is a valid object of size s
|
|
||||||
# inc(i)
|
|
||||||
|
|
||||||
# The zero count table is an array. Since we know that the RC is zero, we can
|
|
||||||
# use the bits for an index into this array. Thus multiple ZCT tables are not
|
|
||||||
# difficult to support and insertion and removal is O(1). We use negative
|
|
||||||
# indexes for this. This makes it even fast enough (and necessary!) to do a ZCT
|
|
||||||
# removal if the RC is incremented.
|
|
||||||
#
|
|
||||||
|
|
||||||
# Huge blocks
|
|
||||||
# -----------
|
|
||||||
#
|
|
||||||
# Huge blocks are always rounded up to a multiple of the page size. These are
|
|
||||||
# called *strides*. We also need to keep an index structure
|
|
||||||
# of (stridesize, pagelist).
|
|
||||||
#
|
|
||||||
|
|
||||||
const
|
|
||||||
MemAlign = 8
|
|
||||||
PageShift = if sizeof(int) == 4: 12 else: 13
|
|
||||||
PageSize = 1 shl PageShift
|
|
||||||
type
|
|
||||||
TFreeList {.final.} = object
|
|
||||||
next, prev: ptr TFreeList
|
|
||||||
|
|
||||||
TPageDesc {.final.} = object # the start of a stride always starts with this!
|
|
||||||
size: int # lowest bit is set, if it is a huge block
|
|
||||||
free: ptr TFreeList # only used if it manages multiple cells
|
|
||||||
snext, sprev: ptr TPageDesc # next and prev pagedescs with the same size
|
|
||||||
|
|
||||||
TCellArray {.final.} = object
|
|
||||||
i: int # length
|
|
||||||
d: ptr array [0..1000_000, TCell]
|
|
||||||
|
|
||||||
TPageManager = table[page, ptr TPageDesc]
|
|
||||||
|
|
||||||
TGcHeap {.final.} = object
|
|
||||||
# structure that manages the garbage collected heap
|
|
||||||
zct: TCellArray
|
|
||||||
stackCells: TCellArray
|
|
||||||
smallBlocks: array [PageSize div MemAlign, ptr TPageDesc]
|
|
||||||
freeLists: array [PageSize div MemAlign, ptr TFreeList]
|
|
||||||
pages: TPageManager
|
|
||||||
usedPages: TPageList
|
|
||||||
freePages: TPageList
|
|
||||||
|
|
||||||
# small blocks:
|
|
||||||
proc allocSmall(var h: TGcHeap, size: int): pointer =
|
|
||||||
var s = align(size)
|
|
||||||
var f = h.freeLists[s]
|
|
||||||
if f != nil:
|
|
||||||
f.prev = f.next # remove from list
|
|
||||||
f.next.prev = f.prev
|
|
||||||
return f
|
|
||||||
var p = h.smallBlocks[s]
|
|
||||||
if p == nil or p.free == nil:
|
|
||||||
p = newSmallBlock(s, p)
|
|
||||||
h.smallBlocks[s] = p
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc decRef(cell: PCell) {.inline.} =
|
|
||||||
assert(cell in ct.AT)
|
|
||||||
assert(cell.refcount > 0) # this should be the case!
|
|
||||||
assert(seqCheck(cell))
|
|
||||||
dec(cell.refcount)
|
|
||||||
if cell.refcount == 0:
|
|
||||||
# add to zero count table:
|
|
||||||
zct.d[zct.i] = cell
|
|
||||||
cell.recfcount = -zct.i
|
|
||||||
inc(zct.i)
|
|
||||||
|
|
||||||
proc incRef(cell: PCell) {.inline.} =
|
|
||||||
assert(seqCheck(cell))
|
|
||||||
if cell.refcount < 0:
|
|
||||||
# remove from zero count table:
|
|
||||||
zct.d[-cell.refcount] = zct.d[zct.i-1]
|
|
||||||
dec(zct.i)
|
|
||||||
cell.refcount = 1
|
|
||||||
else:
|
|
||||||
inc(cell.refcount)
|
|
||||||
|
|
||||||
proc asgnRef(dest: ppointer, src: pointer) =
|
|
||||||
# the code generator calls this proc!
|
|
||||||
assert(not isOnStack(dest))
|
|
||||||
# BUGFIX: first incRef then decRef!
|
|
||||||
if src != nil: incRef(usrToCell(src))
|
|
||||||
if dest^ != nil: decRef(usrToCell(dest^))
|
|
||||||
dest^ = src
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# helpers
|
|
||||||
|
|
||||||
const
|
|
||||||
table: array[0..255, int8] = [
|
|
||||||
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
|
||||||
6, 6, 6, 6, 6, 6, 6, 6,
|
|
||||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
|
||||||
6, 6, 6, 6, 6, 6, 6, 6,
|
|
||||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
|
||||||
7, 7, 7, 7, 7, 7, 7, 7,
|
|
||||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
|
||||||
7, 7, 7, 7, 7, 7, 7, 7,
|
|
||||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
|
||||||
7, 7, 7, 7, 7, 7, 7, 7,
|
|
||||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
|
||||||
7, 7, 7, 7, 7, 7, 7, 7
|
|
||||||
]
|
|
||||||
|
|
||||||
proc ls_bit(i: int32): int {.inline.} =
|
|
||||||
var
|
|
||||||
a: int = 0
|
|
||||||
x: int = i and -i
|
|
||||||
if x <=% 0xffff:
|
|
||||||
if x <=% ff: a = 0
|
|
||||||
else: a = 8
|
|
||||||
elif x <=% 0xffffff: a = 16
|
|
||||||
else: a = 24
|
|
||||||
return table[x shr a] + a
|
|
||||||
|
|
||||||
proc ms_bit(i: int): int {.inline.} =
|
|
||||||
var
|
|
||||||
a = if i <=% 0xffff: (if i <=% 0xff: 0 else: 8) elif
|
|
||||||
i <=% 0xffffff: 16 else: 24
|
|
||||||
return table[i shr a] + a
|
|
||||||
|
|
||||||
proc set_bit[IX](nr: int, adr: var array[IX, int32]) {.inline.} =
|
|
||||||
adr[nr shr 5] = adr[nr shr 5] or (1 shl (nr and 0x1f))
|
|
||||||
|
|
||||||
proc clear_bit[IX](nr: int, adr: var array[IX, int32]) {.inline.} =
|
|
||||||
adr[nr shr 5] = adr[nr shr 5] and not (1 shl (nr and 0x1f))
|
|
||||||
|
|
||||||
proc mappingSearch(r, fl, sl: var int) {.inline.} =
|
|
||||||
if r < smallBlock:
|
|
||||||
fl = 0
|
|
||||||
sl = r div (smallBlock div maxSli)
|
|
||||||
else:
|
|
||||||
var t = (1 shl (ms_bit(r) - maxLog2Sli)) - 1
|
|
||||||
r = r + t
|
|
||||||
fl = ms_bit(r)
|
|
||||||
sl = (r shl (fl - maxLog2Sli)) - maxSli
|
|
||||||
fl = fl - fliOffset
|
|
||||||
r = r and not t
|
|
||||||
|
|
||||||
proc mappingInsert(r: int, fl, sl: var int) {.inline.} =
|
|
||||||
if r < smallBlock:
|
|
||||||
fl = 0
|
|
||||||
sl = r div (smallBlock div maxSli)
|
|
||||||
else:
|
|
||||||
fl = ms_bit(r)
|
|
||||||
sl = (r shr (fl - maxLog2Sli)) - maxSli
|
|
||||||
fl = fl - fliOffset
|
|
||||||
|
|
||||||
proc findSuitableBlock(t: var TLSF, fl, sl: var int): Pbhdr =
|
|
||||||
var tmp = t.slBitmap[fl] and ((not 0) shl sl)
|
|
||||||
if tmp != 0:
|
|
||||||
sl = ls_bit(tmp)
|
|
||||||
result = t.matrix[fl][sl]
|
|
||||||
else:
|
|
||||||
fl = ls_bit(t.flBitmap and (not 0 shl (fl + 1)))
|
|
||||||
if fl > 0: # likely
|
|
||||||
sl = ls_bit(t.slBitmap[fl])
|
|
||||||
result = t.matrix[fl][sl]
|
|
||||||
|
|
||||||
proc extractBlockHdr(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
|
|
||||||
t.matrix[fl][sl] = b.freePtr.next
|
|
||||||
if t.matrix[fl][sl] != nil:
|
|
||||||
t.matrix[fl][sl].freePtr.prev = nil
|
|
||||||
else:
|
|
||||||
clear_bit(sl, t.slBitmap[fl])
|
|
||||||
if t.slBitmap[fl] == 0:
|
|
||||||
clear_bit(fl, t.flBitmap)
|
|
||||||
b.freePtr.prev = nil
|
|
||||||
b.freePtr.next = nil
|
|
||||||
|
|
||||||
proc extractBlock(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
|
|
||||||
if b.freePtr.next != nil:
|
|
||||||
b.freePtr.next.freePtr.prev = b.freePtr.prev
|
|
||||||
if b.freePtr.prev != nil:
|
|
||||||
b.freePtr.prev.freePtr.next = b.freePtr.next
|
|
||||||
if t.matrix[fl][sl] == b:
|
|
||||||
t.matrix[fl][sl] = b.freePtr.next
|
|
||||||
if t.matrix[fl][sl] == nil:
|
|
||||||
clear_bit(sl, t.slBitmap[fl])
|
|
||||||
if t.slBitmap[fl] == 0:
|
|
||||||
clear_bit(fl, t.flBitmap)
|
|
||||||
b.freePtr.prev = nil
|
|
||||||
b.freePtr.next = nil
|
|
||||||
|
|
||||||
proc insertBlock(b: Pbhdr, t: var TLSF, fl, sl: int) {.inline.} =
|
|
||||||
b.freePtr.prev = nil
|
|
||||||
b.freePtr.next = t.matrix[fl][sl]
|
|
||||||
if t.matrix[fl][sl] != nil:
|
|
||||||
t.matrix[fl][sl].freePtr.prev = b
|
|
||||||
t.matrix[fl][sl] = b
|
|
||||||
set_bit(sl, t.slBitmap[fl])
|
|
||||||
set_bit(fl, t.flBitmap)
|
|
||||||
|
|
||||||
proc getBuffer(b: Pbhdr): pointer {.inline.} =
|
|
||||||
result = cast[pointer](addr(b.freePtr))
|
|
||||||
|
|
||||||
proc processArea(area: pointer, size: int): Pbhdr =
|
|
||||||
var
|
|
||||||
b, lb, ib: Pbhdr
|
|
||||||
ai: PAreaInfo
|
|
||||||
ib = cast[Pbhdr](area)
|
|
||||||
if sizeof(TAreaInfo) < minBlockSize:
|
|
||||||
ib.size = minBlockSize or usedBlock or prevUsed
|
|
||||||
else
|
|
||||||
ib.size = roundupSize(sizeof(TAreaInfo)) or usedBlock or prevUsed
|
|
||||||
b = getNextBlock(getBuffer(ib), ib.size and blockSize)
|
|
||||||
b.size = rounddownSize(size - 3 * bhdrOverhead - (ib.size and blockSize)) or
|
|
||||||
usedBlock or prevUsed
|
|
||||||
b.freePtr.prev = nil
|
|
||||||
b.freePtr.next = nil
|
|
||||||
lb = getNextBlock(getBuffer(b), b.size and blockSize)
|
|
||||||
lb.prevHdr = b
|
|
||||||
lb.size = 0 or usedBlock or prevFree
|
|
||||||
ai = cast[PAreaInfo](getBuffer(ib))
|
|
||||||
ai.next = nil
|
|
||||||
ai.theEnd = lb
|
|
||||||
return ib
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
|
||||||
# Begin of the allocator code
|
|
||||||
|
|
||||||
proc initMemoryPool(memPoolSize: int, memPool: pointer): int =
|
|
||||||
var
|
|
||||||
t: PLSF
|
|
||||||
b, ib: Pbhdr
|
|
||||||
|
|
||||||
if memPool == nil or memPoolSize < sizeof(TLSF) + bhdrOverhead * 8:
|
|
||||||
writeToStdErr("initMemoryPool(): memory_pool invalid\n")
|
|
||||||
return -1
|
|
||||||
|
|
||||||
if (cast[TAddress](memPool) and ptrMask) != 0:
|
|
||||||
writeToStdErr("initMemoryPool(): memPool must be aligned to a word\n")
|
|
||||||
return -1
|
|
||||||
t = cast[PLSF](memPool)
|
|
||||||
# Check if already initialised
|
|
||||||
if t.signature == tlsfSignature:
|
|
||||||
b = getNextBlock(memPool, roundupSize(sizeof(TLSF)))
|
|
||||||
return b.size and blockSize
|
|
||||||
zeroMem(memPool, sizeof(TLSF))
|
|
||||||
|
|
||||||
t.signature = tlsfSignature
|
|
||||||
ib = processArea(getNextBlock(memPool, roundupSize(sizeof(TLSF))),
|
|
||||||
rounddownSize(memPoolSize - sizeof(TLSF)))
|
|
||||||
b = getNextBlock(getBuffer(ib), ib.size and blockSize)
|
|
||||||
freeEx(getBuffer(b), t)
|
|
||||||
t.areaHead = cast[PAreaInfo](getBuffer(ib))
|
|
||||||
|
|
||||||
t.used_size = memPoolSize - (b.size and blockSize)
|
|
||||||
t.max_size = t.used_size
|
|
||||||
return b.size and blockSize
|
|
||||||
|
|
||||||
|
|
||||||
proc addNewArea(area: pointer, areaSize: int, t: var TLSF): int =
|
|
||||||
var
|
|
||||||
p, ptrPrev, ai: PAreaInfo
|
|
||||||
ib0, b0, lb0, ib1, b1, lb1, nextB: Pbhdr
|
|
||||||
|
|
||||||
zeroMem(area, areaSize)
|
|
||||||
p = t.areaHead
|
|
||||||
ptrPrev = 0
|
|
||||||
|
|
||||||
ib0 = processArea(area, areaSize)
|
|
||||||
b0 = getNextBlock(getBuffer(ib0), ib0.size and blockSize)
|
|
||||||
lb0 = getNextBlock(getBuffer(b0), b0.size and blockSize)
|
|
||||||
|
|
||||||
# Before inserting the new area, we have to merge this area with the
|
|
||||||
# already existing ones
|
|
||||||
while p != nil:
|
|
||||||
ib1 = cast[Pbhdr](cast[TAddress](p) -% bhdrOverhead)
|
|
||||||
b1 = getNextBlock(getBuffer(ib1), ib1.size and blockSize)
|
|
||||||
lb1 = p.theEnd
|
|
||||||
|
|
||||||
# Merging the new area with the next physically contigous one
|
|
||||||
if cast[TAddress](ib1) == cast[TAddress](lb0) +% bhdrOverhead:
|
|
||||||
if t.areaHead == p:
|
|
||||||
t.areaHead = p.next
|
|
||||||
p = p.next
|
|
||||||
else:
|
|
||||||
ptrPrev.next = p.next
|
|
||||||
p = p.next
|
|
||||||
b0.size = rounddownSize((b0.size and blockSize) +
|
|
||||||
(ib1.size and blockSize) + 2 * bhdrOverhead) or
|
|
||||||
usedBlock or prevUsed
|
|
||||||
b1.prevHdr = b0
|
|
||||||
lb0 = lb1
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Merging the new area with the previous physically contigous one
|
|
||||||
if getBuffer(lb1) == pointer(ib0):
|
|
||||||
if t.areaHead == p:
|
|
||||||
t.areaHead = p.next
|
|
||||||
p = p.next
|
|
||||||
else:
|
|
||||||
ptrPrev.next = p.next
|
|
||||||
p = p.next
|
|
||||||
lb1->size = rounddownSize((b0.size and blockSize) +
|
|
||||||
(ib0.size and blockSize) + 2 * bhdrOverhead) or
|
|
||||||
usedBlock or (lb1.size and prevState)
|
|
||||||
nextB = getNextBlock(getBuffer(lb1), lb1.size and blockSize)
|
|
||||||
nextB.prevHdr = lb1
|
|
||||||
b0 = lb1
|
|
||||||
ib0 = ib1
|
|
||||||
continue
|
|
||||||
ptrPrev = p
|
|
||||||
p = p.next
|
|
||||||
|
|
||||||
# Inserting the area in the list of linked areas
|
|
||||||
ai = cast[PAreaInfo](getBuffer(ib0))
|
|
||||||
ai.next = t.areaHead
|
|
||||||
ai.theEnd = lb0
|
|
||||||
t.areaHead = ai
|
|
||||||
freeEx(getBuffer(b0), memPool)
|
|
||||||
return (b0.size and blockSize)
|
|
||||||
|
|
||||||
proc mallocEx(asize: int, t: var TLSF): pointer =
|
|
||||||
var
|
|
||||||
b, b2, nextB: Pbhdr
|
|
||||||
fl, sl, tmpSize, size: int
|
|
||||||
|
|
||||||
size = if asize < minBlockSize: minBlockSize else: roundupSize(asize)
|
|
||||||
|
|
||||||
# Rounding up the requested size and calculating fl and sl
|
|
||||||
mappingSearch(size, fl, sl)
|
|
||||||
|
|
||||||
# Searching a free block, recall that this function changes the values
|
|
||||||
# of fl and sl, so they are not longer valid when the function fails
|
|
||||||
b = findSuitableBlock(tlsf, fl, sl)
|
|
||||||
if b == nil:
|
|
||||||
# Growing the pool size when needed
|
|
||||||
# size plus enough room for the required headers:
|
|
||||||
var areaSize = max(size + bhdrOverhead * 8, defaultAreaSize)
|
|
||||||
var area = getNewArea(areaSize)
|
|
||||||
addNewArea(area, areaSize, t)
|
|
||||||
# Rounding up the requested size and calculating fl and sl
|
|
||||||
mappingSearch(size, fl, sl)
|
|
||||||
# Searching a free block
|
|
||||||
b = findSuitableBlock(t, fl, sl)
|
|
||||||
if b == nil:
|
|
||||||
raiseOutOfMem()
|
|
||||||
|
|
||||||
extractBlockHdr(b, t, fl, sl)
|
|
||||||
|
|
||||||
#-- found:
|
|
||||||
nextB = getNextBlock(getBuffer(b), b.size and blockSize)
|
|
||||||
# Should the block be split?
|
|
||||||
tmpSize = (b.size and blockSize) - size
|
|
||||||
if tmpSize >= sizeof(Tbhdr):
|
|
||||||
dec(tmpSize, bhdrOverhead)
|
|
||||||
b2 = getNextBlock(getBuffer(b), size)
|
|
||||||
b2.size = tmpSize or freeBlock or prevUsed
|
|
||||||
nextB.prevHdr = b2
|
|
||||||
mappingInsert(tmpSize, fl, sl)
|
|
||||||
insertBlock(b2, t, fl, sl)
|
|
||||||
|
|
||||||
b.size = size or (b.size and prevState)
|
|
||||||
else:
|
|
||||||
nextB.size = nextB.size and not prevFree
|
|
||||||
b.size = b.size and not freeBlock # Now it's used
|
|
||||||
|
|
||||||
addSize(t, b)
|
|
||||||
return getBuffer(b)
|
|
||||||
|
|
||||||
|
|
||||||
proc freeEx(p: pointer, t: var TLSF) =
|
|
||||||
var
|
|
||||||
fl = 0
|
|
||||||
sl = 0
|
|
||||||
b, tmpB: Pbhdr
|
|
||||||
|
|
||||||
assert(p != nil)
|
|
||||||
b = cast[Pbhdr](cast[TAddress](p) -% bhdrOverhead)
|
|
||||||
b.size = b.size or freeBlock
|
|
||||||
|
|
||||||
removeSize(t, b)
|
|
||||||
b.freePtr.prev = nil
|
|
||||||
b.freePtr.next = nil
|
|
||||||
tmpB = getNextBlock(getBuffer(b), b.size and blockSize)
|
|
||||||
if (tmpB.size and freeBlock) != 0:
|
|
||||||
mappingInsert(tmpB.size and blockSize, fl, sl)
|
|
||||||
extractBlock(tmpB, t, fl, sl)
|
|
||||||
inc(b.size, (tmpB.size and blockSize) + bhdrOverhead)
|
|
||||||
if (b.size and prevFree) != 0:
|
|
||||||
tmpB = b.prevHdr
|
|
||||||
mappingInsert(tmpB.size and blockSize, fl, sl)
|
|
||||||
extractBlock(tmpB, t, fl, sl)
|
|
||||||
inc(tmpB.size, (b.size and blockSize) + bhdrOverhead)
|
|
||||||
b = tmpB
|
|
||||||
mappingInsert(b.size and blockSize, fl, sl)
|
|
||||||
insertBlock(b, t, fl, sl)
|
|
||||||
|
|
||||||
tmpB = getNextBlock(getBuffer(b), b.size and blockSize)
|
|
||||||
tmpB.size = tmpB.size or prevFree
|
|
||||||
tmpB.prevHdr = b
|
|
||||||
|
|
||||||
proc reallocEx(p: pointer, newSize: int, t: var TLSF): pointer =
|
|
||||||
var
|
|
||||||
cpsize, fl, sl, tmpSize: int
|
|
||||||
b, tmpB, nextB: Pbhdr
|
|
||||||
|
|
||||||
assert(p != nil)
|
|
||||||
assert(newSize > 0)
|
|
||||||
|
|
||||||
b = cast[Pbhdr](cast[TAddress](p) -% bhdrOverhead)
|
|
||||||
nextB = getNextBlock(getBuffer(b), b.size and blockSize)
|
|
||||||
newSize = if newSize < minBlockSize: minBlockSize else: roundupSize(newSize)
|
|
||||||
tmpSize = b.size and blockSize
|
|
||||||
if newSize <= tmpSize:
|
|
||||||
removeSize(t, b)
|
|
||||||
if (nextB.size and freeBlock) != 0:
|
|
||||||
mappingInsert(nextB.size and blockSize, fl, sl)
|
|
||||||
extractBlock(nextB, t, fl, sl)
|
|
||||||
inc(tmpSize, (nextB.size and blockSize) + bhdrOverhead)
|
|
||||||
nextB = getNextBlock(getBuffer(nextB), nextB.size and blockSize)
|
|
||||||
# We always reenter this free block because tmpSize will
|
|
||||||
# be greater then sizeof(Tbhdr)
|
|
||||||
dec(tmpSize, newSize)
|
|
||||||
if tmpSize >= sizeof(Tbhdr):
|
|
||||||
dec(tmpSize, bhdrOverhead)
|
|
||||||
tmpB = getNextBlock(getBuffer(b), newSize)
|
|
||||||
tmpB.size = tmpSize or freeBlock or prevUsed
|
|
||||||
nextB.prevHdr = tmpB
|
|
||||||
nextB.size = nextB.size or prevFree
|
|
||||||
mappingInsert(tmpSize, fl, sl)
|
|
||||||
insertBlock(tmpB, t, fl, sl)
|
|
||||||
b.size = newSize or (b.size and prevState)
|
|
||||||
addSize(t, b)
|
|
||||||
return getBuffer(b)
|
|
||||||
|
|
||||||
if (nextB.size and freeBlock) != 0:
|
|
||||||
if newSize <= tmpSize + (nextB.size and blockSize):
|
|
||||||
removeSize(t, b)
|
|
||||||
mappingInsert(nextB.size and blockSize, fl, sl)
|
|
||||||
extractBlock(nextB, t, fl, sl)
|
|
||||||
inc(b.size, (nextB.size and blockSize) + bhdrOverhead)
|
|
||||||
nextB = getNextBlock(getBuffer(b), b.size and blockSize)
|
|
||||||
nextB.prevHdr = b
|
|
||||||
nextB.size = nextB.size and not prevFree
|
|
||||||
tmpSize = (b.size and blockSize) - newSize
|
|
||||||
if tmpSize >= sizeof(Tbhdr):
|
|
||||||
dec(tmpSize, bhdrOverhead)
|
|
||||||
tmpB = getNextBlock(getBuffer(b), newSize)
|
|
||||||
tmpB.size = tmpSize or freeBlock or prevUsed
|
|
||||||
nextB.prevHdr = tmpB
|
|
||||||
nextB.size = nextB.size or prevFree
|
|
||||||
mappingInsert(tmpSize, fl, sl)
|
|
||||||
insertBlock(tmpB, t, fl, sl)
|
|
||||||
b.size = newSize or (b.size and prevState)
|
|
||||||
addSize(t, b)
|
|
||||||
return getBuffer(b)
|
|
||||||
|
|
||||||
var ptrAux = mallocEx(newSize, t)
|
|
||||||
cpsize = if (b.size and blockSize) > newSize: newSize else:
|
|
||||||
(b.size and blockSize)
|
|
||||||
copyMem(ptrAux, p, cpsize)
|
|
||||||
freeEx(p, memPool)
|
|
||||||
return ptrAux
|
|
||||||
|
|
||||||
|
|
||||||
proc ansiCrealloc(p: pointer, newSize: int, t: var TLSF): pointer =
|
|
||||||
if p == nil:
|
|
||||||
if newSize > 0:
|
|
||||||
result = mallocEx(newSize, t)
|
|
||||||
else:
|
|
||||||
result = nil
|
|
||||||
elif newSize <= 0:
|
|
||||||
freeEx(p, t)
|
|
||||||
result = nil
|
|
||||||
else:
|
|
||||||
result = reallocEx(p, newSize, t)
|
|
||||||
|
|
||||||
proc InitTLSF(t: var TLSF) =
|
|
||||||
var areaSize = sizeof(TLSF) + BHDR_OVERHEAD * 8 # Just a safety constant
|
|
||||||
areaSize = max(areaSize, DEFAULT_areaSize)
|
|
||||||
var area = getNewArea(areaSize)
|
|
||||||
|
|
||||||
|
|
||||||
initMemoryPool(areaSize, area)
|
|
||||||
|
|
||||||
var
|
|
||||||
t: PLSF
|
|
||||||
b, ib: Pbhdr
|
|
||||||
|
|
||||||
t = cast[PLSF](memPool)
|
|
||||||
|
|
||||||
zeroMem(area, areaSize)
|
|
||||||
|
|
||||||
t.signature = tlsfSignature
|
|
||||||
var ib = processArea(getNextBlock(memPool, roundupSize(sizeof(TLSF))),
|
|
||||||
rounddownSize(memPoolSize - sizeof(TLSF)))
|
|
||||||
var b = getNextBlock(getBuffer(ib), ib.size and blockSize)
|
|
||||||
freeEx(getBuffer(b), t)
|
|
||||||
t.areaHead = cast[PAreaInfo](getBuffer(ib))
|
|
||||||
|
|
||||||
t.used_size = memPoolSize - (b.size and blockSize)
|
|
||||||
t.max_size = t.used_size
|
|
||||||
|
|
||||||
# XXX
|
|
||||||
187
lib/mm.nim
Normal file
187
lib/mm.nim
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nimrod's Runtime Library
|
||||||
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Nimrod high-level memory manager: It supports Boehm's GC, no GC and the
|
||||||
|
# native Nimrod GC. The native Nimrod GC is the default.
|
||||||
|
|
||||||
|
#{.push checks:on, assertions:on.}
|
||||||
|
{.push checks:off.}
|
||||||
|
|
||||||
|
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 = false
|
||||||
|
reallyOsDealloc = true
|
||||||
|
coalescRight = true
|
||||||
|
coalescLeft = true
|
||||||
|
|
||||||
|
type
|
||||||
|
PPointer = ptr pointer
|
||||||
|
TByteArray = array[0..1000_0000, byte]
|
||||||
|
PByte = ptr TByteArray
|
||||||
|
PString = ptr string
|
||||||
|
|
||||||
|
# Page size of the system; in most cases 4096 bytes. For exotic OS or
|
||||||
|
# CPU this needs to be changed:
|
||||||
|
const
|
||||||
|
PageShift = 12
|
||||||
|
PageSize = 1 shl PageShift
|
||||||
|
PageMask = PageSize-1
|
||||||
|
|
||||||
|
MemAlign = 8 # also minimal allocatable memory block
|
||||||
|
|
||||||
|
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 (!)
|
||||||
|
|
||||||
|
TrunkShift = 9
|
||||||
|
BitsPerTrunk = 1 shl TrunkShift # needs to be 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
|
||||||
|
|
||||||
|
var
|
||||||
|
gOutOfMem: ref EOutOfMemory
|
||||||
|
|
||||||
|
proc raiseOutOfMem() {.noreturn.} =
|
||||||
|
if gOutOfMem == nil: quit("out of memory; cannot even throw an exception")
|
||||||
|
gOutOfMem.msg = "out of memory"
|
||||||
|
raise gOutOfMem
|
||||||
|
|
||||||
|
when defined(boehmgc):
|
||||||
|
when defined(windows):
|
||||||
|
const boehmLib = "boehmgc.dll"
|
||||||
|
else:
|
||||||
|
const boehmLib = "/usr/lib/libgc.so.1"
|
||||||
|
|
||||||
|
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.}
|
||||||
|
|
||||||
|
proc alloc(size: int): pointer =
|
||||||
|
result = boehmAlloc(size)
|
||||||
|
if result == nil: raiseOutOfMem()
|
||||||
|
proc alloc0(size: int): pointer =
|
||||||
|
result = alloc(size)
|
||||||
|
zeroMem(result, size)
|
||||||
|
proc realloc(p: Pointer, newsize: int): pointer =
|
||||||
|
result = boehmRealloc(p, newsize)
|
||||||
|
if result == nil: raiseOutOfMem()
|
||||||
|
proc dealloc(p: Pointer) =
|
||||||
|
boehmDealloc(p)
|
||||||
|
|
||||||
|
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 newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
|
||||||
|
result = alloc(size)
|
||||||
|
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
|
||||||
|
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
|
||||||
|
cast[PGenericSeq](result).len = len
|
||||||
|
cast[PGenericSeq](result).space = len
|
||||||
|
|
||||||
|
proc growObj(old: pointer, newsize: int): pointer =
|
||||||
|
result = realloc(old, newsize)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
include cellsets
|
||||||
|
elif defined(nogc):
|
||||||
|
proc alloc(size: int): pointer =
|
||||||
|
result = c_malloc(size)
|
||||||
|
if result == nil: raiseOutOfMem()
|
||||||
|
proc alloc0(size: int): pointer =
|
||||||
|
result = alloc(size)
|
||||||
|
zeroMem(result, size)
|
||||||
|
proc realloc(p: Pointer, newsize: int): pointer =
|
||||||
|
result = c_realloc(p, newsize)
|
||||||
|
if result == nil: raiseOutOfMem()
|
||||||
|
proc dealloc(p: Pointer) =
|
||||||
|
c_free(p)
|
||||||
|
|
||||||
|
proc initGC() = nil
|
||||||
|
proc GC_disable() = nil
|
||||||
|
proc GC_enable() = nil
|
||||||
|
proc GC_fullCollect() = nil
|
||||||
|
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 newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
|
||||||
|
result = alloc0(size)
|
||||||
|
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
|
||||||
|
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
|
||||||
|
cast[PGenericSeq](result).len = len
|
||||||
|
cast[PGenericSeq](result).space = len
|
||||||
|
proc growObj(old: pointer, newsize: int): pointer =
|
||||||
|
result = realloc(old, newsize)
|
||||||
|
# XXX BUG: we need realloc0 here, but C does not support this...
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
include cellsets
|
||||||
|
else:
|
||||||
|
include alloc
|
||||||
|
include cellsets
|
||||||
|
assert(sizeof(TCell) == sizeof(TFreeCell))
|
||||||
|
include gc
|
||||||
|
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -546,7 +546,7 @@ proc getApplicationFilename(): string =
|
||||||
# Solaris:
|
# Solaris:
|
||||||
# /proc/<pid>/object/a.out (filename only)
|
# /proc/<pid>/object/a.out (filename only)
|
||||||
# /proc/<pid>/path/a.out (complete pathname)
|
# /proc/<pid>/path/a.out (complete pathname)
|
||||||
# *BSD (and maybe Darwing too):
|
# *BSD (and maybe Darwin too):
|
||||||
# /proc/<pid>/file
|
# /proc/<pid>/file
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
result = newString(256)
|
result = newString(256)
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,21 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2008 Andreas Rumpf
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module implements an advanced facility for executing OS processes.
|
## This module implements an advanced facility for executing OS processes
|
||||||
## On Windows this module is currently broken. Please help!
|
## and process communication.
|
||||||
|
## **On Windows this module does not work properly. Please help!**
|
||||||
|
|
||||||
import
|
import
|
||||||
os, strtabs, streams
|
os, strtabs, streams
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
import windows
|
import winlean
|
||||||
|
|
||||||
type
|
type
|
||||||
TProcess = object of TObject
|
TProcess = object of TObject
|
||||||
|
|
@ -41,6 +42,10 @@ proc executeProcess*(command: string,
|
||||||
## A convience procedure that executes ``command`` with ``startProcess``
|
## A convience procedure that executes ``command`` with ``startProcess``
|
||||||
## and returns its output as a string.
|
## and returns its output as a string.
|
||||||
|
|
||||||
|
proc executeCommand*(command: string): int
|
||||||
|
## Executes ``command`` and returns its error code. Standard input, output,
|
||||||
|
## error streams are inherited from the calling process.
|
||||||
|
|
||||||
proc startProcess*(command: string,
|
proc startProcess*(command: string,
|
||||||
workingDir: string = "",
|
workingDir: string = "",
|
||||||
args: openarray[string] = [],
|
args: openarray[string] = [],
|
||||||
|
|
@ -133,14 +138,14 @@ when defined(Windows):
|
||||||
|
|
||||||
proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int =
|
proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int =
|
||||||
var br: int32
|
var br: int32
|
||||||
var a = windows.ReadFile(s.handle, buffer, bufLen, br, nil)
|
var a = winlean.ReadFile(s.handle, buffer, bufLen, br, nil)
|
||||||
if a == 0: OSError()
|
if a == 0: OSError()
|
||||||
result = br
|
result = br
|
||||||
#atEnd = bytesRead < bufLen
|
#atEnd = bytesRead < bufLen
|
||||||
|
|
||||||
proc hsWriteData(s: PFileHandleStream, buffer: pointer, bufLen: int) =
|
proc hsWriteData(s: PFileHandleStream, buffer: pointer, bufLen: int) =
|
||||||
var bytesWritten: int32
|
var bytesWritten: int32
|
||||||
var a = windows.writeFile(s.handle, buffer, bufLen, bytesWritten, nil)
|
var a = winlean.writeFile(s.handle, buffer, bufLen, bytesWritten, nil)
|
||||||
if a == 0: OSError()
|
if a == 0: OSError()
|
||||||
|
|
||||||
proc newFileHandleStream(handle: THandle): PFileHandleStream =
|
proc newFileHandleStream(handle: THandle): PFileHandleStream =
|
||||||
|
|
@ -180,12 +185,12 @@ when defined(Windows):
|
||||||
# O_WRONLY {.importc: "_O_WRONLY", header: "<fcntl.h>".}: int
|
# O_WRONLY {.importc: "_O_WRONLY", header: "<fcntl.h>".}: int
|
||||||
# O_RDONLY {.importc: "_O_RDONLY", header: "<fcntl.h>".}: int
|
# O_RDONLY {.importc: "_O_RDONLY", header: "<fcntl.h>".}: int
|
||||||
|
|
||||||
proc CreatePipeHandles(Inhandle, OutHandle: ptr THandle) =
|
proc CreatePipeHandles(Inhandle, OutHandle: var THandle) =
|
||||||
var piInheritablePipe: TSecurityAttributes
|
var piInheritablePipe: TSecurityAttributes
|
||||||
piInheritablePipe.nlength = SizeOF(TSecurityAttributes)
|
piInheritablePipe.nlength = SizeOF(TSecurityAttributes)
|
||||||
piInheritablePipe.lpSecurityDescriptor = Nil
|
piInheritablePipe.lpSecurityDescriptor = nil
|
||||||
piInheritablePipe.Binherithandle = 1
|
piInheritablePipe.Binherithandle = 1
|
||||||
if CreatePipe(Inhandle, Outhandle, addr(piInheritablePipe), 0) == 0'i32:
|
if CreatePipe(Inhandle, Outhandle, piInheritablePipe, 0) == 0'i32:
|
||||||
OSError()
|
OSError()
|
||||||
|
|
||||||
proc startProcess*(command: string,
|
proc startProcess*(command: string,
|
||||||
|
|
@ -201,15 +206,15 @@ when defined(Windows):
|
||||||
hi, ho, he: THandle
|
hi, ho, he: THandle
|
||||||
SI.cb = SizeOf(SI)
|
SI.cb = SizeOf(SI)
|
||||||
SI.dwFlags = STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES
|
SI.dwFlags = STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES
|
||||||
CreatePipeHandles(addr(SI.hStdInput), addr(HI))
|
CreatePipeHandles(SI.hStdInput, HI)
|
||||||
CreatePipeHandles(addr(HO), addr(Si.hStdOutput))
|
CreatePipeHandles(HO, Si.hStdOutput)
|
||||||
#SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE())
|
#SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE())
|
||||||
#SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE())
|
#SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE())
|
||||||
if poStdErrToStdOut in options:
|
if poStdErrToStdOut in options:
|
||||||
SI.hStdError = SI.hStdOutput
|
SI.hStdError = SI.hStdOutput
|
||||||
HE = HO
|
HE = HO
|
||||||
else:
|
else:
|
||||||
CreatePipeHandles(addr(HE), addr(Si.hStdError))
|
CreatePipeHandles(HE, Si.hStdError)
|
||||||
#SI.hStdError = GetStdHandle(STD_ERROR_HANDLE())
|
#SI.hStdError = GetStdHandle(STD_ERROR_HANDLE())
|
||||||
#result.inputHandle = open_osfhandle(HI, O_WRONLY)
|
#result.inputHandle = open_osfhandle(HI, O_WRONLY)
|
||||||
#if result.inputHandle == -1'i32: OSError()
|
#if result.inputHandle == -1'i32: OSError()
|
||||||
|
|
@ -224,16 +229,12 @@ when defined(Windows):
|
||||||
var wd: cstring = nil
|
var wd: cstring = nil
|
||||||
if len(workingDir) > 0: wd = workingDir
|
if len(workingDir) > 0: wd = workingDir
|
||||||
if env == nil:
|
if env == nil:
|
||||||
success = Windows.CreateProcess(nil,
|
success = winlean.CreateProcess(nil,
|
||||||
cmdl, nil, nil, 0,
|
cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, nil, wd, SI, ProcInfo)
|
||||||
NORMAL_PRIORITY_CLASS, nil, wd,
|
|
||||||
addr(SI), addr(ProcInfo))
|
|
||||||
else:
|
else:
|
||||||
var e = buildEnv(env)
|
var e = buildEnv(env)
|
||||||
success = Windows.CreateProcess(nil,
|
success = winlean.CreateProcess(nil,
|
||||||
cmdl, nil, nil, 0,
|
cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
|
||||||
NORMAL_PRIORITY_CLASS, e, wd,
|
|
||||||
addr(SI), addr(ProcInfo))
|
|
||||||
dealloc(e)
|
dealloc(e)
|
||||||
dealloc(cmdl)
|
dealloc(cmdl)
|
||||||
if success == 0:
|
if success == 0:
|
||||||
|
|
@ -249,7 +250,7 @@ when defined(Windows):
|
||||||
discard ResumeThread(p.FThreadHandle)
|
discard ResumeThread(p.FThreadHandle)
|
||||||
|
|
||||||
proc running(p: PProcess): bool =
|
proc running(p: PProcess): bool =
|
||||||
var x = waitForSingleObject(p.FThreadHandle, 50)
|
var x = waitForSingleObject(p.FProcessHandle, 50)
|
||||||
return x == WAIT_TIMEOUT
|
return x == WAIT_TIMEOUT
|
||||||
|
|
||||||
proc terminate(p: PProcess) =
|
proc terminate(p: PProcess) =
|
||||||
|
|
@ -257,11 +258,11 @@ when defined(Windows):
|
||||||
discard TerminateProcess(p.FProcessHandle, 0)
|
discard TerminateProcess(p.FProcessHandle, 0)
|
||||||
|
|
||||||
proc waitForExit(p: PProcess): int =
|
proc waitForExit(p: PProcess): int =
|
||||||
discard WaitForSingleObject(p.FThreadHandle, Infinite)
|
discard CloseHandle(p.FThreadHandle)
|
||||||
var res: dword
|
discard WaitForSingleObject(p.FProcessHandle, Infinite)
|
||||||
|
var res: int32
|
||||||
discard GetExitCodeProcess(p.FProcessHandle, res)
|
discard GetExitCodeProcess(p.FProcessHandle, res)
|
||||||
result = res
|
result = res
|
||||||
discard CloseHandle(p.FThreadHandle)
|
|
||||||
discard CloseHandle(p.FProcessHandle)
|
discard CloseHandle(p.FProcessHandle)
|
||||||
|
|
||||||
proc inputStream(p: PProcess): PStream =
|
proc inputStream(p: PProcess): PStream =
|
||||||
|
|
@ -273,6 +274,29 @@ when defined(Windows):
|
||||||
proc errorStream(p: PProcess): PStream =
|
proc errorStream(p: PProcess): PStream =
|
||||||
result = newFileHandleStream(p.errorHandle)
|
result = newFileHandleStream(p.errorHandle)
|
||||||
|
|
||||||
|
proc executeCommand(command: string): int =
|
||||||
|
var
|
||||||
|
SI: TStartupInfo
|
||||||
|
ProcInfo: TProcessInformation
|
||||||
|
process: THandle
|
||||||
|
L: int32
|
||||||
|
SI.cb = SizeOf(SI)
|
||||||
|
SI.hStdError = GetStdHandle(STD_ERROR_HANDLE)
|
||||||
|
SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE)
|
||||||
|
SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||||
|
if winlean.CreateProcess(nil, command, nil, nil, 0,
|
||||||
|
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo) == 0:
|
||||||
|
OSError()
|
||||||
|
else:
|
||||||
|
Process = ProcInfo.hProcess
|
||||||
|
discard CloseHandle(ProcInfo.hThread)
|
||||||
|
if WaitForSingleObject(Process, INFINITE) != -1:
|
||||||
|
discard GetExitCodeProcess(Process, L)
|
||||||
|
result = int(L)
|
||||||
|
else:
|
||||||
|
result = -1
|
||||||
|
discard CloseHandle(Process)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
import posix
|
import posix
|
||||||
|
|
||||||
|
|
@ -321,15 +345,15 @@ else:
|
||||||
if pid == 0:
|
if pid == 0:
|
||||||
## child process:
|
## child process:
|
||||||
discard close(p_stdin[writeIdx])
|
discard close(p_stdin[writeIdx])
|
||||||
discard dup2(p_stdin[readIdx], readIdx)
|
if dup2(p_stdin[readIdx], readIdx) < 0: OSError()
|
||||||
discard close(p_stdout[readIdx])
|
discard close(p_stdout[readIdx])
|
||||||
discard dup2(p_stdout[writeIdx], writeIdx)
|
if dup2(p_stdout[writeIdx], writeIdx) < 0: OSError()
|
||||||
if poStdErrToStdOut in options:
|
if poStdErrToStdOut in options:
|
||||||
discard dup2(p_stdout[writeIdx], 2)
|
if dup2(p_stdout[writeIdx], 2) < 0: OSError()
|
||||||
else:
|
else:
|
||||||
if pipe(p_stderr) != 0'i32: OSError("failed to create a pipe")
|
if pipe(p_stderr) != 0'i32: OSError("failed to create a pipe")
|
||||||
discard close(p_stderr[readIdx])
|
discard close(p_stderr[readIdx])
|
||||||
discard dup2(p_stderr[writeIdx], 2)
|
if dup2(p_stderr[writeIdx], 2) < 0: OSError()
|
||||||
|
|
||||||
if workingDir.len > 0:
|
if workingDir.len > 0:
|
||||||
os.setCurrentDir(workingDir)
|
os.setCurrentDir(workingDir)
|
||||||
|
|
@ -347,8 +371,7 @@ else:
|
||||||
else:
|
else:
|
||||||
discard execve("/bin/sh", a, ToCStringArray(env))
|
discard execve("/bin/sh", a, ToCStringArray(env))
|
||||||
# too risky to raise an exception here:
|
# too risky to raise an exception here:
|
||||||
echo("execve call failed: " & $strerror(errno))
|
quit("execve call failed: " & $strerror(errno))
|
||||||
quit(1)
|
|
||||||
# Parent process. Copy process information.
|
# Parent process. Copy process information.
|
||||||
result.id = pid
|
result.id = pid
|
||||||
|
|
||||||
|
|
@ -376,6 +399,7 @@ else:
|
||||||
if running(p): discard kill(p.id, SIGKILL)
|
if running(p): discard kill(p.id, SIGKILL)
|
||||||
|
|
||||||
proc waitForExit(p: PProcess): int =
|
proc waitForExit(p: PProcess): int =
|
||||||
|
result = 1
|
||||||
if waitPid(p.id, p.exitCode, 0) == int(p.id):
|
if waitPid(p.id, p.exitCode, 0) == int(p.id):
|
||||||
result = p.exitCode
|
result = p.exitCode
|
||||||
|
|
||||||
|
|
@ -393,3 +417,11 @@ else:
|
||||||
var f: TFile
|
var f: TFile
|
||||||
if not openFile(f, p.errorHandle, fmRead): OSError()
|
if not openFile(f, p.errorHandle, fmRead): OSError()
|
||||||
result = newFileStream(f)
|
result = newFileStream(f)
|
||||||
|
|
||||||
|
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
|
||||||
|
|
||||||
|
proc executeCommand(command: string): int =
|
||||||
|
result = csystem(command)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
echo executeCommand("gcc -v")
|
||||||
|
|
|
||||||
176
lib/parsecsv.nim
Normal file
176
lib/parsecsv.nim
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nimrod's Runtime Library
|
||||||
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module implements a simple high performance `CSV`:idx:
|
||||||
|
## (`comma separated value`:idx:) parser.
|
||||||
|
##
|
||||||
|
## Example: How to use the parser
|
||||||
|
## ==============================
|
||||||
|
##
|
||||||
|
## .. code-block:: nimrod
|
||||||
|
## import os, parsecsv, streams
|
||||||
|
## var s = newFileStream(ParamStr(1), fmRead)
|
||||||
|
## if s == nil: quit("cannot open the file" & ParamStr(1))
|
||||||
|
## var x: TCsvParser
|
||||||
|
## open(x, s, ParamStr(1))
|
||||||
|
## while readRow(x):
|
||||||
|
## Echo "new row: "
|
||||||
|
## for val in items(x.row):
|
||||||
|
## Echo "##", val, "##"
|
||||||
|
## close(x)
|
||||||
|
##
|
||||||
|
|
||||||
|
import
|
||||||
|
lexbase, streams
|
||||||
|
|
||||||
|
type
|
||||||
|
TCsvRow* = seq[string] ## a row in a CSV file
|
||||||
|
TCsvParser* = object of TBaseLexer ## the parser object.
|
||||||
|
row*: TCsvRow ## the current row
|
||||||
|
filename: string
|
||||||
|
sep, quote, esc: char
|
||||||
|
skipWhite: bool
|
||||||
|
currRow: int
|
||||||
|
|
||||||
|
EInvalidCsv* = object of EIO ## exception that is raised if
|
||||||
|
## a parsing error occurs
|
||||||
|
|
||||||
|
proc raiseEInvalidCsv(filename: string, line, col: int,
|
||||||
|
msg: string) {.noreturn.} =
|
||||||
|
var e: ref EInvalidCsv
|
||||||
|
new(e)
|
||||||
|
e.msg = filename & "(" & $line & ", " & $col & ") Error: " & msg
|
||||||
|
raise e
|
||||||
|
|
||||||
|
proc error(my: TCsvParser, pos: int, msg: string) =
|
||||||
|
raiseEInvalidCsv(my.filename, my.LineNumber, getColNumber(my, pos), msg)
|
||||||
|
|
||||||
|
proc open*(my: var TCsvParser, input: PStream, filename: string,
|
||||||
|
separator = ',', quote = '"', escape = '\0',
|
||||||
|
skipInitialSpace = false) =
|
||||||
|
## initializes the parser with an input stream. `Filename` is only used
|
||||||
|
## for nice error messages. The parser's behaviour can be controlled by
|
||||||
|
## the diverse optional parameters:
|
||||||
|
## - `separator`: character used to separate fields
|
||||||
|
## - `quote`: Used to quote fields containing special characters like
|
||||||
|
## `separator`, `quote` or new-line characters. '\0' disables the parsing
|
||||||
|
## of quotes.
|
||||||
|
## - `escape`: removes any special meaning from the following character;
|
||||||
|
## '\0' disables escaping; if escaping is disabled and `quote` is not '\0',
|
||||||
|
## two `quote` characters are parsed one literal `quote` character.
|
||||||
|
## - `skipInitialSpace`: If true, whitespace immediately following the
|
||||||
|
## `separator` is ignored.
|
||||||
|
lexbase.open(my, input)
|
||||||
|
my.filename = filename
|
||||||
|
my.sep = separator
|
||||||
|
my.quote = quote
|
||||||
|
my.esc = escape
|
||||||
|
my.skipWhite = skipInitialSpace
|
||||||
|
my.row = @[]
|
||||||
|
my.currRow = 0
|
||||||
|
|
||||||
|
proc parseField(my: var TCsvParser, a: var string) =
|
||||||
|
var pos = my.bufpos
|
||||||
|
var buf = my.buf
|
||||||
|
if my.skipWhite:
|
||||||
|
while buf[pos] in {' ', '\t'}: inc(pos)
|
||||||
|
setLen(a, 0) # reuse memory
|
||||||
|
if buf[pos] == my.quote and my.quote != '\0':
|
||||||
|
inc(pos)
|
||||||
|
while true:
|
||||||
|
var c = buf[pos]
|
||||||
|
if c == '\0':
|
||||||
|
my.bufpos = pos # can continue after exception?
|
||||||
|
error(my, pos, my.quote & " expected")
|
||||||
|
break
|
||||||
|
elif c == my.quote:
|
||||||
|
if my.esc == '\0' and buf[pos+1] == my.quote:
|
||||||
|
add(a, my.quote)
|
||||||
|
inc(pos, 2)
|
||||||
|
else:
|
||||||
|
inc(pos)
|
||||||
|
break
|
||||||
|
elif c == my.esc:
|
||||||
|
add(a, buf[pos+1])
|
||||||
|
inc(pos, 2)
|
||||||
|
else:
|
||||||
|
case c
|
||||||
|
of '\c':
|
||||||
|
pos = handleCR(my, pos)
|
||||||
|
add(a, "\n")
|
||||||
|
of '\l':
|
||||||
|
pos = handleLF(my, pos)
|
||||||
|
add(a, "\n")
|
||||||
|
else:
|
||||||
|
add(a, c)
|
||||||
|
inc(pos)
|
||||||
|
else:
|
||||||
|
while true:
|
||||||
|
var c = buf[pos]
|
||||||
|
if c == my.sep: break
|
||||||
|
if c in {'\c', '\l', '\0'}: break
|
||||||
|
add(a, c)
|
||||||
|
inc(pos)
|
||||||
|
my.bufpos = pos
|
||||||
|
|
||||||
|
proc processedRows*(my: var TCsvParser): int =
|
||||||
|
## returns number of the processed rows
|
||||||
|
return my.currRow
|
||||||
|
|
||||||
|
proc readRow*(my: var TCsvParser, columns = 0): bool =
|
||||||
|
## reads the next row; if `columns` > 0, it expects the row to have
|
||||||
|
## exactly this many columns. Returns false if the end of the file
|
||||||
|
## has been encountered else true.
|
||||||
|
var col = 0 # current column
|
||||||
|
var oldpos = my.bufpos
|
||||||
|
while my.buf[my.bufpos] != '\0':
|
||||||
|
var oldlen = my.row.len
|
||||||
|
if oldlen < col+1:
|
||||||
|
setLen(my.row, col+1)
|
||||||
|
my.row[col] = ""
|
||||||
|
parseField(my, my.row[col])
|
||||||
|
inc(col)
|
||||||
|
if my.buf[my.bufpos] == my.sep:
|
||||||
|
inc(my.bufpos)
|
||||||
|
else:
|
||||||
|
case my.buf[my.bufpos]
|
||||||
|
of '\c', '\l':
|
||||||
|
# skip empty lines:
|
||||||
|
while true:
|
||||||
|
case my.buf[my.bufpos]
|
||||||
|
of '\c': my.bufpos = handleCR(my, my.bufpos)
|
||||||
|
of '\l': my.bufpos = handleLF(my, my.bufpos)
|
||||||
|
else: break
|
||||||
|
of '\0': nil
|
||||||
|
else: error(my, my.bufpos, my.sep & " expected")
|
||||||
|
break
|
||||||
|
|
||||||
|
setlen(my.row, col)
|
||||||
|
result = col > 0
|
||||||
|
if result and col != columns and columns > 0:
|
||||||
|
error(my, oldpos+1, $columns & " columns expected, but found " &
|
||||||
|
$col & " columns")
|
||||||
|
inc(my.currRow)
|
||||||
|
|
||||||
|
proc close*(my: var TCsvParser) {.inline.} =
|
||||||
|
## closes the parser `my` and its associated input stream.
|
||||||
|
lexbase.close(my)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
import os
|
||||||
|
var s = newFileStream(ParamStr(1), fmRead)
|
||||||
|
if s == nil: quit("cannot open the file" & ParamStr(1))
|
||||||
|
var x: TCsvParser
|
||||||
|
open(x, s, ParamStr(1))
|
||||||
|
while readRow(x):
|
||||||
|
Echo "new row: "
|
||||||
|
for val in items(x.row):
|
||||||
|
Echo "##", val, "##"
|
||||||
|
close(x)
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
## * Thus the checks would have been very difficult to implement properly with
|
## * Thus the checks would have been very difficult to implement properly with
|
||||||
## little benefit, especially since they are simple to implement in the
|
## little benefit, especially since they are simple to implement in the
|
||||||
## client. The client should use the `errorMsgExpected` proc to generate
|
## client. The client should use the `errorMsgExpected` proc to generate
|
||||||
## a nice error message that fits to the other error messages this library
|
## a nice error message that fits the other error messages this library
|
||||||
## creates.
|
## creates.
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
|
|
|
||||||
205
lib/ptrset.nim
205
lib/ptrset.nim
|
|
@ -1,205 +0,0 @@
|
||||||
# This implements a new pointer set. Access time O(1). For 32 bit systems, we
|
|
||||||
# currently need 3 memory accesses.
|
|
||||||
|
|
||||||
const
|
|
||||||
PageSize = 1024 * sizeof(int)
|
|
||||||
MemAlignment = 8 # minimal memory block that can be allocated
|
|
||||||
BitsPerUnit = sizeof(int)*8
|
|
||||||
# a "unit" is a word, i.e. 4 bytes
|
|
||||||
# on a 32 bit system; I do not use the term "word" because under 32-bit
|
|
||||||
# Windows it is sometimes only 16 bits
|
|
||||||
|
|
||||||
BitsPerPage = PageSize div MemAlignment
|
|
||||||
UnitsPerPage = BitsPerPage div BitsPerUnit
|
|
||||||
# how many units do we need to describe a page:
|
|
||||||
# on 32 bit systems this is only 16 (!)
|
|
||||||
|
|
||||||
type
|
|
||||||
PPointer = ptr pointer
|
|
||||||
|
|
||||||
TCollectorData = int
|
|
||||||
TCell {.final.} = object
|
|
||||||
refcount: TCollectorData # the refcount and bit flags
|
|
||||||
typ: PNimType
|
|
||||||
stackcount: int # stack counter for debugging
|
|
||||||
drefc: int # real reference counter for debugging
|
|
||||||
|
|
||||||
PCell = ptr TCell
|
|
||||||
|
|
||||||
proc cellToUsr(cell: PCell): pointer {.inline.} =
|
|
||||||
# convert object (=pointer to refcount) to pointer to userdata
|
|
||||||
result = cast[pointer](cast[TAddress](cell)+%TAddress(sizeof(TCell)))
|
|
||||||
|
|
||||||
proc usrToCell(usr: pointer): PCell {.inline.} =
|
|
||||||
# convert pointer to userdata to object (=pointer to refcount)
|
|
||||||
result = cast[PCell](cast[TAddress](usr)-%TAddress(sizeof(TCell)))
|
|
||||||
|
|
||||||
proc gcAlloc(size: int): pointer =
|
|
||||||
result = alloc0(size)
|
|
||||||
if result == nil: raiseOutOfMem()
|
|
||||||
|
|
||||||
# ------------------ Zero count table (ZCT) and any table (AT) -------------
|
|
||||||
|
|
||||||
# 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 ------------------------------
|
|
||||||
# A cellset consists of a hash table of page descriptors. A page
|
|
||||||
# descriptor has a bit for
|
|
||||||
# every Memalignment'th byte in the page.
|
|
||||||
# However, only bits corresponding to addresses that start memory blocks
|
|
||||||
# are set.
|
|
||||||
# Page descriptors are also linked to a list; the list
|
|
||||||
# is used for easy traversing of all page descriptors; this allows a
|
|
||||||
# fast iterator.
|
|
||||||
# We use a specialized hashing scheme; the formula is :
|
|
||||||
# hash = Page bitand max
|
|
||||||
# We use linear probing with the formular: (5*h)+1
|
|
||||||
# Thus we likely get no collisions at all if the pages are given to us
|
|
||||||
# in a sequential manner by the operating system!
|
|
||||||
const
|
|
||||||
bitsPerNode = 10 # we use 10 bits per node; this means 3 memory accesses on
|
|
||||||
# 32 bit systems
|
|
||||||
|
|
||||||
type
|
|
||||||
PPageDesc = ptr TPageDesc
|
|
||||||
|
|
||||||
TBitIndex = range[0..UnitsPerPage-1]
|
|
||||||
|
|
||||||
TPageDesc {.final.} = 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.} = object
|
|
||||||
counter, max: int
|
|
||||||
head: PPageDesc
|
|
||||||
data: PPageDescArray
|
|
||||||
|
|
||||||
TSetNode {.final.} = object
|
|
||||||
n: array[0.. (1 shl bitsPerNode)-1, PSetNode]
|
|
||||||
PSetNode = ptr TSetNode
|
|
||||||
|
|
||||||
const
|
|
||||||
InitCellSetSize = 1024 # must be a power of two!
|
|
||||||
|
|
||||||
proc CellSetInit(s: var TCellSet) =
|
|
||||||
s.data = 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
|
|
||||||
dealloc(it)
|
|
||||||
it = n
|
|
||||||
s.head = nil # play it safe here
|
|
||||||
dealloc(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
|
|
||||||
n: PPageDescArray
|
|
||||||
oldMax = t.max
|
|
||||||
t.max = ((t.max+1)*2)-1
|
|
||||||
n = gcAlloc((t.max + 1) * sizeof(PPageDesc))
|
|
||||||
for i in 0 .. oldmax:
|
|
||||||
if t.data[i] != nil:
|
|
||||||
CellSetRawInsert(t, n, t.data[i])
|
|
||||||
dealloc(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: 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 = gcAlloc(sizeof(TPageDesc))
|
|
||||||
result.next = t.head
|
|
||||||
result.key = key
|
|
||||||
t.head = result
|
|
||||||
t.data[h] = result
|
|
||||||
|
|
||||||
# ---------- slightly higher level procs ----------------------------------
|
|
||||||
|
|
||||||
proc in_Operator(s: TCellSet, cell: PCell): bool =
|
|
||||||
var
|
|
||||||
u: TAddress
|
|
||||||
t: PPageDesc
|
|
||||||
u = cast[TAddress](cell)
|
|
||||||
t = CellSetGet(s, u /% PageSize)
|
|
||||||
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: TAddress
|
|
||||||
t: PPageDesc
|
|
||||||
u = cast[TAddress](cell)
|
|
||||||
t = CellSetPut(s, u /% PageSize)
|
|
||||||
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: TAddress
|
|
||||||
t: PPageDesc
|
|
||||||
u = cast[TAddress](cell)
|
|
||||||
t = CellSetGet(s, u /% PageSize)
|
|
||||||
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 *% PageSize) +%
|
|
||||||
(i*%BitsPerUnit+%j) *% MemAlignment)
|
|
||||||
inc(j)
|
|
||||||
w = w shr 1
|
|
||||||
inc(i)
|
|
||||||
r = r.next
|
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ proc reprSet(p: pointer, typ: PNimType): string {.compilerproc.} =
|
||||||
type
|
type
|
||||||
TReprClosure {.final.} = object # we cannot use a global variable here
|
TReprClosure {.final.} = object # we cannot use a global variable here
|
||||||
# as this wouldn't be thread-safe
|
# as this wouldn't be thread-safe
|
||||||
marked: TCellSeq
|
marked: TCellSet
|
||||||
recdepth: int # do not recurse endless
|
recdepth: int # do not recurse endless
|
||||||
indent: int # indentation
|
indent: int # indentation
|
||||||
|
|
||||||
|
|
@ -171,14 +171,14 @@ proc reprRecord(result: var string, p: pointer, typ: PNimType,
|
||||||
proc reprRef(result: var string, p: pointer, typ: PNimType,
|
proc reprRef(result: var string, p: pointer, typ: PNimType,
|
||||||
cl: var TReprClosure) =
|
cl: var TReprClosure) =
|
||||||
# we know that p is not nil here:
|
# we know that p is not nil here:
|
||||||
when defined(boehmGC):
|
when defined(boehmGC) or defined(nogc):
|
||||||
var cell = cast[PCell](p)
|
var cell = cast[PCell](p)
|
||||||
else:
|
else:
|
||||||
var cell = usrToCell(p)
|
var cell = usrToCell(p)
|
||||||
add result, "ref " & reprPointer(p)
|
add result, "ref " & reprPointer(p)
|
||||||
if cell notin cl.marked:
|
if cell notin cl.marked:
|
||||||
# only the address is shown:
|
# only the address is shown:
|
||||||
add(cl.marked, cell)
|
incl(cl.marked, cell)
|
||||||
add result, " --> "
|
add result, " --> "
|
||||||
reprAux(result, p, typ.base, cl)
|
reprAux(result, p, typ.base, cl)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -865,6 +865,15 @@ proc validEmailAddress*(s: string): bool =
|
||||||
"aero", "jobs", "museum": return true
|
"aero", "jobs", "museum": return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
proc validIdentifier*(s: string): bool =
|
||||||
|
## returns true if `s` is a valid identifier. A valid identifier starts
|
||||||
|
## with a character of the set `IdentStartChars` and is followed by any
|
||||||
|
## number of characters of the set `IdentChars`.
|
||||||
|
if s[0] in IdentStartChars:
|
||||||
|
for i in 1..s.len-1:
|
||||||
|
if s[i] notin IdentChars: return false
|
||||||
|
return true
|
||||||
|
|
||||||
proc editDistance*(a, b: string): int =
|
proc editDistance*(a, b: string): int =
|
||||||
## returns the edit distance between `a` and `b`. This uses the Levenshtein
|
## returns the edit distance between `a` and `b`. This uses the Levenshtein
|
||||||
## distance algorithm with only a linear memory overhead. This implementation
|
## distance algorithm with only a linear memory overhead. This implementation
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2006 Andreas Rumpf
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -54,7 +54,12 @@ proc readLine(f: TFile): string =
|
||||||
rawReadLine(f, result)
|
rawReadLine(f, result)
|
||||||
|
|
||||||
proc write(f: TFile, s: string) = fputs(s, f)
|
proc write(f: TFile, s: string) = fputs(s, f)
|
||||||
proc write(f: TFile, i: int) = fprintf(f, "%ld", i)
|
proc write(f: TFile, i: int) =
|
||||||
|
when sizeof(int) == 8:
|
||||||
|
fprintf(f, "%lld", i)
|
||||||
|
else:
|
||||||
|
fprintf(f, "%ld", i)
|
||||||
|
|
||||||
proc write(f: TFile, b: bool) =
|
proc write(f: TFile, b: bool) =
|
||||||
if b: write(f, "true")
|
if b: write(f, "true")
|
||||||
else: write(f, "false")
|
else: write(f, "false")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2006 Andreas Rumpf
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -37,27 +37,20 @@ proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
|
||||||
proc rawNewString(space: int): NimString {.compilerProc.} =
|
proc rawNewString(space: int): NimString {.compilerProc.} =
|
||||||
var s = space
|
var s = space
|
||||||
if s < 8: s = 7
|
if s < 8: s = 7
|
||||||
when defined(boehmGC):
|
result = cast[NimString](newObj(addr(strDesc), sizeof(TGenericSeq) +
|
||||||
result = cast[NimString](boehmAllocAtomic(
|
(s+1) * sizeof(char)))
|
||||||
sizeof(TGenericSeq) + (s+1) * sizeof(char)))
|
|
||||||
result.len = 0
|
|
||||||
result.data[0] = '\0'
|
|
||||||
else:
|
|
||||||
result = cast[NimString](newObj(addr(strDesc), sizeof(TGenericSeq) +
|
|
||||||
(s+1) * sizeof(char)))
|
|
||||||
result.space = s
|
result.space = s
|
||||||
|
|
||||||
proc mnewString(len: int): NimString {.exportc.} =
|
proc mnewString(len: int): NimString {.exportc.} =
|
||||||
|
#c_fprintf(c_stdout, "[NEWSTRING] len: %ld\n", len)
|
||||||
result = rawNewString(len)
|
result = rawNewString(len)
|
||||||
result.len = len
|
result.len = len
|
||||||
when defined(boehmGC):
|
|
||||||
result.data[len] = '\0'
|
|
||||||
|
|
||||||
proc toNimStr(str: CString, len: int): NimString {.compilerProc.} =
|
proc toNimStr(str: CString, len: int): NimString {.compilerProc.} =
|
||||||
result = rawNewString(len)
|
result = rawNewString(len)
|
||||||
result.len = len
|
result.len = len
|
||||||
c_memcpy(result.data, str, (len+1) * sizeof(Char))
|
c_memcpy(result.data, str, (len+1) * sizeof(Char))
|
||||||
result.data[len] = '\0' # IO.readline relies on this!
|
result.data[len] = '\0' # readline relies on this!
|
||||||
|
|
||||||
proc cstrToNimstr(str: CString): NimString {.compilerProc.} =
|
proc cstrToNimstr(str: CString): NimString {.compilerProc.} =
|
||||||
return toNimstr(str, c_strlen(str))
|
return toNimstr(str, c_strlen(str))
|
||||||
|
|
@ -184,9 +177,9 @@ proc setLengthStr(s: NimString, newLen: int): NimString {.compilerProc.} =
|
||||||
|
|
||||||
proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
|
proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
|
||||||
# increments the length by one:
|
# increments the length by one:
|
||||||
# this is needed for supporting the &= operator;
|
# this is needed for supporting ``add``;
|
||||||
#
|
#
|
||||||
# add seq x generates:
|
# add(seq, x) generates:
|
||||||
# seq = incrSeq(seq, sizeof(x));
|
# seq = incrSeq(seq, sizeof(x));
|
||||||
# seq[seq->len-1] = x;
|
# seq[seq->len-1] = x;
|
||||||
when false:
|
when false:
|
||||||
|
|
@ -229,7 +222,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
|
||||||
GenericSeqSize))
|
GenericSeqSize))
|
||||||
elif newLen < result.len:
|
elif newLen < result.len:
|
||||||
# we need to decref here, otherwise the GC leaks!
|
# we need to decref here, otherwise the GC leaks!
|
||||||
when not defined(boehmGC):
|
when not defined(boehmGC) and not defined(nogc):
|
||||||
for i in newLen..result.len-1:
|
for i in newLen..result.len-1:
|
||||||
forAllChildrenAux(cast[pointer](cast[TAddress](result) +%
|
forAllChildrenAux(cast[pointer](cast[TAddress](result) +%
|
||||||
GenericSeqSize +% (i*%elemSize)),
|
GenericSeqSize +% (i*%elemSize)),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2008 Andreas Rumpf
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -415,11 +415,11 @@ proc `<=` *(x, y: int32): bool {.magic: "LeI", noSideEffect.}
|
||||||
proc `<=` *(x, y: int64): bool {.magic: "LeI64", noSideEffect.}
|
proc `<=` *(x, y: int64): bool {.magic: "LeI64", noSideEffect.}
|
||||||
## Returns true iff `x` is less than or equal to `y`.
|
## Returns true iff `x` is less than or equal to `y`.
|
||||||
|
|
||||||
proc `<` *(x, y: int): bool {.magic: "LtI", noSideEffect.}
|
proc `<` *(x, y: int): bool {.magic: "LtI", noSideEffect.}
|
||||||
proc `<` *(x, y: int8): bool {.magic: "LtI", noSideEffect.}
|
proc `<` *(x, y: int8): bool {.magic: "LtI", noSideEffect.}
|
||||||
proc `<` *(x, y: int16): bool {.magic: "LtI", noSideEffect.}
|
proc `<` *(x, y: int16): bool {.magic: "LtI", noSideEffect.}
|
||||||
proc `<` *(x, y: int32): bool {.magic: "LtI", noSideEffect.}
|
proc `<` *(x, y: int32): bool {.magic: "LtI", noSideEffect.}
|
||||||
proc `<` *(x, y: int64): bool {.magic: "LtI64", noSideEffect.}
|
proc `<` *(x, y: int64): bool {.magic: "LtI64", noSideEffect.}
|
||||||
## Returns true iff `x` is less than `y`.
|
## Returns true iff `x` is less than `y`.
|
||||||
|
|
||||||
proc abs*(x: int): int {.magic: "AbsI", noSideEffect.}
|
proc abs*(x: int): int {.magic: "AbsI", noSideEffect.}
|
||||||
|
|
@ -790,9 +790,10 @@ proc addQuitProc*(QuitProc: proc {.noconv.}) {.importc: "atexit", nodecl.}
|
||||||
# In case of an unhandled exeption the exit handlers should
|
# In case of an unhandled exeption the exit handlers should
|
||||||
# not be called explicitly! The user may decide to do this manually though.
|
# not be called explicitly! The user may decide to do this manually though.
|
||||||
|
|
||||||
proc copy*(s: string, first = 0): string {.importc: "copyStr", noSideEffect.}
|
proc copy*(s: string, first = 0): string {.
|
||||||
proc copy*(s: string, first, last: int): string {.importc: "copyStrLast",
|
magic: "CopyStr", importc: "copyStr", noSideEffect.}
|
||||||
noSideEffect.}
|
proc copy*(s: string, first, last: int): string {.
|
||||||
|
magic: "CopyStrLast", importc: "copyStrLast", noSideEffect.}
|
||||||
## copies a slice of `s` into a new string and returns this new
|
## copies a slice of `s` into a new string and returns this new
|
||||||
## string. The bounds `first` and `last` denote the indices of
|
## string. The bounds `first` and `last` denote the indices of
|
||||||
## the first and last characters that shall be copied. If ``last``
|
## the first and last characters that shall be copied. If ``last``
|
||||||
|
|
@ -803,7 +804,8 @@ proc setLen*(s: var string, newlen: int) {.magic: "SetLengthStr".}
|
||||||
## If the current length is greater than the new length,
|
## If the current length is greater than the new length,
|
||||||
## ``s`` will be truncated.
|
## ``s`` will be truncated.
|
||||||
|
|
||||||
proc newString*(len: int): string {.importc: "mnewString", noSideEffect.}
|
proc newString*(len: int): string {.
|
||||||
|
magic: "NewString", importc: "mnewString", noSideEffect.}
|
||||||
## returns a new string of length ``len`` but with uninitialized
|
## returns a new string of length ``len`` but with uninitialized
|
||||||
## content. One needs to fill the string character after character
|
## content. One needs to fill the string character after character
|
||||||
## with the index operator ``s[i]``. This procedure exists only for
|
## with the index operator ``s[i]``. This procedure exists only for
|
||||||
|
|
@ -835,30 +837,23 @@ proc equalMem*(a, b: Pointer, size: int): bool {.
|
||||||
## otherwise. Like any procedure dealing with raw memory this is
|
## otherwise. Like any procedure dealing with raw memory this is
|
||||||
## *unsafe*.
|
## *unsafe*.
|
||||||
|
|
||||||
const
|
proc alloc*(size: int): pointer {.noconv.}
|
||||||
mallocHeader = "<stdlib.h>"
|
|
||||||
|
|
||||||
proc alloc*(size: int): pointer {.
|
|
||||||
importc: "malloc", header: mallocHeader, noconv.}
|
|
||||||
## allocates a new memory block with at least ``size`` bytes. The
|
## allocates a new memory block with at least ``size`` bytes. The
|
||||||
## block has to be freed with ``realloc(block, 0)`` or
|
## block has to be freed with ``realloc(block, 0)`` or
|
||||||
## ``dealloc(block)``. The block is not initialized, so reading
|
## ``dealloc(block)``. The block is not initialized, so reading
|
||||||
## from it before writing to it is undefined behaviour!
|
## from it before writing to it is undefined behaviour!
|
||||||
proc alloc0*(size: int): pointer {.
|
proc alloc0*(size: int): pointer {.noconv.}
|
||||||
importc: "ALLOC_0", header: mallocHeader, noconv.}
|
|
||||||
## allocates a new memory block with at least ``size`` bytes. The
|
## allocates a new memory block with at least ``size`` bytes. The
|
||||||
## block has to be freed with ``realloc(block, 0)`` or
|
## block has to be freed with ``realloc(block, 0)`` or
|
||||||
## ``dealloc(block)``. The block is initialized with all bytes
|
## ``dealloc(block)``. The block is initialized with all bytes
|
||||||
## containing zero, so it is somewhat safer than ``alloc``.
|
## containing zero, so it is somewhat safer than ``alloc``.
|
||||||
proc realloc*(p: Pointer, newsize: int): pointer {.
|
proc realloc*(p: Pointer, newsize: int): pointer {.noconv.}
|
||||||
importc: "realloc", header: mallocHeader, noconv.}
|
|
||||||
## grows or shrinks a given memory block. If p is **nil** then a new
|
## grows or shrinks a given memory block. If p is **nil** then a new
|
||||||
## memory block is returned. In either way the block has at least
|
## memory block is returned. In either way the block has at least
|
||||||
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
|
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
|
||||||
## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
|
## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
|
||||||
## be freed with ``dealloc``.
|
## be freed with ``dealloc``.
|
||||||
proc dealloc*(p: Pointer) {.
|
proc dealloc*(p: Pointer) {.noconv.}
|
||||||
importc: "free", header: mallocHeader, noconv.}
|
|
||||||
## frees the memory allocated with ``alloc``, ``alloc0`` or
|
## frees the memory allocated with ``alloc``, ``alloc0`` or
|
||||||
## ``realloc``. This procedure is dangerous! If one forgets to
|
## ``realloc``. This procedure is dangerous! If one forgets to
|
||||||
## free the memory a leak occurs; if one tries to access freed
|
## free the memory a leak occurs; if one tries to access freed
|
||||||
|
|
@ -1436,66 +1431,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||||
else:
|
else:
|
||||||
result = n.sons[n.len]
|
result = n.sons[n.len]
|
||||||
|
|
||||||
when defined(boehmgc):
|
include mm
|
||||||
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 sysstr
|
||||||
include assign
|
include assign
|
||||||
include repr
|
include repr
|
||||||
|
|
|
||||||
|
|
@ -3879,7 +3879,7 @@ const
|
||||||
SW_SHOWNORMAL* = 1
|
SW_SHOWNORMAL* = 1
|
||||||
WPF_RESTORETOMAXIMIZED* = 2
|
WPF_RESTORETOMAXIMIZED* = 2
|
||||||
WPF_SETMINPOSITION* = 1 # Sleep
|
WPF_SETMINPOSITION* = 1 # Sleep
|
||||||
INFINITE* = 0xFFFFFFFF # SystemParametersInfo
|
INFINITE* = -1'i32 # SystemParametersInfo
|
||||||
SPI_GETBEEP* = 1
|
SPI_GETBEEP* = 1
|
||||||
SPI_SETBEEP* = 2
|
SPI_SETBEEP* = 2
|
||||||
SPI_GETMOUSE* = 3
|
SPI_GETMOUSE* = 3
|
||||||
|
|
|
||||||
108
lib/winlean.nim
Normal file
108
lib/winlean.nim
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nimrod's Runtime Library
|
||||||
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module implements a small wrapper for some needed Win API procedures,
|
||||||
|
## so that the Nimrod compiler does not depend on the huge Windows module.
|
||||||
|
|
||||||
|
type
|
||||||
|
THandle* = int
|
||||||
|
WINBOOL* = int32
|
||||||
|
|
||||||
|
TSECURITY_ATTRIBUTES* {.final, pure.} = object
|
||||||
|
nLength*: int32
|
||||||
|
lpSecurityDescriptor*: pointer
|
||||||
|
bInheritHandle*: WINBOOL
|
||||||
|
|
||||||
|
TSTARTUPINFO* {.final, pure.} = object
|
||||||
|
cb*: int32
|
||||||
|
lpReserved*: cstring
|
||||||
|
lpDesktop*: cstring
|
||||||
|
lpTitle*: cstring
|
||||||
|
dwX*: int32
|
||||||
|
dwY*: int32
|
||||||
|
dwXSize*: int32
|
||||||
|
dwYSize*: int32
|
||||||
|
dwXCountChars*: int32
|
||||||
|
dwYCountChars*: int32
|
||||||
|
dwFillAttribute*: int32
|
||||||
|
dwFlags*: int32
|
||||||
|
wShowWindow*: int16
|
||||||
|
cbReserved2*: int16
|
||||||
|
lpReserved2*: pointer
|
||||||
|
hStdInput*: THANDLE
|
||||||
|
hStdOutput*: THANDLE
|
||||||
|
hStdError*: THANDLE
|
||||||
|
|
||||||
|
TPROCESS_INFORMATION* {.final, pure.} = object
|
||||||
|
hProcess*: THANDLE
|
||||||
|
hThread*: THANDLE
|
||||||
|
dwProcessId*: int32
|
||||||
|
dwThreadId*: int32
|
||||||
|
|
||||||
|
const
|
||||||
|
STARTF_USESHOWWINDOW* = 1'i32
|
||||||
|
STARTF_USESTDHANDLES* = 256'i32
|
||||||
|
HIGH_PRIORITY_CLASS* = 128'i32
|
||||||
|
IDLE_PRIORITY_CLASS* = 64'i32
|
||||||
|
NORMAL_PRIORITY_CLASS* = 32'i32
|
||||||
|
REALTIME_PRIORITY_CLASS* = 256'i32
|
||||||
|
WAIT_TIMEOUT* = 0x00000102'i32
|
||||||
|
INFINITE* = -1'i32
|
||||||
|
|
||||||
|
STD_INPUT_HANDLE* = -10'i32
|
||||||
|
STD_OUTPUT_HANDLE* = -11'i32
|
||||||
|
STD_ERROR_HANDLE* = -12'i32
|
||||||
|
|
||||||
|
proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
|
||||||
|
importc: "CloseHandle".}
|
||||||
|
|
||||||
|
proc ReadFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToRead: int32,
|
||||||
|
lpNumberOfBytesRead: var int32, lpOverlapped: pointer): WINBOOL{.
|
||||||
|
stdcall, dynlib: "kernel32", importc: "ReadFile".}
|
||||||
|
|
||||||
|
proc WriteFile*(hFile: THandle, Buffer: pointer, nNumberOfBytesToWrite: int32,
|
||||||
|
lpNumberOfBytesWritten: var int32,
|
||||||
|
lpOverlapped: pointer): WINBOOL{.
|
||||||
|
stdcall, dynlib: "kernel32", importc: "WriteFile".}
|
||||||
|
|
||||||
|
proc CreatePipe*(hReadPipe, hWritePipe: var THandle,
|
||||||
|
lpPipeAttributes: var TSECURITY_ATTRIBUTES,
|
||||||
|
nSize: int32): WINBOOL{.
|
||||||
|
stdcall, dynlib: "kernel32", importc: "CreatePipe".}
|
||||||
|
|
||||||
|
proc CreateProcess*(lpApplicationName, lpCommandLine: cstring,
|
||||||
|
lpProcessAttributes: ptr TSECURITY_ATTRIBUTES,
|
||||||
|
lpThreadAttributes: ptr TSECURITY_ATTRIBUTES,
|
||||||
|
bInheritHandles: WINBOOL, dwCreationFlags: int32,
|
||||||
|
lpEnvironment: pointer, lpCurrentDirectory: cstring,
|
||||||
|
lpStartupInfo: var TSTARTUPINFO,
|
||||||
|
lpProcessInformation: var TPROCESS_INFORMATION): WINBOOL{.
|
||||||
|
stdcall, dynlib: "kernel32", importc: "CreateProcessA".}
|
||||||
|
|
||||||
|
proc SuspendThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32",
|
||||||
|
importc: "SuspendThread".}
|
||||||
|
proc ResumeThread*(hThread: THANDLE): int32 {.stdcall, dynlib: "kernel32",
|
||||||
|
importc: "ResumeThread".}
|
||||||
|
|
||||||
|
proc WaitForSingleObject*(hHandle: THANDLE, dwMilliseconds: int32): int32 {.
|
||||||
|
stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
|
||||||
|
|
||||||
|
proc TerminateProcess*(hProcess: THANDLE, uExitCode: int): WINBOOL {.stdcall,
|
||||||
|
dynlib: "kernel32", importc: "TerminateProcess".}
|
||||||
|
|
||||||
|
proc GetExitCodeProcess*(hProcess: THANDLE, lpExitCode: var int32): WINBOOL {.
|
||||||
|
stdcall, dynlib: "kernel32", importc: "GetExitCodeProcess".}
|
||||||
|
|
||||||
|
proc GetStdHandle*(nStdHandle: int32): THANDLE {.stdcall, dynlib: "kernel32",
|
||||||
|
importc: "GetStdHandle".}
|
||||||
|
proc SetStdHandle*(nStdHandle: int32, hHandle: THANDLE): WINBOOL {.stdcall,
|
||||||
|
dynlib: "kernel32", importc: "SetStdHandle".}
|
||||||
|
proc FlushFileBuffers*(hFile: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
|
||||||
|
importc: "FlushFileBuffers".}
|
||||||
|
|
||||||
1107
lib/wz_jsgraphics.js
1107
lib/wz_jsgraphics.js
File diff suppressed because it is too large
Load diff
406
lib/xmlgen.nim
Normal file
406
lib/xmlgen.nim
Normal file
|
|
@ -0,0 +1,406 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nimrod's Runtime Library
|
||||||
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module implements a simple `XML`:idx: and `HTML`:idx: code
|
||||||
|
## generator. Each commonly used HTML tag has a corresponding macro
|
||||||
|
## that generates a string with its HTML representation.
|
||||||
|
##
|
||||||
|
## Example:
|
||||||
|
##
|
||||||
|
## .. code-block:: nimrod
|
||||||
|
## var nim = "Nimrod"
|
||||||
|
## echo h1(a(href="http://force7.de/nimrod", nim))
|
||||||
|
##
|
||||||
|
## Writes the string::
|
||||||
|
##
|
||||||
|
## <h1><a href="http://force7.de/nimrod">Nimrod</a></h1>
|
||||||
|
##
|
||||||
|
|
||||||
|
import
|
||||||
|
macros, strutils
|
||||||
|
|
||||||
|
const
|
||||||
|
coreAttr* = " id class title style "
|
||||||
|
eventAttr* = " onclick ondblclick onmousedown onmouseup " &
|
||||||
|
"onmouseover onmousemove onmouseout onkeypress onkeydown onkeyup "
|
||||||
|
commonAttr* = coreAttr & eventAttr
|
||||||
|
|
||||||
|
proc getIdent(e: PNimrodNode): string {.compileTime.} =
|
||||||
|
case e.kind
|
||||||
|
of nnkIdent: result = normalize($e.ident)
|
||||||
|
of nnkAccQuoted: result = getIdent(e[0])
|
||||||
|
else: error("cannot extract identifier from node: " & toStrLit(e).strVal)
|
||||||
|
|
||||||
|
proc delete[T](s: var seq[T], attr: T): bool =
|
||||||
|
var idx = find(s, attr)
|
||||||
|
if idx >= 0:
|
||||||
|
var L = s.len
|
||||||
|
s[idx] = s[L-1]
|
||||||
|
setLen(s, L-1)
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc xmlCheckedTag*(e: PNimrodNode, tag: string,
|
||||||
|
optAttr = "", reqAttr = "",
|
||||||
|
isLeaf = false): PNimrodNode {.compileTime.} =
|
||||||
|
## use this procedure to define a new XML tag
|
||||||
|
|
||||||
|
# copy the attributes; when iterating over them these lists
|
||||||
|
# will be modified, so that each attribute is only given one value
|
||||||
|
var req = splitSeq(reqAttr)
|
||||||
|
var opt = splitSeq(optAttr)
|
||||||
|
result = newNimNode(nnkBracket, e)
|
||||||
|
result.add(newStrLitNode("<"))
|
||||||
|
result.add(newStrLitNode(tag))
|
||||||
|
# first pass over attributes:
|
||||||
|
for i in 1..e.len-1:
|
||||||
|
if e[i].kind == nnkExprEqExpr:
|
||||||
|
var name = getIdent(e[i][0])
|
||||||
|
if delete(req, name) or delete(opt, name):
|
||||||
|
result.add(newStrLitNode(" "))
|
||||||
|
result.add(newStrLitNode(name))
|
||||||
|
result.add(newStrLitNode("=\""))
|
||||||
|
result.add(e[i][1])
|
||||||
|
result.add(newStrLitNode("\""))
|
||||||
|
else:
|
||||||
|
error("invalid attribute for '" & tag & "' element: " & name)
|
||||||
|
# check each required attribute exists:
|
||||||
|
if req.len > 0:
|
||||||
|
error(req[0] & " attribute for '" & tag & "' element expected")
|
||||||
|
if isLeaf:
|
||||||
|
for i in 1..e.len-1:
|
||||||
|
if e[i].kind != nnkExprEqExpr:
|
||||||
|
error("element " & tag & " cannot be nested")
|
||||||
|
result.add(newStrLitNode(" />"))
|
||||||
|
else:
|
||||||
|
result.add(newStrLitNode(">"))
|
||||||
|
# second pass over elements:
|
||||||
|
for i in 1..e.len-1:
|
||||||
|
if e[i].kind != nnkExprEqExpr: result.add(e[i])
|
||||||
|
result.add(newStrLitNode("</"))
|
||||||
|
result.add(newStrLitNode(tag))
|
||||||
|
result.add(newStrLitNode(">"))
|
||||||
|
result = NestList(!"&", result)
|
||||||
|
|
||||||
|
|
||||||
|
macro a*(e: expr): expr =
|
||||||
|
## generates the HTML ``a`` element.
|
||||||
|
result = xmlCheckedTag(e, "a", "href charset type hreflang rel rev " &
|
||||||
|
"accesskey tabindex" & commonAttr)
|
||||||
|
|
||||||
|
macro acronym*(e: expr): expr =
|
||||||
|
## generates the HTML ``acronym`` element.
|
||||||
|
result = xmlCheckedTag(e, "acronym", commonAttr)
|
||||||
|
|
||||||
|
macro address*(e: expr): expr =
|
||||||
|
## generates the HTML ``address`` element.
|
||||||
|
result = xmlCheckedTag(e, "address", commonAttr)
|
||||||
|
|
||||||
|
macro area*(e: expr): expr =
|
||||||
|
## generates the HTML ``area`` element.
|
||||||
|
result = xmlCheckedTag(e, "area", "shape coords href nohref" &
|
||||||
|
" accesskey tabindex" & commonAttr, "alt", true)
|
||||||
|
|
||||||
|
macro b*(e: expr): expr =
|
||||||
|
## generates the HTML ``b`` element.
|
||||||
|
result = xmlCheckedTag(e, "b", commonAttr)
|
||||||
|
|
||||||
|
macro base*(e: expr): expr =
|
||||||
|
## generates the HTML ``base`` element.
|
||||||
|
result = xmlCheckedTag(e, "base", "", "href", true)
|
||||||
|
|
||||||
|
macro big*(e: expr): expr =
|
||||||
|
## generates the HTML ``big`` element.
|
||||||
|
result = xmlCheckedTag(e, "big", commonAttr)
|
||||||
|
|
||||||
|
macro blockquote*(e: expr): expr =
|
||||||
|
## generates the HTML ``blockquote`` element.
|
||||||
|
result = xmlCheckedTag(e, "blockquote", " cite" & commonAttr)
|
||||||
|
|
||||||
|
macro body*(e: expr): expr =
|
||||||
|
## generates the HTML ``body`` element.
|
||||||
|
result = xmlCheckedTag(e, "body", commonAttr)
|
||||||
|
|
||||||
|
macro br*(e: expr): expr =
|
||||||
|
## generates the HTML ``br`` element.
|
||||||
|
result = xmlCheckedTag(e, "br", "", "", true)
|
||||||
|
|
||||||
|
macro button*(e: expr): expr =
|
||||||
|
## generates the HTML ``button`` element.
|
||||||
|
result = xmlCheckedTag(e, "button", "accesskey tabindex " &
|
||||||
|
"disabled name type value" & commonAttr)
|
||||||
|
|
||||||
|
macro caption*(e: expr): expr =
|
||||||
|
## generates the HTML ``caption`` element.
|
||||||
|
result = xmlCheckedTag(e, "caption", commonAttr)
|
||||||
|
|
||||||
|
macro cite*(e: expr): expr =
|
||||||
|
## generates the HTML ``cite`` element.
|
||||||
|
result = xmlCheckedTag(e, "cite", commonAttr)
|
||||||
|
|
||||||
|
macro code*(e: expr): expr =
|
||||||
|
## generates the HTML ``code`` element.
|
||||||
|
result = xmlCheckedTag(e, "code", commonAttr)
|
||||||
|
|
||||||
|
macro col*(e: expr): expr =
|
||||||
|
## generates the HTML ``col`` element.
|
||||||
|
result = xmlCheckedTag(e, "col", "span align valign" & commonAttr, "", true)
|
||||||
|
|
||||||
|
macro colgroup*(e: expr): expr =
|
||||||
|
## generates the HTML ``colgroup`` element.
|
||||||
|
result = xmlCheckedTag(e, "colgroup", "span align valign" & commonAttr)
|
||||||
|
|
||||||
|
macro dd*(e: expr): expr =
|
||||||
|
## generates the HTML ``dd`` element.
|
||||||
|
result = xmlCheckedTag(e, "dd", commonAttr)
|
||||||
|
|
||||||
|
macro del*(e: expr): expr =
|
||||||
|
## generates the HTML ``del`` element.
|
||||||
|
result = xmlCheckedTag(e, "del", "cite datetime" & commonAttr)
|
||||||
|
|
||||||
|
macro dfn*(e: expr): expr =
|
||||||
|
## generates the HTML ``dfn`` element.
|
||||||
|
result = xmlCheckedTag(e, "dfn", commonAttr)
|
||||||
|
|
||||||
|
macro `div`*(e: expr): expr =
|
||||||
|
## generates the HTML ``div`` element.
|
||||||
|
result = xmlCheckedTag(e, "div", commonAttr)
|
||||||
|
|
||||||
|
macro dl*(e: expr): expr =
|
||||||
|
## generates the HTML ``dl`` element.
|
||||||
|
result = xmlCheckedTag(e, "dl", commonAttr)
|
||||||
|
|
||||||
|
macro dt*(e: expr): expr =
|
||||||
|
## generates the HTML ``dt`` element.
|
||||||
|
result = xmlCheckedTag(e, "dt", commonAttr)
|
||||||
|
|
||||||
|
macro em*(e: expr): expr =
|
||||||
|
## generates the HTML ``em`` element.
|
||||||
|
result = xmlCheckedTag(e, "em", commonAttr)
|
||||||
|
|
||||||
|
macro fieldset*(e: expr): expr =
|
||||||
|
## generates the HTML ``fieldset`` element.
|
||||||
|
result = xmlCheckedTag(e, "fieldset", commonAttr)
|
||||||
|
|
||||||
|
macro form*(e: expr): expr =
|
||||||
|
## generates the HTML ``form`` element.
|
||||||
|
result = xmlCheckedTag(e, "form", "method encype accept accept-charset" &
|
||||||
|
commonAttr, "action")
|
||||||
|
|
||||||
|
macro h1*(e: expr): expr =
|
||||||
|
## generates the HTML ``h1`` element.
|
||||||
|
result = xmlCheckedTag(e, "h1", commonAttr)
|
||||||
|
|
||||||
|
macro h2*(e: expr): expr =
|
||||||
|
## generates the HTML ``h2`` element.
|
||||||
|
result = xmlCheckedTag(e, "h2", commonAttr)
|
||||||
|
|
||||||
|
macro h3*(e: expr): expr =
|
||||||
|
## generates the HTML ``h3`` element.
|
||||||
|
result = xmlCheckedTag(e, "h3", commonAttr)
|
||||||
|
|
||||||
|
macro h4*(e: expr): expr =
|
||||||
|
## generates the HTML ``h4`` element.
|
||||||
|
result = xmlCheckedTag(e, "h4", commonAttr)
|
||||||
|
|
||||||
|
macro h5*(e: expr): expr =
|
||||||
|
## generates the HTML ``h5`` element.
|
||||||
|
result = xmlCheckedTag(e, "h5", commonAttr)
|
||||||
|
|
||||||
|
macro h6*(e: expr): expr =
|
||||||
|
## generates the HTML ``h6`` element.
|
||||||
|
result = xmlCheckedTag(e, "h6", commonAttr)
|
||||||
|
|
||||||
|
macro head*(e: expr): expr =
|
||||||
|
## generates the HTML ``head`` element.
|
||||||
|
result = xmlCheckedTag(e, "head", "profile")
|
||||||
|
|
||||||
|
macro html*(e: expr): expr =
|
||||||
|
## generates the HTML ``html`` element.
|
||||||
|
result = xmlCheckedTag(e, "html", "", "xmlns")
|
||||||
|
|
||||||
|
macro hr*(e: expr): expr =
|
||||||
|
## generates the HTML ``hr`` element.
|
||||||
|
result = xmlCheckedTag(e, "hr", commonAttr, "", true)
|
||||||
|
|
||||||
|
macro i*(e: expr): expr =
|
||||||
|
## generates the HTML ``i`` element.
|
||||||
|
result = xmlCheckedTag(e, "i", commonAttr)
|
||||||
|
|
||||||
|
macro img*(e: expr): expr =
|
||||||
|
## generates the HTML ``img`` element.
|
||||||
|
result = xmlCheckedTag(e, "img", "longdesc height width", "src alt", true)
|
||||||
|
|
||||||
|
macro input*(e: expr): expr =
|
||||||
|
## generates the HTML ``input`` element.
|
||||||
|
result = xmlCheckedTag(e, "input", "name type value checked maxlength src" &
|
||||||
|
" alt accept disabled readonly accesskey tabindex" & commonAttr, "", true)
|
||||||
|
|
||||||
|
macro ins*(e: expr): expr =
|
||||||
|
## generates the HTML ``ins`` element.
|
||||||
|
result = xmlCheckedTag(e, "ins", "cite datetime" & commonAttr)
|
||||||
|
|
||||||
|
macro kbd*(e: expr): expr =
|
||||||
|
## generates the HTML ``kbd`` element.
|
||||||
|
result = xmlCheckedTag(e, "kbd", commonAttr)
|
||||||
|
|
||||||
|
macro label*(e: expr): expr =
|
||||||
|
## generates the HTML ``label`` element.
|
||||||
|
result = xmlCheckedTag(e, "label", "for accesskey" & commonAttr)
|
||||||
|
|
||||||
|
macro legend*(e: expr): expr =
|
||||||
|
## generates the HTML ``legend`` element.
|
||||||
|
result = xmlCheckedTag(e, "legend", "accesskey" & commonAttr)
|
||||||
|
|
||||||
|
macro li*(e: expr): expr =
|
||||||
|
## generates the HTML ``li`` element.
|
||||||
|
result = xmlCheckedTag(e, "li", commonAttr)
|
||||||
|
|
||||||
|
macro link*(e: expr): expr =
|
||||||
|
## generates the HTML ``link`` element.
|
||||||
|
result = xmlCheckedTag(e, "link", "href charset hreflang type rel rev media" &
|
||||||
|
commonAttr, "", true)
|
||||||
|
|
||||||
|
macro map*(e: expr): expr =
|
||||||
|
## generates the HTML ``map`` element.
|
||||||
|
result = xmlCheckedTag(e, "map", "class title" & eventAttr, "id", false)
|
||||||
|
|
||||||
|
macro meta*(e: expr): expr =
|
||||||
|
## generates the HTML ``meta`` element.
|
||||||
|
result = xmlCheckedTag(e, "meta", "name http-equiv scheme", "content", true)
|
||||||
|
|
||||||
|
macro noscript*(e: expr): expr =
|
||||||
|
## generates the HTML ``noscript`` element.
|
||||||
|
result = xmlCheckedTag(e, "noscript", commonAttr)
|
||||||
|
|
||||||
|
macro `object`*(e: expr): expr =
|
||||||
|
## generates the HTML ``object`` element.
|
||||||
|
result = xmlCheckedTag(e, "object", "classid data codebase declare type " &
|
||||||
|
"codetype archive standby width height name tabindex" & commonAttr)
|
||||||
|
|
||||||
|
macro ol*(e: expr): expr =
|
||||||
|
## generates the HTML ``ol`` element.
|
||||||
|
result = xmlCheckedTag(e, "ol", commonAttr)
|
||||||
|
|
||||||
|
macro optgroup*(e: expr): expr =
|
||||||
|
## generates the HTML ``optgroup`` element.
|
||||||
|
result = xmlCheckedTag(e, "optgroup", "disabled" & commonAttr, "label", false)
|
||||||
|
|
||||||
|
macro option*(e: expr): expr =
|
||||||
|
## generates the HTML ``option`` element.
|
||||||
|
result = xmlCheckedTag(e, "option", "selected value" & commonAttr)
|
||||||
|
|
||||||
|
macro p*(e: expr): expr =
|
||||||
|
## generates the HTML ``p`` element.
|
||||||
|
result = xmlCheckedTag(e, "p", commonAttr)
|
||||||
|
|
||||||
|
macro param*(e: expr): expr =
|
||||||
|
## generates the HTML ``param`` element.
|
||||||
|
result = xmlCheckedTag(e, "param", "value id type valuetype", "name", true)
|
||||||
|
|
||||||
|
macro pre*(e: expr): expr =
|
||||||
|
## generates the HTML ``pre`` element.
|
||||||
|
result = xmlCheckedTag(e, "pre", commonAttr)
|
||||||
|
|
||||||
|
macro q*(e: expr): expr =
|
||||||
|
## generates the HTML ``q`` element.
|
||||||
|
result = xmlCheckedTag(e, "q", "cite" & commonAttr)
|
||||||
|
|
||||||
|
macro samp*(e: expr): expr =
|
||||||
|
## generates the HTML ``samp`` element.
|
||||||
|
result = xmlCheckedTag(e, "samp", commonAttr)
|
||||||
|
|
||||||
|
macro script*(e: expr): expr =
|
||||||
|
## generates the HTML ``script`` element.
|
||||||
|
result = xmlCheckedTag(e, "script", "src charset defer", "type", false)
|
||||||
|
|
||||||
|
macro select*(e: expr): expr =
|
||||||
|
## generates the HTML ``select`` element.
|
||||||
|
result = xmlCheckedTag(e, "select", "name size multiple disabled tabindex" &
|
||||||
|
commonAttr)
|
||||||
|
|
||||||
|
macro small*(e: expr): expr =
|
||||||
|
## generates the HTML ``small`` element.
|
||||||
|
result = xmlCheckedTag(e, "small", commonAttr)
|
||||||
|
|
||||||
|
macro span*(e: expr): expr =
|
||||||
|
## generates the HTML ``span`` element.
|
||||||
|
result = xmlCheckedTag(e, "span", commonAttr)
|
||||||
|
|
||||||
|
macro strong*(e: expr): expr =
|
||||||
|
## generates the HTML ``strong`` element.
|
||||||
|
result = xmlCheckedTag(e, "strong", commonAttr)
|
||||||
|
|
||||||
|
macro style*(e: expr): expr =
|
||||||
|
## generates the HTML ``style`` element.
|
||||||
|
result = xmlCheckedTag(e, "style", "media title", "type")
|
||||||
|
|
||||||
|
macro sub*(e: expr): expr =
|
||||||
|
## generates the HTML ``sub`` element.
|
||||||
|
result = xmlCheckedTag(e, "sub", commonAttr)
|
||||||
|
|
||||||
|
macro sup*(e: expr): expr =
|
||||||
|
## generates the HTML ``sup`` element.
|
||||||
|
result = xmlCheckedTag(e, "sup", commonAttr)
|
||||||
|
|
||||||
|
macro table*(e: expr): expr =
|
||||||
|
## generates the HTML ``table`` element.
|
||||||
|
result = xmlCheckedTag(e, "table", "summary border cellpadding cellspacing" &
|
||||||
|
" frame rules width" & commonAttr)
|
||||||
|
|
||||||
|
macro tbody*(e: expr): expr =
|
||||||
|
## generates the HTML ``tbody`` element.
|
||||||
|
result = xmlCheckedTag(e, "tbody", "align valign" & commonAttr)
|
||||||
|
|
||||||
|
macro td*(e: expr): expr =
|
||||||
|
## generates the HTML ``td`` element.
|
||||||
|
result = xmlCheckedTag(e, "td", "colspan rowspan abbr axis headers scope" &
|
||||||
|
" align valign" & commonAttr)
|
||||||
|
|
||||||
|
macro textarea*(e: expr): expr =
|
||||||
|
## generates the HTML ``textarea`` element.
|
||||||
|
result = xmlCheckedTag(e, "textarea", " name disabled readonly accesskey" &
|
||||||
|
" tabindex" & commonAttr, "rows cols", false)
|
||||||
|
|
||||||
|
macro tfoot*(e: expr): expr =
|
||||||
|
## generates the HTML ``tfoot`` element.
|
||||||
|
result = xmlCheckedTag(e, "tfoot", "align valign" & commonAttr)
|
||||||
|
|
||||||
|
macro th*(e: expr): expr =
|
||||||
|
## generates the HTML ``th`` element.
|
||||||
|
result = xmlCheckedTag(e, "th", "colspan rowspan abbr axis headers scope" &
|
||||||
|
" align valign" & commonAttr)
|
||||||
|
|
||||||
|
macro thead*(e: expr): expr =
|
||||||
|
## generates the HTML ``thead`` element.
|
||||||
|
result = xmlCheckedTag(e, "thead", "align valign" & commonAttr)
|
||||||
|
|
||||||
|
macro title*(e: expr): expr =
|
||||||
|
## generates the HTML ``title`` element.
|
||||||
|
result = xmlCheckedTag(e, "title")
|
||||||
|
|
||||||
|
macro tr*(e: expr): expr =
|
||||||
|
## generates the HTML ``tr`` element.
|
||||||
|
result = xmlCheckedTag(e, "tr", "align valign" & commonAttr)
|
||||||
|
|
||||||
|
macro tt*(e: expr): expr =
|
||||||
|
## generates the HTML ``tt`` element.
|
||||||
|
result = xmlCheckedTag(e, "tt", commonAttr)
|
||||||
|
|
||||||
|
macro ul*(e: expr): expr =
|
||||||
|
## generates the HTML ``ul`` element.
|
||||||
|
result = xmlCheckedTag(e, "ul", commonAttr)
|
||||||
|
|
||||||
|
macro `var`*(e: expr): expr =
|
||||||
|
## generates the HTML ``var`` element.
|
||||||
|
result = xmlCheckedTag(e, "var", commonAttr)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
var nim = "Nimrod"
|
||||||
|
echo h1(a(href="http://force7.de/nimrod", nim))
|
||||||
|
|
||||||
50
nim/ast.pas
50
nim/ast.pas
|
|
@ -15,7 +15,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, charsets, msgs, hashes,
|
nsystem, charsets, msgs, nhashes,
|
||||||
nversion, options, strutils, crc, ropes, idents, lists;
|
nversion, options, strutils, crc, ropes, idents, lists;
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -245,18 +245,18 @@ type
|
||||||
mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mConTArr, mConTT,
|
mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mConTArr, mConTT,
|
||||||
mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq, mInRange,
|
mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq, mInRange,
|
||||||
mInSet, mAsgn, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
|
mInSet, mAsgn, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
|
||||||
mAssert, mSwap, mIsNil, mArrToSeq, mArray, mOpenArray,
|
mAssert, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast,
|
||||||
mRange, mSet, mSeq, mInt, mInt8, mInt16,
|
mNewString, mArray, mOpenArray, mRange, mSet, mSeq,
|
||||||
mInt32, mInt64, mFloat, mFloat32, mFloat64, mBool,
|
mInt, mInt8, mInt16, mInt32, mInt64, mFloat,
|
||||||
mChar, mString, mCstring, mPointer, mAnyEnum, mEmptySet,
|
mFloat32, mFloat64, mBool, mChar, mString, mCstring,
|
||||||
mIntSetBaseType, mNil, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion,
|
mPointer, mAnyEnum, mEmptySet, mIntSetBaseType, mNil, mIsMainModule,
|
||||||
mNimrodMajor, mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU,
|
mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor, mNimrodMinor, mNimrodPatch,
|
||||||
mNaN, mInf, mNegInf, mNLen, mNChild, mNSetChild,
|
mCpuEndian, mHostOS, mHostCPU, mNaN, mInf, mNegInf,
|
||||||
mNAdd, mNAddMultiple, mNDel, mNKind, mNIntVal, mNFloatVal,
|
mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel,
|
||||||
mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal,
|
mNKind, mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType,
|
||||||
mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNNewNimNode, mNCopyNimNode,
|
mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType,
|
||||||
mNCopyNimTree, mStrToIdent, mIdentToStr, mEqIdent, mNHint, mNWarning,
|
mNSetStrVal, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
|
||||||
mNError
|
mEqIdent, mNHint, mNWarning, mNError
|
||||||
//[[[end]]]
|
//[[[end]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -504,18 +504,18 @@ const // "MagicToStr" array:
|
||||||
'SymDiffSet', 'ConStrStr', 'ConArrArr', 'ConArrT', 'ConTArr', 'ConTT',
|
'SymDiffSet', 'ConStrStr', 'ConArrArr', 'ConArrT', 'ConTArr', 'ConTT',
|
||||||
'Slice', 'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'AppendSeqSeq', 'InRange',
|
'Slice', 'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'AppendSeqSeq', 'InRange',
|
||||||
'InSet', 'Asgn', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq',
|
'InSet', 'Asgn', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq',
|
||||||
'Assert', 'Swap', 'IsNil', 'ArrToSeq', 'Array', 'OpenArray',
|
'Assert', 'Swap', 'IsNil', 'ArrToSeq', 'CopyStr', 'CopyStrLast',
|
||||||
'Range', 'Set', 'Seq', 'Int', 'Int8', 'Int16',
|
'NewString', 'Array', 'OpenArray', 'Range', 'Set', 'Seq',
|
||||||
'Int32', 'Int64', 'Float', 'Float32', 'Float64', 'Bool',
|
'Int', 'Int8', 'Int16', 'Int32', 'Int64', 'Float',
|
||||||
'Char', 'String', 'Cstring', 'Pointer', 'AnyEnum', 'EmptySet',
|
'Float32', 'Float64', 'Bool', 'Char', 'String', 'Cstring',
|
||||||
'IntSetBaseType', 'Nil', 'IsMainModule', 'CompileDate', 'CompileTime', 'NimrodVersion',
|
'Pointer', 'AnyEnum', 'EmptySet', 'IntSetBaseType', 'Nil', 'IsMainModule',
|
||||||
'NimrodMajor', 'NimrodMinor', 'NimrodPatch', 'CpuEndian', 'HostOS', 'HostCPU',
|
'CompileDate', 'CompileTime', 'NimrodVersion', 'NimrodMajor', 'NimrodMinor', 'NimrodPatch',
|
||||||
'NaN', 'Inf', 'NegInf', 'NLen', 'NChild', 'NSetChild',
|
'CpuEndian', 'HostOS', 'HostCPU', 'NaN', 'Inf', 'NegInf',
|
||||||
'NAdd', 'NAddMultiple', 'NDel', 'NKind', 'NIntVal', 'NFloatVal',
|
'NLen', 'NChild', 'NSetChild', 'NAdd', 'NAddMultiple', 'NDel',
|
||||||
'NSymbol', 'NIdent', 'NGetType', 'NStrVal', 'NSetIntVal', 'NSetFloatVal',
|
'NKind', 'NIntVal', 'NFloatVal', 'NSymbol', 'NIdent', 'NGetType',
|
||||||
'NSetSymbol', 'NSetIdent', 'NSetType', 'NSetStrVal', 'NNewNimNode', 'NCopyNimNode',
|
'NStrVal', 'NSetIntVal', 'NSetFloatVal', 'NSetSymbol', 'NSetIdent', 'NSetType',
|
||||||
'NCopyNimTree', 'StrToIdent', 'IdentToStr', 'EqIdent', 'NHint', 'NWarning',
|
'NSetStrVal', 'NNewNimNode', 'NCopyNimNode', 'NCopyNimTree', 'StrToIdent', 'IdentToStr',
|
||||||
'NError'
|
'EqIdent', 'NHint', 'NWarning', 'NError'
|
||||||
//[[[end]]]
|
//[[[end]]]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, ast, hashes, charsets, strutils, options, msgs, ropes, idents;
|
nsystem, ast, nhashes, charsets, strutils, options, msgs, ropes, idents;
|
||||||
|
|
||||||
function hashNode(p: PObject): THash;
|
function hashNode(p: PObject): THash;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1254,15 +1254,9 @@ begin
|
||||||
refType := skipVarGenericRange(e.sons[1].typ);
|
refType := skipVarGenericRange(e.sons[1].typ);
|
||||||
InitLocExpr(p, e.sons[1], a);
|
InitLocExpr(p, e.sons[1], a);
|
||||||
initLoc(b, locExpr, a.t, OnHeap);
|
initLoc(b, locExpr, a.t, OnHeap);
|
||||||
|
b.r := ropef('($1) newObj($2, sizeof($3))',
|
||||||
if optBoehmGC in gGlobalOptions then
|
[getTypeDesc(p.module, reftype), genTypeInfo(p.module, refType),
|
||||||
b.r := ropef('($1) newObj(sizeof($2))',
|
getTypeDesc(p.module, skipGenericRange(reftype.sons[0]))]);
|
||||||
[getTypeDesc(p.module, reftype),
|
|
||||||
getTypeDesc(p.module, skipGenericRange(reftype.sons[0]))])
|
|
||||||
else
|
|
||||||
b.r := ropef('($1) newObj($2, sizeof($3))',
|
|
||||||
[getTypeDesc(p.module, reftype), genTypeInfo(p.module, refType),
|
|
||||||
getTypeDesc(p.module, skipGenericRange(reftype.sons[0]))]);
|
|
||||||
genAssignment(p, a, b, {@set}[]);
|
genAssignment(p, a, b, {@set}[]);
|
||||||
// set the object type:
|
// set the object type:
|
||||||
bt := skipGenericRange(refType.sons[0]);
|
bt := skipGenericRange(refType.sons[0]);
|
||||||
|
|
@ -1279,16 +1273,10 @@ begin
|
||||||
InitLocExpr(p, e.sons[1], a);
|
InitLocExpr(p, e.sons[1], a);
|
||||||
InitLocExpr(p, e.sons[2], b);
|
InitLocExpr(p, e.sons[2], b);
|
||||||
initLoc(c, locExpr, a.t, OnHeap);
|
initLoc(c, locExpr, a.t, OnHeap);
|
||||||
if optBoehmGC in gGlobalOptions then
|
c.r := ropef('($1) newSeq($2, $3)',
|
||||||
c.r := ropef('($1) newSeq(sizeof($2), $3)',
|
[getTypeDesc(p.module, seqtype),
|
||||||
[getTypeDesc(p.module, seqtype),
|
genTypeInfo(p.module, seqType),
|
||||||
getTypeDesc(p.module, skipGenericRange(seqtype.sons[0])),
|
rdLoc(b)]);
|
||||||
rdLoc(b)])
|
|
||||||
else
|
|
||||||
c.r := ropef('($1) newSeq($2, $3)',
|
|
||||||
[getTypeDesc(p.module, seqtype),
|
|
||||||
genTypeInfo(p.module, seqType),
|
|
||||||
rdLoc(b)]);
|
|
||||||
genAssignment(p, a, c, {@set}[]);
|
genAssignment(p, a, c, {@set}[]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -1945,6 +1933,7 @@ begin
|
||||||
mSetLengthSeq: genSetLengthSeq(p, e, d);
|
mSetLengthSeq: genSetLengthSeq(p, e, d);
|
||||||
mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet,
|
mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet,
|
||||||
mMinusSet, mInSet: genSetOp(p, e, d, op);
|
mMinusSet, mInSet: genSetOp(p, e, d, op);
|
||||||
|
mNewString, mCopyStr, mCopyStrLast: genCall(p, e, d);
|
||||||
mExit: genCall(p, e, d);
|
mExit: genCall(p, e, d);
|
||||||
mArrToSeq: genArrToSeq(p, e, d);
|
mArrToSeq: genArrToSeq(p, e, d);
|
||||||
mNLen..mNError:
|
mNLen..mNError:
|
||||||
|
|
|
||||||
|
|
@ -878,7 +878,7 @@ var
|
||||||
i: int;
|
i: int;
|
||||||
prc: PSym;
|
prc: PSym;
|
||||||
begin
|
begin
|
||||||
assert(t <> nil);
|
//assert(t <> nil);
|
||||||
if inCheckpoint(t.info) then
|
if inCheckpoint(t.info) then
|
||||||
MessageOut(renderTree(t));
|
MessageOut(renderTree(t));
|
||||||
case t.kind of
|
case t.kind of
|
||||||
|
|
@ -913,7 +913,7 @@ begin
|
||||||
nkRaiseStmt: genRaiseStmt(p, t);
|
nkRaiseStmt: genRaiseStmt(p, t);
|
||||||
nkTypeSection: begin
|
nkTypeSection: begin
|
||||||
// we have to emit the type information for object types here to support
|
// we have to emit the type information for object types here to support
|
||||||
// seperate compilation:
|
// separate compilation:
|
||||||
genTypeSection(p.module, t);
|
genTypeSection(p.module, t);
|
||||||
end;
|
end;
|
||||||
nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, nkImportStmt,
|
nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, nkImportStmt,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
charsets, nsystem,
|
charsets, nsystem,
|
||||||
ast, astalgo, ropes, lists, hashes, strutils, types, msgs;
|
ast, astalgo, ropes, lists, nhashes, strutils, types, msgs;
|
||||||
|
|
||||||
function toCChar(c: Char): string;
|
function toCChar(c: Char): string;
|
||||||
function makeCString(const s: string): PRope;
|
function makeCString(const s: string): PRope;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, ast, astalgo, strutils, hashes, trees, platform, magicsys,
|
nsystem, ast, astalgo, strutils, nhashes, trees, platform, magicsys,
|
||||||
extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents,
|
extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents,
|
||||||
lists, types, ccgutils, nos, ntime, ropes, nmath, passes, rodread,
|
lists, types, ccgutils, nos, ntime, ropes, nmath, passes, rodread,
|
||||||
wordrecg, rnimsyn, treetab;
|
wordrecg, rnimsyn, treetab;
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@ const
|
||||||
+{&} ' --checkpoints:on|off turn on|off checkpoints; for debugging Nimrod' +{&} nl
|
+{&} ' --checkpoints:on|off turn on|off checkpoints; for debugging Nimrod' +{&} nl
|
||||||
+{&} ' --skip_cfg do not read the general configuration file' +{&} nl
|
+{&} ' --skip_cfg do not read the general configuration file' +{&} nl
|
||||||
+{&} ' --skip_proj_cfg do not read the project''s configuration file' +{&} nl
|
+{&} ' --skip_proj_cfg do not read the project''s configuration file' +{&} nl
|
||||||
|
+{&} ' --gc:refc|boehm use Nimrod''s native GC|Boehm GC' +{&} nl
|
||||||
+{&} ' --index:FILE use FILE to generate a documenation index file' +{&} nl
|
+{&} ' --index:FILE use FILE to generate a documenation index file' +{&} nl
|
||||||
+{&} ' --putenv:key=value set an environment variable' +{&} nl
|
+{&} ' --putenv:key=value set an environment variable' +{&} nl
|
||||||
+{&} ' --list_cmd list the commands used to execute external programs' +{&} nl
|
+{&} ' --list_cmd list the commands used to execute external programs' +{&} nl
|
||||||
|
|
@ -244,6 +245,13 @@ begin
|
||||||
liMessage(info, errCmdLineArgExpected, switch)
|
liMessage(info, errCmdLineArgExpected, switch)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure ExpectNoArg(const switch, arg: string; pass: TCmdLinePass;
|
||||||
|
const info: TLineInfo);
|
||||||
|
begin
|
||||||
|
if (arg <> '') then
|
||||||
|
liMessage(info, errCmdLineNoArgExpected, switch)
|
||||||
|
end;
|
||||||
|
|
||||||
procedure ProcessSpecificNote(const arg: string; state: TSpecialWord;
|
procedure ProcessSpecificNote(const arg: string; state: TSpecialWord;
|
||||||
pass: TCmdlinePass; const info: TLineInfo);
|
pass: TCmdlinePass; const info: TLineInfo);
|
||||||
var
|
var
|
||||||
|
|
@ -340,11 +348,24 @@ begin
|
||||||
if pass in {@set}[passCmd2, passPP] then
|
if pass in {@set}[passCmd2, passPP] then
|
||||||
addFileToLink(arg);
|
addFileToLink(arg);
|
||||||
end;
|
end;
|
||||||
wDebuginfo: include(gGlobalOptions, optCDebug);
|
wDebuginfo: begin
|
||||||
wCompileOnly, wC: include(gGlobalOptions, optCompileOnly);
|
expectNoArg(switch, arg, pass, info);
|
||||||
wNoLinking: include(gGlobalOptions, optNoLinking);
|
include(gGlobalOptions, optCDebug);
|
||||||
wForceBuild, wF: include(gGlobalOptions, optForceFullMake);
|
end;
|
||||||
|
wCompileOnly, wC: begin
|
||||||
|
expectNoArg(switch, arg, pass, info);
|
||||||
|
include(gGlobalOptions, optCompileOnly);
|
||||||
|
end;
|
||||||
|
wNoLinking: begin
|
||||||
|
expectNoArg(switch, arg, pass, info);
|
||||||
|
include(gGlobalOptions, optNoLinking);
|
||||||
|
end;
|
||||||
|
wForceBuild, wF: begin
|
||||||
|
expectNoArg(switch, arg, pass, info);
|
||||||
|
include(gGlobalOptions, optForceFullMake);
|
||||||
|
end;
|
||||||
wGC: begin
|
wGC: begin
|
||||||
|
expectArg(switch, arg, pass, info);
|
||||||
case whichKeyword(arg) of
|
case whichKeyword(arg) of
|
||||||
wBoehm: begin
|
wBoehm: begin
|
||||||
include(gGlobalOptions, optBoehmGC);
|
include(gGlobalOptions, optBoehmGC);
|
||||||
|
|
@ -393,6 +414,7 @@ begin
|
||||||
wAssertions, wA: ProcessOnOffSwitch({@set}[optAssert], arg, pass, info);
|
wAssertions, wA: ProcessOnOffSwitch({@set}[optAssert], arg, pass, info);
|
||||||
wDeadCodeElim: ProcessOnOffSwitchG({@set}[optDeadCodeElim], arg, pass, info);
|
wDeadCodeElim: ProcessOnOffSwitchG({@set}[optDeadCodeElim], arg, pass, info);
|
||||||
wOpt: begin
|
wOpt: begin
|
||||||
|
expectArg(switch, arg, pass, info);
|
||||||
case whichKeyword(arg) of
|
case whichKeyword(arg) of
|
||||||
wSpeed: begin
|
wSpeed: begin
|
||||||
include(gOptions, optOptimizeSpeed);
|
include(gOptions, optOptimizeSpeed);
|
||||||
|
|
@ -411,6 +433,7 @@ begin
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
wApp: begin
|
wApp: begin
|
||||||
|
expectArg(switch, arg, pass, info);
|
||||||
case whichKeyword(arg) of
|
case whichKeyword(arg) of
|
||||||
wGui: begin
|
wGui: begin
|
||||||
include(gGlobalOptions, optGenGuiApp);
|
include(gGlobalOptions, optGenGuiApp);
|
||||||
|
|
@ -428,6 +451,7 @@ begin
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
wListDef: begin
|
wListDef: begin
|
||||||
|
expectNoArg(switch, arg, pass, info);
|
||||||
if pass in {@set}[passCmd2, passPP] then
|
if pass in {@set}[passCmd2, passPP] then
|
||||||
condsyms.listSymbols();
|
condsyms.listSymbols();
|
||||||
end;
|
end;
|
||||||
|
|
@ -450,9 +474,16 @@ begin
|
||||||
expectArg(switch, arg, pass, info);
|
expectArg(switch, arg, pass, info);
|
||||||
options.addImplicitMod(arg);
|
options.addImplicitMod(arg);
|
||||||
end;
|
end;
|
||||||
wListCmd: include(gGlobalOptions, optListCmd);
|
wListCmd: begin
|
||||||
wGenMapping: include(gGlobalOptions, optGenMapping);
|
expectNoArg(switch, arg, pass, info);
|
||||||
|
include(gGlobalOptions, optListCmd);
|
||||||
|
end;
|
||||||
|
wGenMapping: begin
|
||||||
|
expectNoArg(switch, arg, pass, info);
|
||||||
|
include(gGlobalOptions, optGenMapping);
|
||||||
|
end;
|
||||||
wOS: begin
|
wOS: begin
|
||||||
|
expectArg(switch, arg, pass, info);
|
||||||
if (pass = passCmd1) then begin
|
if (pass = passCmd1) then begin
|
||||||
theOS := platform.NameToOS(arg);
|
theOS := platform.NameToOS(arg);
|
||||||
if theOS = osNone then
|
if theOS = osNone then
|
||||||
|
|
@ -465,6 +496,7 @@ begin
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
wCPU: begin
|
wCPU: begin
|
||||||
|
expectArg(switch, arg, pass, info);
|
||||||
if (pass = passCmd1) then begin
|
if (pass = passCmd1) then begin
|
||||||
cpu := platform.NameToCPU(arg);
|
cpu := platform.NameToCPU(arg);
|
||||||
if cpu = cpuNone then
|
if cpu = cpuNone then
|
||||||
|
|
@ -476,18 +508,39 @@ begin
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
wRun, wR: include(gGlobalOptions, optRun);
|
wRun, wR: begin
|
||||||
|
expectNoArg(switch, arg, pass, info);
|
||||||
|
include(gGlobalOptions, optRun);
|
||||||
|
end;
|
||||||
wVerbosity: begin
|
wVerbosity: begin
|
||||||
expectArg(switch, arg, pass, info);
|
expectArg(switch, arg, pass, info);
|
||||||
gVerbosity := parseInt(arg);
|
gVerbosity := parseInt(arg);
|
||||||
end;
|
end;
|
||||||
wVersion, wV: writeVersionInfo(pass);
|
wVersion, wV: begin
|
||||||
wAdvanced: writeAdvancedUsage(pass);
|
expectNoArg(switch, arg, pass, info);
|
||||||
wHelp, wH: helpOnError(pass);
|
writeVersionInfo(pass);
|
||||||
|
end;
|
||||||
|
wAdvanced: begin
|
||||||
|
expectNoArg(switch, arg, pass, info);
|
||||||
|
writeAdvancedUsage(pass);
|
||||||
|
end;
|
||||||
|
wHelp, wH: begin
|
||||||
|
expectNoArg(switch, arg, pass, info);
|
||||||
|
helpOnError(pass);
|
||||||
|
end;
|
||||||
wSymbolFiles: ProcessOnOffSwitchG({@set}[optSymbolFiles], arg, pass, info);
|
wSymbolFiles: ProcessOnOffSwitchG({@set}[optSymbolFiles], arg, pass, info);
|
||||||
wSkipCfg: include(gGlobalOptions, optSkipConfigFile);
|
wSkipCfg: begin
|
||||||
wSkipProjCfg: include(gGlobalOptions, optSkipProjConfigFile);
|
expectNoArg(switch, arg, pass, info);
|
||||||
wGenScript: include(gGlobalOptions, optGenScript);
|
include(gGlobalOptions, optSkipConfigFile);
|
||||||
|
end;
|
||||||
|
wSkipProjCfg: begin
|
||||||
|
expectNoArg(switch, arg, pass, info);
|
||||||
|
include(gGlobalOptions, optSkipProjConfigFile);
|
||||||
|
end;
|
||||||
|
wGenScript: begin
|
||||||
|
expectNoArg(switch, arg, pass, info);
|
||||||
|
include(gGlobalOptions, optGenScript);
|
||||||
|
end;
|
||||||
wLib: begin
|
wLib: begin
|
||||||
expectArg(switch, arg, pass, info);
|
expectArg(switch, arg, pass, info);
|
||||||
libpath := processPath(arg)
|
libpath := processPath(arg)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ unit condsyms;
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, ast, astalgo, msgs, hashes, platform, strutils, idents;
|
nsystem, ast, astalgo, msgs, nhashes, platform, strutils, idents;
|
||||||
|
|
||||||
var
|
var
|
||||||
gSymbols: TStrTable;
|
gSymbols: TStrTable;
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, charsets, ast, astalgo, strutils, hashes, options, nversion, msgs,
|
nsystem, charsets, ast, astalgo, strutils, nhashes, options, nversion, msgs,
|
||||||
nos, ropes, idents, wordrecg, nmath, pnimsyn, rnimsyn, scanner, rst, ntime,
|
nos, ropes, idents, wordrecg, nmath, pnimsyn, rnimsyn, scanner, rst, ntime,
|
||||||
highlite;
|
highlite;
|
||||||
|
|
||||||
|
|
@ -722,6 +722,16 @@ begin
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function renderContainer(d: PDoc; n: PRstNode): PRope;
|
||||||
|
var
|
||||||
|
arg: PRope;
|
||||||
|
begin
|
||||||
|
result := renderRstToHtml(d, n.sons[2]);
|
||||||
|
arg := toRope(strip(getArgument(n)));
|
||||||
|
if arg = nil then result := ropef('<div>$1</div>', [result])
|
||||||
|
else result := ropef('<div class="$1">$2</div>', [arg, result])
|
||||||
|
end;
|
||||||
|
|
||||||
function renderRstToHtml(d: PDoc; n: PRstNode): PRope;
|
function renderRstToHtml(d: PDoc; n: PRstNode): PRope;
|
||||||
var
|
var
|
||||||
outer, inner: string;
|
outer, inner: string;
|
||||||
|
|
@ -811,6 +821,10 @@ begin
|
||||||
result := renderCodeBlock(d, n);
|
result := renderCodeBlock(d, n);
|
||||||
exit
|
exit
|
||||||
end;
|
end;
|
||||||
|
rnContainer: begin
|
||||||
|
result := renderContainer(d, n);
|
||||||
|
exit
|
||||||
|
end;
|
||||||
rnSubstitutionReferences, rnSubstitutionDef: outer := '|$1|';
|
rnSubstitutionReferences, rnSubstitutionDef: outer := '|$1|';
|
||||||
rnDirective: outer := '';
|
rnDirective: outer := '';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, ast, astalgo, strutils, hashes, trees, platform, magicsys,
|
nsystem, ast, astalgo, strutils, nhashes, trees, platform, magicsys,
|
||||||
extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents,
|
extccomp, options, nversion, nimsets, msgs, crc, bitsets, idents,
|
||||||
lists, types, nos, ntime, ropes, nmath, passes, ccgutils, wordrecg, rnimsyn,
|
lists, types, nos, ntime, ropes, nmath, passes, ccgutils, wordrecg, rnimsyn,
|
||||||
rodread;
|
rodread;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// The Nimrod Compiler
|
// The Nimrod Compiler
|
||||||
// (c) Copyright 2008 Andreas Rumpf
|
// (c) Copyright 2009 Andreas Rumpf
|
||||||
//
|
//
|
||||||
// See the file "copying.txt", included in this
|
// See the file "copying.txt", included in this
|
||||||
// distribution, for details about the copyright.
|
// distribution, for details about the copyright.
|
||||||
|
|
@ -157,7 +157,7 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var
|
var
|
||||||
gWhileCounter: int; // Use a counter to prevend endless loops!
|
gWhileCounter: int; // Use a counter to prevent endless loops!
|
||||||
// We make this counter global, because otherwise
|
// We make this counter global, because otherwise
|
||||||
// nested loops could make the compiler extremely slow.
|
// nested loops could make the compiler extremely slow.
|
||||||
gNestedEvals: int; // count the recursive calls to ``evalAux`` to prevent
|
gNestedEvals: int; // count the recursive calls to ``evalAux`` to prevent
|
||||||
|
|
@ -390,7 +390,7 @@ begin
|
||||||
stackTrace(c, n, errIndexOutOfBounds);
|
stackTrace(c, n, errIndexOutOfBounds);
|
||||||
end;
|
end;
|
||||||
else
|
else
|
||||||
stackTrace(c, n, errIndexNoIntType);
|
stackTrace(c, n, errNilAccess);
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -506,7 +506,8 @@ begin
|
||||||
result := emptyNode
|
result := emptyNode
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
if result = nil then InternalError(n.info, 'evalSym: ' + n.sym.name.s);
|
if result = nil then
|
||||||
|
stackTrace(c, n, errCannotInterpretNodeX, n.sym.name.s);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function evalIncDec(c: PEvalContext; n: PNode; sign: biggestInt): PNode;
|
function evalIncDec(c: PEvalContext; n: PNode; sign: biggestInt): PNode;
|
||||||
|
|
@ -563,8 +564,12 @@ function evalDeref(c: PEvalContext; n: PNode): PNode;
|
||||||
begin
|
begin
|
||||||
result := evalAux(c, n.sons[0]);
|
result := evalAux(c, n.sons[0]);
|
||||||
if result.kind = nkExceptBranch then exit;
|
if result.kind = nkExceptBranch then exit;
|
||||||
if result.kind <> nkRefTy then InternalError(n.info, 'evalDeref');
|
case result.kind of
|
||||||
result := result.sons[0];
|
nkExceptBranch: exit;
|
||||||
|
nkNilLit: stackTrace(c, n, errNilAccess);
|
||||||
|
nkRefTy: result := result.sons[0];
|
||||||
|
else InternalError(n.info, 'evalDeref ' + nodeKindToStr[result.kind]);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function evalAddr(c: PEvalContext; n: PNode): PNode;
|
function evalAddr(c: PEvalContext; n: PNode): PNode;
|
||||||
|
|
@ -574,7 +579,6 @@ var
|
||||||
begin
|
begin
|
||||||
result := evalAux(c, n.sons[0]);
|
result := evalAux(c, n.sons[0]);
|
||||||
if result.kind = nkExceptBranch then exit;
|
if result.kind = nkExceptBranch then exit;
|
||||||
if result.kind <> nkRefTy then InternalError(n.info, 'evalDeref');
|
|
||||||
a := result;
|
a := result;
|
||||||
t := newType(tyPtr, c.module);
|
t := newType(tyPtr, c.module);
|
||||||
addSon(t, a.typ);
|
addSon(t, a.typ);
|
||||||
|
|
@ -732,6 +736,7 @@ end;
|
||||||
function evalSetLengthSeq(c: PEvalContext; n: PNode): PNode;
|
function evalSetLengthSeq(c: PEvalContext; n: PNode): PNode;
|
||||||
var
|
var
|
||||||
a, b: PNode;
|
a, b: PNode;
|
||||||
|
newLen, oldLen, i: int;
|
||||||
begin
|
begin
|
||||||
result := evalAux(c, n.sons[1]);
|
result := evalAux(c, n.sons[1]);
|
||||||
if result.kind = nkExceptBranch then exit;
|
if result.kind = nkExceptBranch then exit;
|
||||||
|
|
@ -739,8 +744,12 @@ begin
|
||||||
result := evalAux(c, n.sons[2]);
|
result := evalAux(c, n.sons[2]);
|
||||||
if result.kind = nkExceptBranch then exit;
|
if result.kind = nkExceptBranch then exit;
|
||||||
b := result;
|
b := result;
|
||||||
if a.kind = nkBracket then setLength(a.sons, int(getOrdValue(b)))
|
if a.kind <> nkBracket then InternalError(n.info, 'evalSetLengthSeq');
|
||||||
else InternalError(n.info, 'evalSetLengthSeq');
|
newLen := int(getOrdValue(b));
|
||||||
|
oldLen := sonsLen(a);
|
||||||
|
setLength(a.sons, newLen);
|
||||||
|
for i := oldLen to newLen-1 do
|
||||||
|
a.sons[i] := getNullValue(skipVarGeneric(n.sons[1].typ), n.info);
|
||||||
result := emptyNode
|
result := emptyNode
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -758,10 +767,13 @@ begin
|
||||||
b := result;
|
b := result;
|
||||||
|
|
||||||
t := skipVarGeneric(n.sons[1].typ);
|
t := skipVarGeneric(n.sons[1].typ);
|
||||||
result := newNodeIT(nkBracket, n.info, t);
|
if a.kind = nkEmpty then InternalError(n.info, 'first parameter is empty');
|
||||||
|
a.kind := nkBracket;
|
||||||
|
a.info := n.info;
|
||||||
|
a.typ := t;
|
||||||
for i := 0 to int(getOrdValue(b))-1 do
|
for i := 0 to int(getOrdValue(b))-1 do
|
||||||
addSon(result, getNullValue(t.sons[0], n.info));
|
addSon(a, getNullValue(t.sons[0], n.info));
|
||||||
// XXX: assign to `a`? result := emptyNode
|
result := emptyNode
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function evalAssert(c: PEvalContext; n: PNode): PNode;
|
function evalAssert(c: PEvalContext; n: PNode): PNode;
|
||||||
|
|
@ -907,7 +919,7 @@ end;
|
||||||
function evalMagicOrCall(c: PEvalContext; n: PNode): PNode;
|
function evalMagicOrCall(c: PEvalContext; n: PNode): PNode;
|
||||||
var
|
var
|
||||||
m: TMagic;
|
m: TMagic;
|
||||||
a, b: PNode;
|
a, b, cc: PNode;
|
||||||
k: biggestInt;
|
k: biggestInt;
|
||||||
i: int;
|
i: int;
|
||||||
begin
|
begin
|
||||||
|
|
@ -1179,22 +1191,35 @@ begin
|
||||||
mNError: begin
|
mNError: begin
|
||||||
result := evalAux(c, n.sons[1]);
|
result := evalAux(c, n.sons[1]);
|
||||||
if result.kind = nkExceptBranch then exit;
|
if result.kind = nkExceptBranch then exit;
|
||||||
liMessage(n.info, errUser, getStrValue(result));
|
stackTrace(c, n, errUser, getStrValue(result));
|
||||||
result := emptyNode
|
result := emptyNode
|
||||||
end;
|
end;
|
||||||
mConStrStr: result := evalConStrStr(c, n);
|
mConStrStr: result := evalConStrStr(c, n);
|
||||||
mRepr: result := evalRepr(c, n);
|
mRepr: result := evalRepr(c, n);
|
||||||
|
mNewString: begin
|
||||||
|
result := evalAux(c, n.sons[1]);
|
||||||
|
if result.kind = nkExceptBranch then exit;
|
||||||
|
a := result;
|
||||||
|
result := newNodeIT(nkStrLit, n.info, n.typ);
|
||||||
|
result.strVal := newString(int(getOrdValue(a)));
|
||||||
|
end;
|
||||||
else begin
|
else begin
|
||||||
result := evalAux(c, n.sons[1]);
|
result := evalAux(c, n.sons[1]);
|
||||||
if result.kind = nkExceptBranch then exit;
|
if result.kind = nkExceptBranch then exit;
|
||||||
a := result;
|
a := result;
|
||||||
|
b := nil;
|
||||||
|
cc := nil;
|
||||||
if sonsLen(n) > 2 then begin
|
if sonsLen(n) > 2 then begin
|
||||||
result := evalAux(c, n.sons[2]);
|
result := evalAux(c, n.sons[2]);
|
||||||
if result.kind = nkExceptBranch then exit;
|
if result.kind = nkExceptBranch then exit;
|
||||||
end
|
b := result;
|
||||||
else
|
if sonsLen(n) > 3 then begin
|
||||||
result := nil;
|
result := evalAux(c, n.sons[3]);
|
||||||
result := evalOp(m, n, a, result);
|
if result.kind = nkExceptBranch then exit;
|
||||||
|
cc := result;
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
result := evalOp(m, n, a, b, cc);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
|
|
@ -1213,7 +1238,7 @@ begin
|
||||||
nkNilLit: result := n; // end of atoms
|
nkNilLit: result := n; // end of atoms
|
||||||
|
|
||||||
nkCall, nkHiddenCallConv, nkMacroStmt: result := evalMagicOrCall(c, n);
|
nkCall, nkHiddenCallConv, nkMacroStmt: result := evalMagicOrCall(c, n);
|
||||||
nkCurly, nkBracket: begin
|
nkCurly, nkBracket, nkRange: begin
|
||||||
result := copyNode(n);
|
result := copyNode(n);
|
||||||
for i := 0 to sonsLen(n)-1 do addSon(result, evalAux(c, n.sons[i]));
|
for i := 0 to sonsLen(n)-1 do addSon(result, evalAux(c, n.sons[i]));
|
||||||
end;
|
end;
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, charsets, lists, ropes, nos, strutils, platform, condsyms, options,
|
nsystem, charsets, lists, ropes, nos, strutils, osproc, platform, condsyms,
|
||||||
msgs;
|
options, msgs;
|
||||||
|
|
||||||
// some things are read in from the configuration file
|
// some things are read in from the configuration file
|
||||||
|
|
||||||
|
|
@ -387,7 +387,7 @@ procedure execExternalProgram(const cmd: string);
|
||||||
begin
|
begin
|
||||||
if (optListCmd in gGlobalOptions) or (gVerbosity > 0) then
|
if (optListCmd in gGlobalOptions) or (gVerbosity > 0) then
|
||||||
MessageOut('Executing: ' +{&} nl +{&} cmd);
|
MessageOut('Executing: ' +{&} nl +{&} cmd);
|
||||||
if executeShellCommand(cmd) <> 0 then
|
if executeCommand(cmd) <> 0 then
|
||||||
rawMessage(errExecutionOfProgramFailed);
|
rawMessage(errExecutionOfProgramFailed);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ program hashtest;
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
hashes;
|
nhashes;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
writeln(output, getNormalizedHash(ParamStr(1)));
|
writeln(output, getNormalizedHash(ParamStr(1)));
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
charsets, nsystem, sysutils, hashes, options, msgs, strutils, platform,
|
charsets, nsystem, sysutils, nhashes, options, msgs, strutils, platform,
|
||||||
idents, lexbase, wordrecg, scanner;
|
idents, lexbase, wordrecg, scanner;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ unit idents;
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
hashes, nsystem, strutils;
|
nhashes, nsystem, strutils;
|
||||||
|
|
||||||
type
|
type
|
||||||
TIdObj = object(NObject)
|
TIdObj = object(NObject)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, llstream, strutils, charsets, ropes, strtabs, msgs;
|
nsystem, llstream, strutils, charsets, ropes, nstrtabs, msgs;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem,
|
nsystem,
|
||||||
ast, astalgo, hashes, msgs, platform, nversion, ntime, idents, rodread;
|
ast, astalgo, nhashes, msgs, platform, nversion, ntime, idents, rodread;
|
||||||
|
|
||||||
var // magic symbols in the system module:
|
var // magic symbols in the system module:
|
||||||
SystemModule: PSym;
|
SystemModule: PSym;
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ begin
|
||||||
semanticPasses();
|
semanticPasses();
|
||||||
registerPass(cgen.cgenPass());
|
registerPass(cgen.cgenPass());
|
||||||
registerPass(rodwrite.rodwritePass());
|
registerPass(rodwrite.rodwritePass());
|
||||||
registerPass(cleanupPass());
|
//registerPass(cleanupPass());
|
||||||
compileProject(filename);
|
compileProject(filename);
|
||||||
//for i := low(TTypeKind) to high(TTypeKind) do
|
//for i := low(TTypeKind) to high(TTypeKind) do
|
||||||
// MessageOut('kind: ' +{&} typeKindToStr[i] +{&} ' = ' +{&} toString(sameTypeA[i]));
|
// MessageOut('kind: ' +{&} typeKindToStr[i] +{&} ' = ' +{&} toString(sameTypeA[i]));
|
||||||
|
|
|
||||||
10
nim/msgs.pas
10
nim/msgs.pas
|
|
@ -96,10 +96,10 @@ type
|
||||||
errAttemptToRedefine,
|
errAttemptToRedefine,
|
||||||
errStmtInvalidAfterReturn,
|
errStmtInvalidAfterReturn,
|
||||||
errStmtExpected,
|
errStmtExpected,
|
||||||
errYieldOnlyInInterators,
|
|
||||||
errInvalidLabel,
|
errInvalidLabel,
|
||||||
errInvalidCmdLineOption,
|
errInvalidCmdLineOption,
|
||||||
errCmdLineArgExpected,
|
errCmdLineArgExpected,
|
||||||
|
errCmdLineNoArgExpected,
|
||||||
errInvalidVarSubstitution,
|
errInvalidVarSubstitution,
|
||||||
errUnknownVar,
|
errUnknownVar,
|
||||||
errUnknownCcompiler,
|
errUnknownCcompiler,
|
||||||
|
|
@ -138,7 +138,7 @@ type
|
||||||
errStaticAssertCannotBeEval,
|
errStaticAssertCannotBeEval,
|
||||||
errDotRequiresRecordOrObjectType,
|
errDotRequiresRecordOrObjectType,
|
||||||
errUndeclaredFieldX,
|
errUndeclaredFieldX,
|
||||||
errIndexNoIntType,
|
errNilAccess,
|
||||||
errIndexOutOfBounds,
|
errIndexOutOfBounds,
|
||||||
errIndexTypesDoNotMatch,
|
errIndexTypesDoNotMatch,
|
||||||
errBracketsInvalidForType,
|
errBracketsInvalidForType,
|
||||||
|
|
@ -348,17 +348,17 @@ const
|
||||||
'exception expected',
|
'exception expected',
|
||||||
'exception already handled',
|
'exception already handled',
|
||||||
'''return'' only allowed in routine',
|
'''return'' only allowed in routine',
|
||||||
'''yield'' only allowed in iterator',
|
'''yield'' only allowed in a loop of an iterator',
|
||||||
'invalid number of ''yield'' expresions',
|
'invalid number of ''yield'' expresions',
|
||||||
'''return'' not allowed in iterator',
|
'''return'' not allowed in iterator',
|
||||||
'current routine cannot return an expression',
|
'current routine cannot return an expression',
|
||||||
'attempt to redefine ''$1''',
|
'attempt to redefine ''$1''',
|
||||||
'statement not allowed after ''return'', ''break'' or ''raise''',
|
'statement not allowed after ''return'', ''break'' or ''raise''',
|
||||||
'statement expected',
|
'statement expected',
|
||||||
'''yield'' statement is only allowed in iterators',
|
|
||||||
'''$1'' is no label',
|
'''$1'' is no label',
|
||||||
'invalid command line option: ''$1''',
|
'invalid command line option: ''$1''',
|
||||||
'argument for command line option expected: ''$1''',
|
'argument for command line option expected: ''$1''',
|
||||||
|
'invalid argument for command line option: ''$1''',
|
||||||
'invalid variable substitution in ''$1''',
|
'invalid variable substitution in ''$1''',
|
||||||
'unknown variable: ''$1''',
|
'unknown variable: ''$1''',
|
||||||
'unknown C compiler: ''$1''',
|
'unknown C compiler: ''$1''',
|
||||||
|
|
@ -397,7 +397,7 @@ const
|
||||||
'argument to ''staticAssert'' cannot be evaluated at compile time',
|
'argument to ''staticAssert'' cannot be evaluated at compile time',
|
||||||
'''.'' requires a record or object type',
|
'''.'' requires a record or object type',
|
||||||
'undeclared field: ''$1''',
|
'undeclared field: ''$1''',
|
||||||
'index has to be an integer type',
|
'attempt to access a nil address',
|
||||||
'index out of bounds',
|
'index out of bounds',
|
||||||
'index types do not match',
|
'index types do not match',
|
||||||
'''[]'' operator invalid for this type',
|
'''[]'' operator invalid for this type',
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
// See the file "copying.txt", included in this
|
// See the file "copying.txt", included in this
|
||||||
// distribution, for details about the copyright.
|
// distribution, for details about the copyright.
|
||||||
//
|
//
|
||||||
unit hashes;
|
unit nhashes;
|
||||||
|
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
// See the file "copying.txt", included in this
|
// See the file "copying.txt", included in this
|
||||||
// distribution, for details about the copyright.
|
// distribution, for details about the copyright.
|
||||||
//
|
//
|
||||||
unit strtabs;
|
unit nstrtabs;
|
||||||
|
|
||||||
// String tables.
|
// String tables.
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, nos, hashes, strutils;
|
nsystem, nos, nhashes, strutils;
|
||||||
|
|
||||||
type
|
type
|
||||||
TStringTableMode = (
|
TStringTableMode = (
|
||||||
|
|
@ -93,9 +93,9 @@ end;
|
||||||
function myhash(t: PStringTable; const key: string): THash;
|
function myhash(t: PStringTable; const key: string): THash;
|
||||||
begin
|
begin
|
||||||
case t.mode of
|
case t.mode of
|
||||||
modeCaseSensitive: result := hashes.GetHashStr(key);
|
modeCaseSensitive: result := nhashes.GetHashStr(key);
|
||||||
modeCaseInsensitive: result := hashes.GetHashStrCI(key);
|
modeCaseInsensitive: result := nhashes.GetHashStrCI(key);
|
||||||
modeStyleInsensitive: result := hashes.getNormalizedHash(key);
|
modeStyleInsensitive: result := nhashes.getNormalizedHash(key);
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -31,10 +31,10 @@ const
|
||||||
//cog.outl('VersionMinor = %s;' % ver[1])
|
//cog.outl('VersionMinor = %s;' % ver[1])
|
||||||
//cog.outl('VersionPatch = %s;' % ver[2])
|
//cog.outl('VersionPatch = %s;' % ver[2])
|
||||||
//]]]
|
//]]]
|
||||||
VersionAsString = '0.7.6';
|
VersionAsString = '0.7.8';
|
||||||
VersionMajor = 0;
|
VersionMajor = 0;
|
||||||
VersionMinor = 7;
|
VersionMinor = 7;
|
||||||
VersionPatch = 6;
|
VersionPatch = 8;
|
||||||
//[[[[end]]]]
|
//[[[[end]]]]
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, nos, lists, strutils, strtabs;
|
nsystem, nos, lists, strutils, nstrtabs;
|
||||||
|
|
||||||
type
|
type
|
||||||
// please make sure we have under 32 options
|
// please make sure we have under 32 options
|
||||||
|
|
@ -150,12 +150,12 @@ end;
|
||||||
|
|
||||||
function getConfigVar(const key: string): string;
|
function getConfigVar(const key: string): string;
|
||||||
begin
|
begin
|
||||||
result := strtabs.get(gConfigVars, key);
|
result := nstrtabs.get(gConfigVars, key);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure setConfigVar(const key, val: string);
|
procedure setConfigVar(const key, val: string);
|
||||||
begin
|
begin
|
||||||
strtabs.put(gConfigVars, key, val);
|
nstrtabs.put(gConfigVars, key, val);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function getOutFile(const filename, ext: string): string;
|
function getOutFile(const filename, ext: string): string;
|
||||||
|
|
|
||||||
30
nim/osproc.pas
Normal file
30
nim/osproc.pas
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// The Nimrod Compiler
|
||||||
|
// (c) Copyright 2009 Andreas Rumpf
|
||||||
|
//
|
||||||
|
// See the file "copying.txt", included in this
|
||||||
|
// distribution, for details about the copyright.
|
||||||
|
//
|
||||||
|
unit osproc;
|
||||||
|
|
||||||
|
// This module provides Nimrod's osproc module in Pascal
|
||||||
|
// Note: Only implement what is really needed here!
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
{$include 'config.inc'}
|
||||||
|
|
||||||
|
uses
|
||||||
|
nsystem, nos;
|
||||||
|
|
||||||
|
function executeCommand(const cmd: string): int;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
function executeCommand(const cmd: string): int;
|
||||||
|
begin
|
||||||
|
result := executeShellCommand(cmd);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
|
|
@ -17,7 +17,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, charsets, llstream, sysutils, hashes, strutils, lexbase;
|
nsystem, charsets, llstream, sysutils, nhashes, strutils, lexbase;
|
||||||
|
|
||||||
type
|
type
|
||||||
TCfgEventKind = (
|
TCfgEventKind = (
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
charsets, nsystem, sysutils,
|
charsets, nsystem, sysutils,
|
||||||
hashes, options, msgs, strutils, platform, idents,
|
nhashes, options, msgs, strutils, platform, idents,
|
||||||
lexbase, wordrecg, scanner;
|
lexbase, wordrecg, scanner;
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// The Nimrod Compiler
|
// The Nimrod Compiler
|
||||||
// (c) Copyright 2008 Andreas Rumpf
|
// (c) Copyright 2009 Andreas Rumpf
|
||||||
//
|
//
|
||||||
// See the file "copying.txt", included in this
|
// See the file "copying.txt", included in this
|
||||||
// distribution, for details about the copyright.
|
// distribution, for details about the copyright.
|
||||||
|
|
@ -143,7 +143,7 @@ begin
|
||||||
else v := expectStrLit(c, n);
|
else v := expectStrLit(c, n);
|
||||||
Include(s.flags, sfImportc); // magics don't need an implementation, so we
|
Include(s.flags, sfImportc); // magics don't need an implementation, so we
|
||||||
// treat them as imported, instead of modifing a lot of working code
|
// treat them as imported, instead of modifing a lot of working code
|
||||||
Include(s.loc.Flags, lfNoDecl); // magics don't need to be declared!
|
// BUGFIX: magic does not imply ``lfNoDecl`` anymore!
|
||||||
for m := low(TMagic) to high(TMagic) do
|
for m := low(TMagic) to high(TMagic) do
|
||||||
if magicToStr[m] = v then begin
|
if magicToStr[m] = v then begin
|
||||||
s.magic := m; exit
|
s.magic := m; exit
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, msgs, strutils, platform, hashes, crc;
|
nsystem, msgs, strutils, platform, nhashes, crc;
|
||||||
|
|
||||||
const
|
const
|
||||||
CacheLeafs = true;
|
CacheLeafs = true;
|
||||||
|
|
|
||||||
24
nim/rst.pas
24
nim/rst.pas
|
|
@ -16,7 +16,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, nos, msgs, strutils, platform, hashes, ropes, charsets, options;
|
nsystem, nos, msgs, strutils, platform, nhashes, ropes, charsets, options;
|
||||||
|
|
||||||
type
|
type
|
||||||
TRstNodeKind = (
|
TRstNodeKind = (
|
||||||
|
|
@ -79,6 +79,7 @@ type
|
||||||
rnImage,
|
rnImage,
|
||||||
rnFigure,
|
rnFigure,
|
||||||
rnCodeBlock,
|
rnCodeBlock,
|
||||||
|
rnContainer, // ``container`` directive
|
||||||
rnIndex, // index directve:
|
rnIndex, // index directve:
|
||||||
// .. index::
|
// .. index::
|
||||||
// key
|
// key
|
||||||
|
|
@ -111,9 +112,9 @@ const
|
||||||
'TableRow', 'TableHeaderCell', 'TableDataCell', 'Label', 'Footnote',
|
'TableRow', 'TableHeaderCell', 'TableDataCell', 'Label', 'Footnote',
|
||||||
'Citation', 'StandaloneHyperlink', 'Hyperlink', 'Ref', 'Directive',
|
'Citation', 'StandaloneHyperlink', 'Hyperlink', 'Ref', 'Directive',
|
||||||
'DirArg', 'Raw', 'Title', 'Contents', 'Image', 'Figure', 'CodeBlock',
|
'DirArg', 'Raw', 'Title', 'Contents', 'Image', 'Figure', 'CodeBlock',
|
||||||
'Index', 'SubstitutionDef', 'GeneralRole', 'Sub', 'Sup', 'Idx',
|
'Container', 'Index', 'SubstitutionDef', 'GeneralRole',
|
||||||
'Emphasis', 'StrongEmphasis', 'InterpretedText', 'InlineLiteral',
|
'Sub', 'Sup', 'Idx', 'Emphasis', 'StrongEmphasis', 'InterpretedText',
|
||||||
'SubstitutionReferences', 'Leaf'
|
'InlineLiteral', 'SubstitutionReferences', 'Leaf'
|
||||||
);
|
);
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -1178,12 +1179,12 @@ end;
|
||||||
|
|
||||||
type
|
type
|
||||||
TDirKind = ( // must be ordered alphabetically!
|
TDirKind = ( // must be ordered alphabetically!
|
||||||
dkNone, dkAuthor, dkAuthors, dkCodeBlock,
|
dkNone, dkAuthor, dkAuthors, dkCodeBlock, dkContainer,
|
||||||
dkContents, dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
|
dkContents, dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
|
||||||
);
|
);
|
||||||
const
|
const
|
||||||
DirIds: array [0..10] of string = (
|
DirIds: array [0..11] of string = (
|
||||||
'', 'author', 'authors', 'code-block',
|
'', 'author', 'authors', 'code-block', 'container',
|
||||||
'contents', 'figure', 'image', 'include', 'index', 'raw', 'title'
|
'contents', 'figure', 'image', 'include', 'index', 'raw', 'title'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -1993,6 +1994,14 @@ begin
|
||||||
result.kind := rnCodeBlock
|
result.kind := rnCodeBlock
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function dirContainer(var p: TRstParser): PRstNode;
|
||||||
|
begin
|
||||||
|
result := parseDirective(p, {@set}[hasArg], parseSectionWrapper);
|
||||||
|
assert(result.kind = rnDirective);
|
||||||
|
assert(rsonsLen(result) = 3);
|
||||||
|
result.kind := rnContainer;
|
||||||
|
end;
|
||||||
|
|
||||||
function dirImage(var p: TRstParser): PRstNode;
|
function dirImage(var p: TRstParser): PRstNode;
|
||||||
begin
|
begin
|
||||||
result := parseDirective(p, {@set}[hasOptions, hasArg, argIsFile], nil);
|
result := parseDirective(p, {@set}[hasOptions, hasArg, argIsFile], nil);
|
||||||
|
|
@ -2071,6 +2080,7 @@ begin
|
||||||
dkImage: result := dirImage(p);
|
dkImage: result := dirImage(p);
|
||||||
dkFigure: result := dirFigure(p);
|
dkFigure: result := dirFigure(p);
|
||||||
dkTitle: result := dirTitle(p);
|
dkTitle: result := dirTitle(p);
|
||||||
|
dkContainer: result := dirContainer(p);
|
||||||
dkContents: result := dirContents(p);
|
dkContents: result := dirContents(p);
|
||||||
dkRaw: result := dirRaw(p);
|
dkRaw: result := dirRaw(p);
|
||||||
dkCodeblock: result := dirCodeBlock(p);
|
dkCodeblock: result := dirCodeBlock(p);
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
charsets, nsystem, sysutils, hashes, options, msgs, strutils, platform,
|
charsets, nsystem, sysutils, nhashes, options, msgs, strutils, platform,
|
||||||
idents, lexbase, llstream, wordrecg;
|
idents, lexbase, llstream, wordrecg;
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
|
||||||
43
nim/sem.pas
43
nim/sem.pas
|
|
@ -112,6 +112,23 @@ begin
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function semAfterMacroCall(c: PContext; n, p: PNode): PNode;
|
||||||
|
begin
|
||||||
|
result := n;
|
||||||
|
if (p = nil) or (p.kind <> nkIdent) then
|
||||||
|
liMessage(n.info, errInvalidParamKindX, renderTree(p))
|
||||||
|
else begin
|
||||||
|
case p.ident.id of
|
||||||
|
ord(wExpr): result := semExprWithType(c, result);
|
||||||
|
ord(wStmt): result := semStmt(c, result);
|
||||||
|
ord(wTypeDesc):
|
||||||
|
result.typ := semTypeNode(c, result, nil);
|
||||||
|
else
|
||||||
|
liMessage(p.info, errInvalidParamKindX, p.ident.s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
|
||||||
function semMacroExpr(c: PContext; n: PNode; sym: PSym): PNode;
|
function semMacroExpr(c: PContext; n: PNode; sym: PSym): PNode;
|
||||||
var
|
var
|
||||||
p: PEvalContext;
|
p: PEvalContext;
|
||||||
|
|
@ -128,7 +145,7 @@ begin
|
||||||
result := s.params[0];
|
result := s.params[0];
|
||||||
popStackFrame(p);
|
popStackFrame(p);
|
||||||
if cyclicTree(result) then liMessage(n.info, errCyclicTree);
|
if cyclicTree(result) then liMessage(n.info, errCyclicTree);
|
||||||
result := semStmt(c, result);
|
result := semAfterMacroCall(c, result, sym.ast.sons[paramsPos].sons[0]);
|
||||||
// now, that was easy ...
|
// now, that was easy ...
|
||||||
// and we get more flexibility than in any other programming language
|
// and we get more flexibility than in any other programming language
|
||||||
end;
|
end;
|
||||||
|
|
@ -137,14 +154,12 @@ end;
|
||||||
{$include 'seminst.pas'}
|
{$include 'seminst.pas'}
|
||||||
{$include 'sigmatch.pas'}
|
{$include 'sigmatch.pas'}
|
||||||
|
|
||||||
|
|
||||||
procedure CheckBool(t: PNode);
|
procedure CheckBool(t: PNode);
|
||||||
begin
|
begin
|
||||||
if (t.Typ = nil) or (skipVarGeneric(t.Typ).kind <> tyBool) then
|
if (t.Typ = nil) or (skipVarGeneric(t.Typ).kind <> tyBool) then
|
||||||
liMessage(t.Info, errExprMustBeBool);
|
liMessage(t.Info, errExprMustBeBool);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure typeMismatch(n: PNode; formal, actual: PType);
|
procedure typeMismatch(n: PNode; formal, actual: PType);
|
||||||
begin
|
begin
|
||||||
liMessage(n.Info, errGenerated,
|
liMessage(n.Info, errGenerated,
|
||||||
|
|
@ -167,13 +182,17 @@ var
|
||||||
prc: PSym;
|
prc: PSym;
|
||||||
it: PNode;
|
it: PNode;
|
||||||
begin
|
begin
|
||||||
for i := 0 to sonsLen(c.generics)-1 do begin
|
for i := c.lastGenericIdx to sonsLen(c.generics)-1 do begin
|
||||||
it := c.generics.sons[i].sons[1];
|
it := c.generics.sons[i].sons[1];
|
||||||
if it.kind <> nkSym then InternalError('addCodeForGenerics');
|
if it.kind <> nkSym then InternalError('addCodeForGenerics');
|
||||||
prc := it.sym;
|
prc := it.sym;
|
||||||
if (prc.kind in [skProc, skConverter]) and (prc.magic = mNone) then
|
if (prc.kind in [skProc, skConverter]) and (prc.magic = mNone) then begin
|
||||||
|
if (prc.ast = nil) or (prc.ast.sons[codePos] = nil) then
|
||||||
|
InternalError(prc.info, 'no code for ' + prc.name.s);
|
||||||
addSon(n, prc.ast);
|
addSon(n, prc.ast);
|
||||||
|
end
|
||||||
end;
|
end;
|
||||||
|
c.lastGenericIdx := sonsLen(c.generics);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function myOpen(module: PSym; const filename: string): PPassContext;
|
function myOpen(module: PSym; const filename: string): PPassContext;
|
||||||
|
|
@ -212,10 +231,21 @@ end;
|
||||||
function myProcess(context: PPassContext; n: PNode): PNode;
|
function myProcess(context: PPassContext; n: PNode): PNode;
|
||||||
var
|
var
|
||||||
c: PContext;
|
c: PContext;
|
||||||
|
a: PNode;
|
||||||
begin
|
begin
|
||||||
result := nil;
|
result := nil;
|
||||||
c := PContext(context);
|
c := PContext(context);
|
||||||
result := semStmt(c, n);
|
result := semStmt(c, n);
|
||||||
|
// BUGFIX: process newly generated generics here, not at the end!
|
||||||
|
if sonsLen(c.generics) > 0 then begin
|
||||||
|
a := newNodeI(nkStmtList, n.info);
|
||||||
|
addCodeForGenerics(c, a);
|
||||||
|
if sonsLen(a) > 0 then begin
|
||||||
|
// a generic has been added to `a`:
|
||||||
|
addSonIfNotNil(a, result);
|
||||||
|
result := a
|
||||||
|
end
|
||||||
|
end
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function myClose(context: PPassContext; n: PNode): PNode;
|
function myClose(context: PPassContext; n: PNode): PNode;
|
||||||
|
|
@ -226,7 +256,8 @@ begin
|
||||||
closeScope(c.tab); // close module's scope
|
closeScope(c.tab); // close module's scope
|
||||||
rawCloseScope(c.tab); // imported symbols; don't check for unused ones!
|
rawCloseScope(c.tab); // imported symbols; don't check for unused ones!
|
||||||
if n = nil then result := newNode(nkStmtList)
|
if n = nil then result := newNode(nkStmtList)
|
||||||
else result := n;
|
else InternalError(n.info, 'n is not nil');
|
||||||
|
//result := n;
|
||||||
addCodeForGenerics(c, result);
|
addCodeForGenerics(c, result);
|
||||||
popOwner();
|
popOwner();
|
||||||
c.p := nil;
|
c.p := nil;
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ type
|
||||||
libs: TLinkedList; // all libs used by this module
|
libs: TLinkedList; // all libs used by this module
|
||||||
p: PProcCon; // procedure context
|
p: PProcCon; // procedure context
|
||||||
fromCache: bool; // is the module read from a cache?
|
fromCache: bool; // is the module read from a cache?
|
||||||
|
lastGenericIdx: int; // used for the generics stack
|
||||||
semConstExpr: function (c: PContext; n: PNode): PNode;
|
semConstExpr: function (c: PContext; n: PNode): PNode;
|
||||||
// for the pragmas module
|
// for the pragmas module
|
||||||
end;
|
end;
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ function getConstExpr(module: PSym; n: PNode): PNode;
|
||||||
// evaluates the constant expression or returns nil if it is no constant
|
// evaluates the constant expression or returns nil if it is no constant
|
||||||
// expression
|
// expression
|
||||||
|
|
||||||
function evalOp(m: TMagic; n, a, b: PNode): PNode;
|
function evalOp(m: TMagic; n, a, b, c: PNode): PNode;
|
||||||
function leValueConv(a, b: PNode): Boolean;
|
function leValueConv(a, b: PNode): Boolean;
|
||||||
|
|
||||||
function newIntNodeT(const intVal: BiggestInt; n: PNode): PNode;
|
function newIntNodeT(const intVal: BiggestInt; n: PNode): PNode;
|
||||||
|
|
@ -113,8 +113,8 @@ begin
|
||||||
InternalError(a.info, 'no symbol for ordinal value: ' + toString(x));
|
InternalError(a.info, 'no symbol for ordinal value: ' + toString(x));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function evalOp(m: TMagic; n, a, b: PNode): PNode;
|
function evalOp(m: TMagic; n, a, b, c: PNode): PNode;
|
||||||
// if this is an unary operation, b is nil
|
// b and c may be nil
|
||||||
begin
|
begin
|
||||||
result := nil;
|
result := nil;
|
||||||
case m of
|
case m of
|
||||||
|
|
@ -127,8 +127,9 @@ begin
|
||||||
mBitnotI, mBitnotI64: result := newIntNodeT(not getInt(a), n);
|
mBitnotI, mBitnotI64: result := newIntNodeT(not getInt(a), n);
|
||||||
|
|
||||||
mLengthStr: result := newIntNodeT(length(getStr(a)), n);
|
mLengthStr: result := newIntNodeT(length(getStr(a)), n);
|
||||||
mLengthSeq, mLengthArray,
|
mLengthArray: result := newIntNodeT(lengthOrd(a.typ), n);
|
||||||
mLengthOpenArray: result := newIntNodeT(lengthOrd(a.typ), n);
|
mLengthSeq, mLengthOpenArray:
|
||||||
|
result := newIntNodeT(sonsLen(a), n); // BUGFIX
|
||||||
|
|
||||||
mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result := a; // throw `+` away
|
mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: result := a; // throw `+` away
|
||||||
mToFloat, mToBiggestFloat:
|
mToFloat, mToBiggestFloat:
|
||||||
|
|
@ -273,6 +274,11 @@ begin
|
||||||
else
|
else
|
||||||
result := newStrNodeT('true', n)
|
result := newStrNodeT('true', n)
|
||||||
end;
|
end;
|
||||||
|
mCopyStr:
|
||||||
|
result := newStrNodeT(ncopy(getStr(a), int(getOrdValue(b))+strStart), n);
|
||||||
|
mCopyStrLast:
|
||||||
|
result := newStrNodeT(ncopy(getStr(a), int(getOrdValue(b))+strStart,
|
||||||
|
int(getOrdValue(c))+strStart), n);
|
||||||
mFloatToStr: result := newStrNodeT(toStringF(getFloat(a)), n);
|
mFloatToStr: result := newStrNodeT(toStringF(getFloat(a)), n);
|
||||||
mCStrToStr, mCharToStr: result := newStrNodeT(getStrOrChar(a), n);
|
mCStrToStr, mCharToStr: result := newStrNodeT(getStrOrChar(a), n);
|
||||||
mStrToStr: result := a;
|
mStrToStr: result := a;
|
||||||
|
|
@ -281,7 +287,7 @@ begin
|
||||||
result := copyTree(a);
|
result := copyTree(a);
|
||||||
result.typ := n.typ;
|
result.typ := n.typ;
|
||||||
end;
|
end;
|
||||||
mExit, mInc, ast.mDec, mAssert, mSwap,
|
mNewString, mExit, mInc, ast.mDec, mAssert, mSwap,
|
||||||
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq,
|
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq,
|
||||||
mSetLengthStr, mSetLengthSeq, mNLen..mNError: begin end;
|
mSetLengthStr, mSetLengthSeq, mNLen..mNError: begin end;
|
||||||
else InternalError(a.info, 'evalOp(' +{&} magicToStr[m] +{&} ')');
|
else InternalError(a.info, 'evalOp(' +{&} magicToStr[m] +{&} ')');
|
||||||
|
|
@ -376,7 +382,7 @@ end;
|
||||||
function getConstExpr(module: PSym; n: PNode): PNode;
|
function getConstExpr(module: PSym; n: PNode): PNode;
|
||||||
var
|
var
|
||||||
s: PSym;
|
s: PSym;
|
||||||
a, b: PNode;
|
a, b, c: PNode;
|
||||||
i: int;
|
i: int;
|
||||||
begin
|
begin
|
||||||
result := nil;
|
result := nil;
|
||||||
|
|
@ -443,10 +449,14 @@ begin
|
||||||
if a = nil then exit;
|
if a = nil then exit;
|
||||||
if sonsLen(n) > 2 then begin
|
if sonsLen(n) > 2 then begin
|
||||||
b := getConstExpr(module, n.sons[2]);
|
b := getConstExpr(module, n.sons[2]);
|
||||||
if b = nil then exit
|
if b = nil then exit;
|
||||||
|
if sonsLen(n) > 3 then begin
|
||||||
|
c := getConstExpr(module, n.sons[3]);
|
||||||
|
if c = nil then exit;
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else b := nil;
|
else b := nil;
|
||||||
result := evalOp(s.magic, n, a, b);
|
result := evalOp(s.magic, n, a, b, c);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
except
|
except
|
||||||
|
|
|
||||||
|
|
@ -354,7 +354,8 @@ var
|
||||||
begin
|
begin
|
||||||
result := n;
|
result := n;
|
||||||
checkSonsLen(n, 1);
|
checkSonsLen(n, 1);
|
||||||
if (c.p.owner = nil) or (c.p.owner.kind <> skIterator) then
|
if (c.p.owner = nil) or (c.p.owner.kind <> skIterator)
|
||||||
|
or (c.p.nestedLoopCounter <= 0) then
|
||||||
liMessage(n.info, errYieldNotAllowedHere);
|
liMessage(n.info, errYieldNotAllowedHere);
|
||||||
if (n.sons[0] <> nil) then begin
|
if (n.sons[0] <> nil) then begin
|
||||||
n.sons[0] := SemExprWithType(c, n.sons[0]);
|
n.sons[0] := SemExprWithType(c, n.sons[0]);
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, ast, astalgo, strutils, hashes, trees, treetab, platform, magicsys,
|
nsystem, ast, astalgo, strutils, nhashes, trees, treetab, platform, magicsys,
|
||||||
options, msgs, crc, idents, lists, types, rnimsyn;
|
options, msgs, crc, idents, lists, types, rnimsyn;
|
||||||
|
|
||||||
function gcWalker(t: PType): PNode;
|
function gcWalker(t: PType): PNode;
|
||||||
|
|
|
||||||
|
|
@ -801,7 +801,7 @@ begin
|
||||||
inc(j);
|
inc(j);
|
||||||
if isConstExpr(a) then
|
if isConstExpr(a) then
|
||||||
while (j < sonsLen(m)) and isConstExpr(m.sons[j]) do begin
|
while (j < sonsLen(m)) and isConstExpr(m.sons[j]) do begin
|
||||||
a := evalOp(op.magic, m, a, m.sons[j]);
|
a := evalOp(op.magic, m, a, m.sons[j], nil);
|
||||||
inc(j)
|
inc(j)
|
||||||
end;
|
end;
|
||||||
addSon(result, a);
|
addSon(result, a);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, hashes, ast, astalgo, types;
|
nsystem, nhashes, ast, astalgo, types;
|
||||||
|
|
||||||
function NodeTableGet(const t: TNodeTable; key: PNode): int;
|
function NodeTableGet(const t: TNodeTable; key: PNode): int;
|
||||||
procedure NodeTablePut(var t: TNodeTable; key: PNode; val: int);
|
procedure NodeTablePut(var t: TNodeTable; key: PNode; val: int);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ interface
|
||||||
{$include 'config.inc'}
|
{$include 'config.inc'}
|
||||||
|
|
||||||
uses
|
uses
|
||||||
nsystem, hashes, strutils, idents;
|
nsystem, nhashes, strutils, idents;
|
||||||
|
|
||||||
type
|
type
|
||||||
TSpecialWord = (wInvalid,
|
TSpecialWord = (wInvalid,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
nodes: 2224942
|
|
||||||
syms: 39562
|
|
||||||
Types: 28189
|
|
||||||
|
|
||||||
nodes: 2224942
|
|
||||||
syms: 39562
|
|
||||||
Types: 29054
|
|
||||||
Ropes: 1245961
|
|
||||||
|
|
@ -81,6 +81,7 @@ Files: "lib/base/lua/*.nim"
|
||||||
|
|
||||||
[Windows]
|
[Windows]
|
||||||
Files: "bin/nimrod.exe"
|
Files: "bin/nimrod.exe"
|
||||||
|
Files: "bin/nimrodd.exe"
|
||||||
Files: "dist/mingw"
|
Files: "dist/mingw"
|
||||||
BinPath: r"bin;dist\mingw\bin"
|
BinPath: r"bin;dist\mingw\bin"
|
||||||
|
|
||||||
|
|
|
||||||
8
tests/csvtest.csv
Normal file
8
tests/csvtest.csv
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
headerField1,headerField2,headerField3
|
||||||
|
12,12132,"hallo ""
|
||||||
|
du"
|
||||||
|
1,,
|
||||||
|
,2,
|
||||||
|
,,3
|
||||||
|
1,2,3
|
||||||
|
|
||||||
|
6
tests/data.csv
Normal file
6
tests/data.csv
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
Algo_A; Algo_B; Algo_C
|
||||||
|
12; 12; 16
|
||||||
|
2; 5; 9
|
||||||
|
63; 65.3; 90
|
||||||
|
0; 1.2; 3
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,16 @@ type
|
||||||
of nkList: sons: seq[PCaseNode]
|
of nkList: sons: seq[PCaseNode]
|
||||||
else: unused: seq[string]
|
else: unused: seq[string]
|
||||||
|
|
||||||
|
TIdObj* = object of TObject
|
||||||
|
id*: int # unique id; use this for comparisons and not the pointers
|
||||||
|
|
||||||
|
PIdObj* = ref TIdObj
|
||||||
|
PIdent* = ref TIdent
|
||||||
|
TIdent*{.acyclic.} = object of TIdObj
|
||||||
|
s*: string
|
||||||
|
next*: PIdent # for hash-table chaining
|
||||||
|
h*: int # hash value of s
|
||||||
|
|
||||||
var
|
var
|
||||||
flip: int
|
flip: int
|
||||||
|
|
||||||
|
|
@ -134,7 +144,17 @@ proc buildBTree(father: var TBNode) =
|
||||||
father.t.data = @["ha", "lets", "stress", "it"]
|
father.t.data = @["ha", "lets", "stress", "it"]
|
||||||
setSons(father)
|
setSons(father)
|
||||||
|
|
||||||
|
proc getIdent(identifier: cstring, length: int, h: int): PIdent =
|
||||||
|
new(result)
|
||||||
|
result.h = h
|
||||||
|
result.s = newString(length)
|
||||||
|
|
||||||
proc main() =
|
proc main() =
|
||||||
|
discard getIdent("addr", 4, 0)
|
||||||
|
discard getIdent("hall", 4, 0)
|
||||||
|
discard getIdent("echo", 4, 0)
|
||||||
|
discard getIdent("huch", 4, 0)
|
||||||
|
|
||||||
var
|
var
|
||||||
father: TBNode
|
father: TBNode
|
||||||
for i in 1..1_00:
|
for i in 1..1_00:
|
||||||
|
|
@ -156,9 +176,6 @@ proc main() =
|
||||||
writeln(stdout, s[89])
|
writeln(stdout, s[89])
|
||||||
write(stdout, "done!\n")
|
write(stdout, "done!\n")
|
||||||
|
|
||||||
#main()
|
|
||||||
|
|
||||||
#GC_disable()
|
|
||||||
var
|
var
|
||||||
father: TBNode
|
father: TBNode
|
||||||
s: string
|
s: string
|
||||||
|
|
@ -177,4 +194,4 @@ main()
|
||||||
write(stdout, "finished\n")
|
write(stdout, "finished\n")
|
||||||
GC_fullCollect()
|
GC_fullCollect()
|
||||||
GC_fullCollect()
|
GC_fullCollect()
|
||||||
write(stdout, GC_getStatistics())
|
writeln(stdout, GC_getStatistics())
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ proc init(my: var TRectangle) =
|
||||||
my.height = 10
|
my.height = 10
|
||||||
my.draw = drawRectangle
|
my.draw = drawRectangle
|
||||||
|
|
||||||
macro `!` (n: expr): expr =
|
macro `!` (n: expr): stmt =
|
||||||
result = newNimNode(nnkCall, n)
|
result = newNimNode(nnkCall, n)
|
||||||
var dot = newNimNode(nnkDotExpr, n)
|
var dot = newNimNode(nnkDotExpr, n)
|
||||||
dot.add(n[1]) # obj
|
dot.add(n[1]) # obj
|
||||||
|
|
|
||||||
118
todo.txt
118
todo.txt
|
|
@ -4,27 +4,17 @@ TO IMPLEMENT
|
||||||
Plan
|
Plan
|
||||||
----
|
----
|
||||||
|
|
||||||
* implement new memory manager --> hopefully faster turnaround times
|
|
||||||
* implement LLVM backend --> faster turnaround times
|
* implement LLVM backend --> faster turnaround times
|
||||||
|
* implement ``p(.int, int.): string`` notation for resolution of overloaded
|
||||||
|
procs
|
||||||
RST
|
|
||||||
---
|
|
||||||
- footnotes; prefix :i: whitespace before :i:, _reference, `reference`__
|
|
||||||
__ anonymous: www.nimrod.org
|
|
||||||
|
|
||||||
Bugs
|
Bugs
|
||||||
----
|
----
|
||||||
|
|
||||||
- BUG: check if "break" is valid is not complete: "break" within "for"
|
|
||||||
may be incorrect, if the called iterator is not in a loop
|
|
||||||
--> check that "yield" is inside a loop!
|
|
||||||
- BUG: returning an array does not work --> see md5.nim module
|
- BUG: returning an array does not work --> see md5.nim module
|
||||||
- BUG: if not nodeOfDegree(g, 1) >= 0: inc(counter)
|
- BUG: if not nodeOfDegree(g, 1) >= 0: inc(counter)
|
||||||
- BUG: addr/deref may not work when interpreting
|
|
||||||
- BUG: the parser allows empty object case branches
|
- BUG: the parser allows empty object case branches
|
||||||
- BUG: when optmizing cgen.c with Visual C++, GCC, LLVM (O3), it breaks.
|
- BUG: when optmizing cgen.c with Visual C++, GCC, LLVM (O3), it breaks.
|
||||||
- BUG: ``-cc:bcc`` command line option does not error
|
|
||||||
- BUG: symbol files still do not work
|
- BUG: symbol files still do not work
|
||||||
- BUG: tlastmod returns wrong results on BSD (Linux, MacOS X: works)
|
- BUG: tlastmod returns wrong results on BSD (Linux, MacOS X: works)
|
||||||
|
|
||||||
|
|
@ -32,6 +22,12 @@ Bugs
|
||||||
High priority
|
High priority
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
- new $$ string interpolation operator?
|
||||||
|
$$"The value is $var &var" -- too hard to implement
|
||||||
|
|
||||||
|
- find a way to reindroduce the cleanup() pass for C code generation: this
|
||||||
|
is hard because of partial evaluation --> symbol files will fix this as
|
||||||
|
a side effect
|
||||||
- typeAllowed() for parameters...
|
- typeAllowed() for parameters...
|
||||||
- implicit conversions from ``ptr/ref T`` to ``var T`` and from
|
- implicit conversions from ``ptr/ref T`` to ``var T`` and from
|
||||||
``ptr/ref T`` to ``T``? Yes.
|
``ptr/ref T`` to ``T``? Yes.
|
||||||
|
|
@ -44,15 +40,40 @@ High priority
|
||||||
implement
|
implement
|
||||||
- language change: inheritance should only work with reference types, so that
|
- language change: inheritance should only work with reference types, so that
|
||||||
the ``type`` field is not needed for objects! --> zero overhead aggregation
|
the ``type`` field is not needed for objects! --> zero overhead aggregation
|
||||||
|
- resizing of strings/sequences could take into account the memory that
|
||||||
|
is allocated
|
||||||
|
|
||||||
|
|
||||||
|
Library
|
||||||
|
-------
|
||||||
|
|
||||||
|
- socket, http, ftp, email, fastcgi: implement from scratch
|
||||||
|
- osproc for Windows
|
||||||
|
- bignums
|
||||||
|
|
||||||
|
- gui module
|
||||||
|
- finish json module: hard
|
||||||
|
|
||||||
|
- YAML module
|
||||||
|
- ncurses bindings
|
||||||
|
- python
|
||||||
|
- TCL
|
||||||
|
- automate module: expect-like module for Nimrod
|
||||||
|
|
||||||
|
- for system:
|
||||||
|
proc `@` [T](a: openArray[T]): seq[T] =
|
||||||
|
newSeq(result, a.len)
|
||||||
|
for i in 0..a.len-1: result[i] = a[i]
|
||||||
|
|
||||||
|
--> ensure @[] calls the array version!
|
||||||
|
|
||||||
|
|
||||||
For the next versions
|
For the next versions
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
- multi-processor support
|
- multi-processor support
|
||||||
- IDE
|
- IDE
|
||||||
- better support for GDB?
|
|
||||||
- support for generation of dynamic libraries
|
- support for generation of dynamic libraries
|
||||||
- better code generator: skip ropes datastructure, it uses too much memory
|
|
||||||
- make set optimizations part of the transformation (--> all backends profit
|
- make set optimizations part of the transformation (--> all backends profit
|
||||||
from it), but this really needs a more abstract code generator
|
from it), but this really needs a more abstract code generator
|
||||||
|
|
||||||
|
|
@ -60,11 +81,8 @@ For the next versions
|
||||||
Further ideas/nice to have
|
Further ideas/nice to have
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
- queues additional to streams: have to positions (read/write) instead of one
|
- queues additional to streams: have two positions (read/write) instead of one
|
||||||
- introduce: refany type???
|
- introduce: refany type???
|
||||||
- CLR code generator; better use XYZ? --> Version 1.2?
|
|
||||||
- provide an interactive shell: if the user wants his commands
|
|
||||||
to be executed, the command should end with #
|
|
||||||
- implement packed arrays (bit arrays)/ packed records
|
- implement packed arrays (bit arrays)/ packed records
|
||||||
- implement tables (implement as library? - no! Builtin because of
|
- implement tables (implement as library? - no! Builtin because of
|
||||||
constructor syntax is nice to have)
|
constructor syntax is nice to have)
|
||||||
|
|
@ -119,23 +137,6 @@ Version 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Library
|
|
||||||
-------
|
|
||||||
|
|
||||||
- gui module
|
|
||||||
- finish json module: hard
|
|
||||||
- socket, http, ftp, email, fastcgi: implement from scratch
|
|
||||||
- ncurses bindings
|
|
||||||
- osproc for Windows
|
|
||||||
- bignums
|
|
||||||
- python
|
|
||||||
- TCL
|
|
||||||
- automate module: expect-like module for Nimrod
|
|
||||||
- parsecsv module
|
|
||||||
|
|
||||||
- YAML module
|
|
||||||
|
|
||||||
|
|
||||||
Low priority
|
Low priority
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
@ -176,23 +177,25 @@ Low priority
|
||||||
|
|
||||||
o.x = 13 # ambigious
|
o.x = 13 # ambigious
|
||||||
|
|
||||||
- implement stack walking via assembler for horribly advanced optimizers
|
- use `` notation for identifier concatenation
|
||||||
- use `` notation for identifier concatenation?
|
|
||||||
- Visual C++: emit ``default: __assume(0);`` for optimization
|
- Visual C++: emit ``default: __assume(0);`` for optimization
|
||||||
- macros: ``typecheck`` pragma; this is really a good idea! This allows
|
- macros: ``typecheck`` pragma; this is really a good idea! This allows
|
||||||
transformations based on types!
|
transformations based on types!
|
||||||
- make callconv a set
|
- make callconv a set
|
||||||
- partial generic instantation is missing
|
- partial generic instantation is missing
|
||||||
- get code generation for C++ exception handling right!
|
|
||||||
- find a way for easy constructors and destructors; though constructors are
|
- find a way for easy constructors and destructors; though constructors are
|
||||||
flawed... destructors are not, however!
|
flawed... destructors are not, however!
|
||||||
- multiple dispatch: no, but provide a proc ``typeId`` to allow the user to
|
- multiple dispatch: no, but provide a proc ``typeId`` to allow the user to
|
||||||
implement it efficiently
|
implement it efficiently
|
||||||
- replace ropes with strings in the C code generator: This may yield a HUGE
|
|
||||||
speedup of the compiler! (10%)
|
|
||||||
- code generated for type information is wasteful
|
- code generated for type information is wasteful
|
||||||
|
|
||||||
|
|
||||||
|
RST
|
||||||
|
---
|
||||||
|
- footnotes; prefix :i: whitespace before :i:, _reference, `reference`__
|
||||||
|
__ anonymous: www.nimrod.org
|
||||||
|
|
||||||
|
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
|
@ -542,4 +545,39 @@ Changelog
|
||||||
- BUGFIX: iterators using open arrays
|
- BUGFIX: iterators using open arrays
|
||||||
- BUGFIX: [$anyEnum]
|
- BUGFIX: [$anyEnum]
|
||||||
|
|
||||||
|
0.7.6: release
|
||||||
|
0.7.7:
|
||||||
|
- implemented the "parsecsv" module
|
||||||
|
- added ``math.TRunningStat`` object and its methods
|
||||||
|
- added ``strutils.validIdentifier``
|
||||||
|
- reStructuredText parser: implemented ``.. container ::`` directive
|
||||||
|
- updated the website
|
||||||
|
- ``cgi.stackTraceNewLine`` functionality
|
||||||
|
- added ``cgi.decodeData``
|
||||||
|
- BUGFIX: evaluation for ``mLengthSeq``, ``mLengthOpenArray``,
|
||||||
|
``evalSetLengthSeq``, ``evalNewSeq``
|
||||||
|
- ``copy`` and ``newString`` are magics now; thus several more things
|
||||||
|
can be evaluated
|
||||||
|
- ``evalAddr`` fixed
|
||||||
|
- BUGFIX: generics are now processed earlier in the pipeline; thus
|
||||||
|
generics can be used within macros
|
||||||
|
- new module ``xmlgen``
|
||||||
|
- ``macros.error`` now prints a stack trace
|
||||||
|
- changed bootstrapping in ``koch.py`` and ``boot.nim`` to fix bug
|
||||||
|
#369607.
|
||||||
|
- implemented the new memory management; the GC does not take advantage of
|
||||||
|
it yet
|
||||||
|
- BUGFIX: magics don't imply ``lfNoDecl`` any longer
|
||||||
|
- BUGFIX: ``newString`` is not evaluated at compile-time anymore, unless
|
||||||
|
evaluation is requested
|
||||||
|
- the GC now relies on the new memory manager
|
||||||
|
- BUGFIX: check that "yield" is inside a loop!
|
||||||
|
- BUGFIX: ``isAllocatedPtr`` still had a small bug
|
||||||
|
- overflow checking for ``newSeq`` now works
|
||||||
|
- BUGFIX: ``commands.processSwitch`` no errors if an argument is given to a
|
||||||
|
switch that does not receive one
|
||||||
|
|
||||||
|
0.7.8: release
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
BIN
tools/niminst
BIN
tools/niminst
Binary file not shown.
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# The Nimrod Website Generator
|
# The Nimrod Website Generator
|
||||||
# (c) Copyright 2008 Andreas Rumpf
|
# (c) Copyright 2009 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -15,7 +15,7 @@ type
|
||||||
TConfigData = object of TObject
|
TConfigData = object of TObject
|
||||||
tabs, links: seq[TKeyValPair]
|
tabs, links: seq[TKeyValPair]
|
||||||
doc, srcdoc, webdoc: seq[string]
|
doc, srcdoc, webdoc: seq[string]
|
||||||
authors, projectName, projectTitle, infile, outdir, ticker: string
|
authors, projectName, projectTitle, logo, infile, outdir, ticker: string
|
||||||
vars: PStringTable
|
vars: PStringTable
|
||||||
nimrodArgs: string
|
nimrodArgs: string
|
||||||
|
|
||||||
|
|
@ -29,6 +29,9 @@ proc initConfigData(c: var TConfigData) =
|
||||||
c.outdir = ""
|
c.outdir = ""
|
||||||
c.nimrodArgs = ""
|
c.nimrodArgs = ""
|
||||||
c.authors = ""
|
c.authors = ""
|
||||||
|
c.projectTitle = ""
|
||||||
|
c.projectName = ""
|
||||||
|
c.logo = ""
|
||||||
c.ticker = ""
|
c.ticker = ""
|
||||||
c.vars = newStringTable(modeStyleInsensitive)
|
c.vars = newStringTable(modeStyleInsensitive)
|
||||||
|
|
||||||
|
|
@ -37,7 +40,7 @@ include "sunset.tmpl"
|
||||||
# ------------------------- configuration file -------------------------------
|
# ------------------------- configuration file -------------------------------
|
||||||
|
|
||||||
const
|
const
|
||||||
Version = "0.5"
|
Version = "0.6"
|
||||||
Usage = "nimweb - Nimrod Installation Generator Version " & version & """
|
Usage = "nimweb - Nimrod Installation Generator Version " & version & """
|
||||||
|
|
||||||
(c) 2008 Andreas Rumpf
|
(c) 2008 Andreas Rumpf
|
||||||
|
|
@ -119,6 +122,7 @@ proc parseIniFile(c: var TConfigData) =
|
||||||
case normalize(k.key)
|
case normalize(k.key)
|
||||||
of "name": c.projectName = v
|
of "name": c.projectName = v
|
||||||
of "title": c.projectTitle = v
|
of "title": c.projectTitle = v
|
||||||
|
of "logo": c.logo = v
|
||||||
of "authors": c.authors = v
|
of "authors": c.authors = v
|
||||||
else: quit(errorStr(p, "unknown variable: " & k.key))
|
else: quit(errorStr(p, "unknown variable: " & k.key))
|
||||||
of "var": nil
|
of "var": nil
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,9 @@
|
||||||
<div id="links">
|
<div id="links">
|
||||||
<!-- **** INSERT LINKS HERE **** -->
|
<!-- **** INSERT LINKS HERE **** -->
|
||||||
</div>
|
</div>
|
||||||
<div id="logo"><h1>$c.projectTitle</h1></div>
|
<div id="logo"><h1>$c.projectTitle</h1>
|
||||||
|
<h2>$c.logo</h2>
|
||||||
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<div id="menu">
|
<div id="menu">
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue