version 0.8.2

This commit is contained in:
rumpf_a@web.de 2009-10-21 10:20:15 +02:00
commit 053309e60a
125 changed files with 6564 additions and 1308 deletions

View file

@ -23,7 +23,7 @@ char* nimCPU(void) { return "$2"; }
proc exec(cmd: string) = proc exec(cmd: string) =
echo(cmd) echo(cmd)
if executeShellCommand(cmd) != 0: quit("FAILURE") if execShellCmd(cmd) != 0: quit("FAILURE")
proc writePlatdefC = proc writePlatdefC =
var f: TFile var f: TFile
@ -38,19 +38,19 @@ proc rodsrc =
blacklist = ["nsystem", "nmath", "nos", "osproc", "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 = splitFile(fi).name
if find(blacklist, f) >= 0: continue if find(blacklist, f) >= 0: continue
var r = "rod" / appendFileExt(f, "nim") var r = "rod" / addFileExt(f, "nim")
if not existsFile(r) or fileNewer(fi, r): if not existsFile(r) or fileNewer(fi, r):
Exec(cmd % f) Exec(cmd % f)
proc boot(args: string) = proc boot(args: string) =
writePlatdefC() writePlatdefC()
rodsrc() rodsrc()
var newExe = appendFileExt("rod/nimrod", ExeExt) var newExe = addFileExt("rod/nimrod", ExeExt)
var oldExe = appendFileExt("bin/nimrod", ExeExt) var oldExe = addFileExt("bin/nimrod", ExeExt)
for i in 0..1: for i in 0..1:
Echo("iteration: ", $(i+1)) 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[i] % args) Exec(Bootcmd[i] % args)
if sameFileContent(newExe, oldExe): if sameFileContent(newExe, oldExe):

View file

@ -43,18 +43,20 @@ path="$lib/ecmas"
# additional options always passed to the compiler: # additional options always passed to the compiler:
--verbosity: "1" --verbosity: "1"
--parallel_build: "0" # 0 to auto-detect number of processors
hint[LineTooLong]=off hint[LineTooLong]=off
#hint[XDeclaredButNotUsed]=off #hint[XDeclaredButNotUsed]=off
@if unix: @if unix:
@if not bsd: passl= "-ldl" @end @if not bsd:
gcc.options.linker = "-ldl"
@end
@end @end
@if icc: # Configuration for the Intel C/C++ compiler:
passl = "-cxxlib" icc.options.always = "-cxxlib"
passc = "-cxxlib" icc.options.linker = "-cxxlib"
@end
# Configuration for the GNU C/C++ compiler: # Configuration for the GNU C/C++ compiler:
@if windows: @if windows:
@ -77,9 +79,7 @@ llvm_gcc.options.speed = "-O2"
llvm_gcc.options.size = "-Os" llvm_gcc.options.size = "-Os"
# Configuration for the Visual C/C++ compiler: # Configuration for the Visual C/C++ compiler:
@if vcc: vcc.options.linker = r"/F33554432" # set the stack size to 8 MB
passl: r"/F33554432" # set the stack size to 8 MB
@end
vcc.options.debug = "/RTC1 /ZI" vcc.options.debug = "/RTC1 /ZI"
vcc.options.always = "/nologo" vcc.options.always = "/nologo"
vcc.options.speed = "/Ox /arch:SSE2" vcc.options.speed = "/Ox /arch:SSE2"

View file

@ -28,9 +28,11 @@ 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 --gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no 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
--parallel_build=0|1|... perform a parallel build
value = number of processors (0 for auto-detect)
--verbosity:0|1|2|3 set Nimrod's verbosity level (0 is default) --verbosity:0|1|2|3 set Nimrod's verbosity level (0 is default)
-v, --version show detailed version information -v, --version show detailed version information

View file

@ -8,7 +8,7 @@
# #
{ {
'SymFlag': [ # already 29 flags! 'SymFlag': [ # already 30 flags!
'sfUsed', # read access of sym (for warnings) or simply used 'sfUsed', # read access of sym (for warnings) or simply used
'sfStar', # symbol has * visibility 'sfStar', # symbol has * visibility
'sfMinus', # symbol has - visibility 'sfMinus', # symbol has - visibility
@ -34,7 +34,7 @@
'sfAddrTaken', # the variable's address is taken (ex- or implicitely) 'sfAddrTaken', # the variable's address is taken (ex- or implicitely)
'sfCompilerProc', # proc is a compiler proc, that is a C proc that is 'sfCompilerProc', # proc is a compiler proc, that is a C proc that is
# needed for the code generator # needed for the code generator
'sfCppMethod', # proc is a C++ method (not implemented yet) 'sfProcvar', # proc can be passed to a proc var
'sfDiscriminant', # field is a discriminant in a record/object 'sfDiscriminant', # field is a discriminant in a record/object
'sfDeprecated', # symbol is deprecated 'sfDeprecated', # symbol is deprecated
'sfInClosure', # variable is accessed by a closure 'sfInClosure', # variable is accessed by a closure
@ -152,7 +152,6 @@
'nkAccQuoted', # `a` as a node 'nkAccQuoted', # `a` as a node
'nkTableConstr', # a table constructor {expr: expr} 'nkTableConstr', # a table constructor {expr: expr}
'nkQualified', # describes a.b for qualified identifiers
'nkBind', # ``bind expr`` node 'nkBind', # ``bind expr`` node
'nkSymChoice', # symbol choice node 'nkSymChoice', # symbol choice node
'nkHiddenStdConv', # an implicit standard type conversion 'nkHiddenStdConv', # an implicit standard type conversion

View file

@ -27,7 +27,7 @@ Options:
-a, --assertions:on|off code generation for assertions ON|OFF -a, --assertions:on|off code generation for assertions ON|OFF
--dead_code_elim:on|off whole program dead code elimination ON|OFF --dead_code_elim:on|off whole program dead code elimination ON|OFF
--opt:none|speed|size optimize not at all or for speed|size --opt:none|speed|size optimize not at all or for speed|size
--app:console|gui generate a console|GUI application --app:console|gui|lib generate a console|GUI application|dynamic library
-r, --run run the compiled program with given arguments -r, --run run the compiled program with given arguments
--advanced show advanced command line switches --advanced show advanced command line switches
-h, --help show this help -h, --help show this help

View file

@ -92,7 +92,7 @@
{'errCannotEvalXBecauseIncompletelyDefined': {'errCannotEvalXBecauseIncompletelyDefined':
"cannot evalutate '$1' because type is not defined completely"}, "cannot evalutate '$1' because type is not defined completely"},
{'errChrExpectsRange0_255': "'chr' expects an int in the range 0..255"}, {'errChrExpectsRange0_255': "'chr' expects an int in the range 0..255"},
{'errDotRequiresRecordOrObjectType': "'.' requires a record or object type"}, {'errDynlibRequiresExportc': "'dynlib' requires 'exportc'"},
{'errUndeclaredFieldX': "undeclared field: '$1'"}, {'errUndeclaredFieldX': "undeclared field: '$1'"},
{'errNilAccess': 'attempt to access a nil address'}, {'errNilAccess': 'attempt to access a nil address'},
{'errIndexOutOfBounds': 'index out of bounds'}, {'errIndexOutOfBounds': 'index out of bounds'},
@ -152,7 +152,7 @@
{'errButExpectedX': "but expected '$1'"}, {'errButExpectedX': "but expected '$1'"},
{'errAmbiguousCallXYZ': 'ambiguous call; both $1 and $2 match for: $3'}, {'errAmbiguousCallXYZ': 'ambiguous call; both $1 and $2 match for: $3'},
{'errWrongNumberOfArguments': 'wrong number of arguments'}, {'errWrongNumberOfArguments': 'wrong number of arguments'},
{'errInlineProcHasNoAddress': 'an inline proc has no address'}, {'errXCannotBePassedToProcVar': "'$1' cannot be passed to a procvar"},
{'errXCannotBeInParamDecl': '$1 cannot be declared in parameter declaration'}, {'errXCannotBeInParamDecl': '$1 cannot be declared in parameter declaration'},
{'errPragmaOnlyInHeaderOfProc': {'errPragmaOnlyInHeaderOfProc':
'pragmas are only in the header of a proc allowed'}, 'pragmas are only in the header of a proc allowed'},
@ -186,7 +186,6 @@
{'errATypeHasNoValue': 'a type has no value'}, {'errATypeHasNoValue': 'a type has no value'},
{'errXisNoType': "invalid type: '$1'"}, {'errXisNoType': "invalid type: '$1'"},
{'errCircumNeedsPointer': "'^' needs a pointer or reference type"}, {'errCircumNeedsPointer': "'^' needs a pointer or reference type"},
{'errInvalidContextForBuiltinX': "invalid context for builtin '$1'"},
{'errInvalidExpression': 'invalid expression'}, {'errInvalidExpression': 'invalid expression'},
{'errInvalidExpressionX': "invalid expression: '$1'"}, {'errInvalidExpressionX': "invalid expression: '$1'"},
{'errEnumHasNoValueX': "enum has no value '$1'"}, {'errEnumHasNoValueX': "enum has no value '$1'"},
@ -250,6 +249,8 @@
{'warnUnknownSubstitutionX': "unknown substitution '$1'"}, {'warnUnknownSubstitutionX': "unknown substitution '$1'"},
{'warnLanguageXNotSupported': "language '$1' not supported"}, {'warnLanguageXNotSupported': "language '$1' not supported"},
{'warnCommentXIgnored': "comment '$1' ignored"}, {'warnCommentXIgnored': "comment '$1' ignored"},
{'warnXisPassedToProcVar': "'$1' is passed to a procvar; deprecated"},
# user warning message: # user warning message:
{'warnUser': '$1'}, {'warnUser': '$1'},

3
doc/apis.txt Normal file → Executable file
View file

@ -66,4 +66,7 @@ function func
coordinate coord coordinate coord
rectangle rect rectangle rect
point point point point
symbol sym
identifier ident
indentation indent
------------------- ------------ -------------------------------------- ------------------- ------------ --------------------------------------

41
doc/effects.txt Executable file
View file

@ -0,0 +1,41 @@
=====================================================================
Side effects in Nimrod
=====================================================================
Note: Side effects are implicit produced values! Maybe they should be
explicit like in Haskell?
The idea is that side effects and partial evaluation belong together:
Iff a proc is side effect free and all its argument are evaluable at
compile time, it can be evaluated by the compiler. However, really
difficult is the ``newString`` proc: If it is simply wrapped, it
should not be evaluated at compile time! On other occasions it can
and should be evaluted:
.. code-block:: nimrod
proc toUpper(s: string): string =
result = newString(len(s))
for i in 0..len(s) - 1:
result[i] = toUpper(s[i])
No, it really can always be evaluated. The code generator should transform
``s = "\0\0\0..."`` back into ``s = newString(...)``.
``new`` cannot be evaluated at compile time either.
Raise statement
===============
It is impractical to consider ``raise`` a statement with side effects.
Solution
========
Being side effect free does not suffice for compile time evaluation. However,
the evaluator can attempt to evaluate at compile time.

View file

@ -8,7 +8,8 @@ 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 nimconf implements the config file reader
syntaxes dispatcher for the different parsers and filters
ptmplsyn standard template filter (``#! stdtempl``)
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

232
doc/filters.txt Executable file
View file

@ -0,0 +1,232 @@
===================
Parsers and Filters
===================
.. contents::
The Nimrod compiler contains multiple parsers. (The standard is
indentation-based.) Two others are available: The `braces`:idx: parser and the
`endX`:idx: parser. Both parsers use the same lexer as the standard parser.
To use a different parser for a source file the *shebang* notation is used:
.. code-block:: nimrod
#! braces
if (x == 10) {
echo "x is ten"
} else {
echo "x isn't ten"
}
The special ``#!`` comment for specifying a parser needs to be in the first
line with no leading whitespace, unless an UNIX shebang line is used. Then the
parser shebang can occur in the second line:
.. code-block:: nimrod
#! /usr/bin/env nimrod c -r
#! braces
if (x == 10) {
echo "x is ten"
} else {
echo "x isn't ten"
}
An UNIX shebang line is defined by the pattern ``'#!' \s* '/' .*``
(``#!`` followed by optional whitespace followed by ``/``).
Filters
=======
Nimrod's shebang also supports the invokation of `source filters`:idx: before
the source code file is passed to the parser::
#! stdtmpl(subsChar = '$', metaChar = '#')
#proc generateXML(name, age: string): string =
# result = ""
<xml>
<name>$name</name>
<age>$age</age>
</xml>
Filters transform the input character stream to an in-memory output stream.
They are used to provide templating systems or preprocessors.
As the example shows, passing arguments to a filter (or parser) can be done
just like an ordinary procedure call with named or positional arguments. The
available parameters depend on the invoked filter/parser.
Pipe operator
-------------
Filters and parsers can be combined with the ``|`` `pipe operator`:idx:. Only
the last operand can be a parser because a parser returns an abstract syntax
tree which a filter cannot process::
#! strip(startswith="<") | stdtmpl | standard
#proc generateXML(name, age: string): string =
# result = ""
<xml>
<name>$name</name>
<age>$age</age>
</xml>
Available filters
=================
**Hint:** With ``--verbosity:2`` (or higher) Nimrod lists the processed code
after each filter application.
Replace filter
--------------
The `replace`:idx: filter replaces substrings in each line.
Parameters and their defaults:
``sub: string = ""``
the substring that is searched for
``by: string = ""``
the string the substring is replaced with
Strip filter
------------
The `strip`:idx: filter simply removes leading and trailing whitespace from
each line.
Parameters and their defaults:
``startswith: string = ""``
strip only the lines that start with *startswith* (ignoring leading
whitespace). If empty every line is stripped.
``leading: bool = true``
strip leading whitespace
``trailing: bool = true``
strip trailing whitespace
StdTmpl filter
--------------
The `stdtmpl`:idx: filter provides a simple templating engine for Nimrod. The
filter uses a line based parser: Lines prefixed with a *meta character*
(default: ``#``) contain Nimrod code, other lines are verbatim. Because
indentation-based parsing is not suited for a templating engine, control flow
statements need ``end X`` delimiters.
Parameters and their defaults:
``metaChar: char = '#'``
prefix for a line that contains Nimrod code
``subsChar: char = '$'``
prefix for a Nimrod expression within a template line
``conc: string = " & "``
the operation for concatenation
``emit: string = "result.add"``
the operation to emit a string literal
``toString: string = "$"``
the operation that is applied to each expression
Example::
#! stdtmpl | standard
#proc generateHTMLPage(title, currentTab, content: string,
# tabs: openArray[string]): string =
# result = ""
<head><title>$title</title></head>
<body>
<div id="menu">
<ul>
#for tab in items(tabs):
#if currentTab == tab:
<li><a id="selected"
#else:
<li><a
#end if
href="${tab}.html">$tab</a></li>
#end for
</ul>
</div>
<div id="content">
$content
A dollar: $$.
</div>
</body>
The filter transforms this into:
.. code-block:: nimrod
proc generateHTMLPage(title, currentTab, content: string,
tabs: openArray[string]): string =
result = ""
result.add("<head><title>" & $(title) & "</title></head>\n" &
"<body>\n" &
" <div id=\"menu\">\n" &
" <ul>\n")
for tab in items(tabs):
if currentTab == tab:
result.add(" <li><a id=\"selected\" \n")
else:
result.add(" <li><a\n")
#end
result.add(" href=\"" & $(tab) & ".html\">" & $(tab) & "</a></li>\n")
#end
result.add(" </ul>\n" &
" </div>\n" &
" <div id=\"content\">\n" &
" " & $(content) & "\n" &
" A dollar: $.\n" &
" </div>\n" &
"</body>\n")
Each line that does not start with the meta character (ignoring leading
whitespace) is converted to a string literal that is added to ``result``.
The substitution character introduces a Nimrod expression *e* within the
string literal. *e* is converted to a string with the *toString* operation
which defaults to ``$``. For strong type checking, set ``toString`` to the
empty string. *e* must match this PEG pattern::
e <- [a-zA-Z\128-\255][a-zA-Z0-9\128-\255_.]* / '{' x '}'
x <- '{' x+ '}' / [^}]*
To produce a single substitution character it has to be doubled: ``$$``
produces ``$``.
The template engine is quite flexible. It is easy to produce a procedure that
writes the template code directly to a file::
#! stdtmpl(emit="f.write") | standard
#proc writeHTMLPage(f: TFile, title, currentTab, content: string,
# tabs: openArray[string]) =
<head><title>$title</title></head>
<body>
<div id="menu">
<ul>
#for tab in items(tabs):
#if currentTab == tab:
<li><a id="selected"
#else:
<li><a
#end if
href="${tab}.html" title = "$title - $tab">$tab</a></li>
#end for
</ul>
</div>
<div id="content">
$content
A dollar: $$.
</div>
</body>

179
doc/gramcurl.txt Executable file
View file

@ -0,0 +1,179 @@
module ::= stmt*
comma ::= ',' [COMMENT] [IND]
operator ::= OP0 | OR | XOR | AND | OP3 | OP4 | OP5 | OP6 | OP7
| 'is' | 'isnot' | 'in' | 'notin'
| 'div' | 'mod' | 'shl' | 'shr' | 'not'
prefixOperator ::= OP0 | OP3 | OP4 | OP5 | OP6 | OP7 | 'not'
optInd ::= [COMMENT] [IND]
lowestExpr ::= orExpr (OP0 optInd orExpr)*
orExpr ::= andExpr (OR | 'xor' optInd andExpr)*
andExpr ::= cmpExpr ('and' optInd cmpExpr)*
cmpExpr ::= ampExpr (OP3 | 'is' | 'isnot' | 'in' | 'notin' optInd ampExpr)*
ampExpr ::= plusExpr (OP4 optInd plusExpr)*
plusExpr ::= mulExpr (OP5 optInd mulExpr)*
mulExpr ::= dollarExpr (OP6 | 'div' | 'mod' | 'shl' | 'shr' optInd dollarExpr)*
dollarExpr ::= primary (OP7 optInd primary)*
indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr]
castExpr ::= 'cast' '[' optInd typeDesc [SAD] ']' '(' optInd expr [SAD] ')'
addrExpr ::= 'addr' '(' optInd expr ')'
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')'
| '[' ']' | '=' | literal)+ '`'
| IDENT
primaryPrefix ::= (prefixOperator | 'bind') optInd
primarySuffix ::= '.' optInd symbol
| '(' optInd namedExprList [SAD] ')'
| '[' optInd [indexExpr (comma indexExpr)* [comma]] [SAD] ']'
| '^'
| pragma
primary ::= primaryPrefix* (symbol | constructor | castExpr | addrExpr)
primarySuffix*
literal ::= INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
| FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
| STR_LIT | RSTR_LIT | TRIPLESTR_LIT
| CHAR_LIT
| NIL
constructor ::= literal
| '[' optInd colonExprList [SAD] ']'
| '{' optInd sliceExprList [SAD] '}'
| '(' optInd colonExprList [SAD] ')'
colonExpr ::= expr [':' expr]
colonExprList ::= [colonExpr (comma colonExpr)* [comma]]
namedExpr ::= expr ['=' expr]
namedExprList ::= [namedExpr (comma namedExpr)* [comma]]
sliceExpr ::= expr ['..' expr]
sliceExprList ::= [sliceExpr (comma sliceExpr)* [comma]]
exprOrType ::= lowestExpr
| 'if' '(' expr ')' expr ('elif' '(' expr ')' expr)* 'else' expr
| 'var' exprOrType
| 'ref' exprOrType
| 'ptr' exprOrType
| 'type' exprOrType
| 'tuple' tupleDesc
expr ::= exprOrType
| 'proc' paramList [pragma] ['=' stmt]
qualifiedIdent ::= symbol ['.' symbol]
typeDesc ::= exprOrType
| 'proc' paramList [pragma]
macroStmt ::= '{' [stmt] '}' ('of' [sliceExprList] stmt
|'elif' '(' expr ')' stmt
|'except' '(' exceptList ')' stmt )*
['else' stmt]
simpleStmt ::= returnStmt
| yieldStmt
| discardStmt
| raiseStmt
| breakStmt
| continueStmt
| pragma
| importStmt
| fromStmt
| includeStmt
| exprStmt
complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
| blockStmt | asmStmt
| procDecl | iteratorDecl | macroDecl | templateDecl | methodDecl
| constSection | typeSection | whenStmt | varSection
stmt ::= simpleStmt
| indPush (complexStmt | simpleStmt) (';' (complexStmt | simpleStmt))*
DED indPop
exprStmt ::= lowestExpr ['=' expr | [expr (comma expr)*] [macroStmt]]
returnStmt ::= 'return' [expr]
yieldStmt ::= 'yield' expr
discardStmt ::= 'discard' expr
raiseStmt ::= 'raise' [expr]
breakStmt ::= 'break' [symbol]
continueStmt ::= 'continue'
ifStmt ::= 'if' '(' expr ')' stmt ('elif' '(' expr ')' stmt)* ['else' stmt]
whenStmt ::= 'when' '(' expr ')' stmt ('elif' '(' expr ')' stmt)* ['else' stmt]
caseStmt ::= 'case' '(' expr ')' ('of' sliceExprList ':' stmt)*
('elif' '(' expr ')' stmt)*
['else' stmt]
whileStmt ::= 'while' '(' expr ')' stmt
forStmt ::= 'for' '(' symbol (comma symbol)* 'in' expr ['..' expr] ')' stmt
exceptList ::= [qualifiedIdent (comma qualifiedIdent)*]
tryStmt ::= 'try' stmt
('except' '(' exceptList ')' stmt)*
['finally' stmt]
asmStmt ::= 'asm' [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
blockStmt ::= 'block' [symbol] stmt
filename ::= symbol | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
importStmt ::= 'import' filename (comma filename)*
includeStmt ::= 'include' filename (comma filename)*
fromStmt ::= 'from' filename 'import' symbol (comma symbol)*
pragma ::= '{.' optInd (colonExpr [comma])* [SAD] ('.}' | '}')
param ::= symbol (comma symbol)* (':' typeDesc ['=' expr] | '=' expr)
paramList ::= ['(' [param (comma param)*] [SAD] ')'] [':' typeDesc]
genericParam ::= symbol [':' typeDesc] ['=' expr]
genericParams ::= '[' genericParam (comma genericParam)* [SAD] ']'
routineDecl := symbol ['*'] [genericParams] paramList [pragma] ['=' stmt]
procDecl ::= 'proc' routineDecl
macroDecl ::= 'macro' routineDecl
iteratorDecl ::= 'iterator' routineDecl
templateDecl ::= 'template' routineDecl
methodDecl ::= 'method' routineDecl
colonAndEquals ::= [':' typeDesc] '=' expr
constDecl ::= symbol ['*'] [pragma] colonAndEquals ';' [COMMENT]
constSection ::= 'const' [COMMENT] (constDecl | '{' constDecl+ '}')
typeDef ::= typeDesc | objectDef | enumDef | 'distinct' typeDesc
objectField ::= symbol ['*'] [pragma]
objectIdentPart ::= objectField (comma objectField)* ':' typeDesc
[COMMENT|IND COMMENT]
objectWhen ::= 'when' expr ':' [COMMENT] objectPart
('elif' expr ':' [COMMENT] objectPart)*
['else' ':' [COMMENT] objectPart]
objectCase ::= 'case' expr ':' typeDesc [COMMENT]
('of' sliceExprList ':' [COMMENT] objectPart)*
['else' ':' [COMMENT] objectPart]
objectPart ::= objectWhen | objectCase | objectIdentPart | 'nil'
| indPush objectPart (SAD objectPart)* DED indPop
tupleDesc ::= '[' optInd [param (comma param)*] [SAD] ']'
objectDef ::= 'object' [pragma] ['of' typeDesc] objectPart
enumField ::= symbol ['=' expr]
enumDef ::= 'enum' ['of' typeDesc] (enumField [comma] [COMMENT | IND COMMENT])+
typeDecl ::= COMMENT
| symbol ['*'] [genericParams] ['=' typeDef] [COMMENT | IND COMMENT]
typeSection ::= 'type' indPush typeDecl (SAD typeDecl)* DED indPop
colonOrEquals ::= ':' typeDesc ['=' expr] | '=' expr
varField ::= symbol ['*'] [pragma]
varPart ::= symbol (comma symbol)* colonOrEquals [COMMENT | IND COMMENT]
varSection ::= 'var' (varPart
| indPush (COMMENT|varPart)
(SAD (COMMENT|varPart))* DED indPop)

View file

@ -19,25 +19,23 @@ plusExpr ::= mulExpr (OP5 optInd mulExpr)*
mulExpr ::= dollarExpr (OP6 | 'div' | 'mod' | 'shl' | 'shr' optInd dollarExpr)* mulExpr ::= dollarExpr (OP6 | 'div' | 'mod' | 'shl' | 'shr' optInd dollarExpr)*
dollarExpr ::= primary (OP7 optInd primary)* dollarExpr ::= primary (OP7 optInd primary)*
namedTypeOrExpr ::= indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr]
'..' [expr]
| expr ['=' (expr ['..' expr] | typeDescK | '..' [expr]) | '..' [expr]]
| typeDescK
castExpr ::= 'cast' '[' optInd typeDesc [SAD] ']' '(' optInd expr [SAD] ')' castExpr ::= 'cast' '[' optInd typeDesc [SAD] ']' '(' optInd expr [SAD] ')'
addrExpr ::= 'addr' '(' optInd expr ')' addrExpr ::= 'addr' '(' optInd expr ')'
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')' symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')'
| '[' ']' | '=' | literal)+ '`' | '[' ']' | '=' | literal)+ '`'
| IDENT | IDENT
primary ::= ((prefixOperator | 'bind') optInd)* (symbol | constructor |
castExpr | addrExpr) ( primaryPrefix ::= (prefixOperator | 'bind') optInd
'.' optInd symbol primarySuffix ::= '.' optInd symbol
| '(' optInd namedExprList [SAD] ')' | '(' optInd namedExprList [SAD] ')'
| '[' optInd | '[' optInd [indexExpr (comma indexExpr)* [comma]] [SAD] ']'
[namedTypeOrExpr (comma namedTypeOrExpr)* [comma]]
[SAD] ']'
| '^' | '^'
| pragma)* | pragma
primary ::= primaryPrefix* (symbol | constructor | castExpr | addrExpr)
primarySuffix*
literal ::= INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT literal ::= INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
| FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
@ -59,25 +57,22 @@ namedExprList ::= [namedExpr (comma namedExpr)* [comma]]
sliceExpr ::= expr ['..' expr] sliceExpr ::= expr ['..' expr]
sliceExprList ::= [sliceExpr (comma sliceExpr)* [comma]] sliceExprList ::= [sliceExpr (comma sliceExpr)* [comma]]
anonymousProc ::= 'lambda' paramList [pragma] '=' stmt exprOrType ::= lowestExpr
expr ::= lowestExpr
| anonymousProc
| 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr | 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
| 'var' exprOrType
| 'ref' exprOrType
| 'ptr' exprOrType
| 'type' exprOrType
| 'tuple' tupleDesc
namedTypeDesc ::= typeDescK | expr ['=' (typeDescK | expr)] expr ::= exprOrType
namedTypeDescList ::= [namedTypeDesc (comma namedTypeDesc)* [comma]] | 'proc' paramList [pragma] ['=' stmt]
qualifiedIdent ::= symbol ['.' symbol] qualifiedIdent ::= symbol ['.' symbol]
typeDescK ::= 'var' typeDesc typeDesc ::= exprOrType
| 'ref' typeDesc
| 'ptr' typeDesc
| 'type' expr
| 'tuple' tupleDesc
| 'proc' paramList [pragma] | 'proc' paramList [pragma]
typeDesc ::= typeDescK | primary
macroStmt ::= ':' [stmt] ('of' [sliceExprList] ':' stmt macroStmt ::= ':' [stmt] ('of' [sliceExprList] ':' stmt
|'elif' expr ':' stmt |'elif' expr ':' stmt
|'except' exceptList ':' stmt )* |'except' exceptList ':' stmt )*
@ -96,7 +91,7 @@ simpleStmt ::= returnStmt
| exprStmt | exprStmt
complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
| blockStmt | asmStmt | blockStmt | asmStmt
| procDecl | iteratorDecl | macroDecl | templateDecl | procDecl | iteratorDecl | macroDecl | templateDecl | methodDecl
| constSection | typeSection | whenStmt | varSection | constSection | typeSection | whenStmt | varSection
indPush ::= IND # and push indentation onto the stack indPush ::= IND # and push indentation onto the stack
@ -141,25 +136,24 @@ paramList ::= ['(' [param (comma param)*] [SAD] ')'] [':' typeDesc]
genericParam ::= symbol [':' typeDesc] ['=' expr] genericParam ::= symbol [':' typeDesc] ['=' expr]
genericParams ::= '[' genericParam (comma genericParam)* [SAD] ']' genericParams ::= '[' genericParam (comma genericParam)* [SAD] ']'
procDecl ::= 'proc' symbol ['*'] [genericParams] paramList [pragma]
['=' stmt] routineDecl := symbol ['*'] [genericParams] paramList [pragma] ['=' stmt]
macroDecl ::= 'macro' symbol ['*'] [genericParams] paramList [pragma] procDecl ::= 'proc' routineDecl
['=' stmt] macroDecl ::= 'macro' routineDecl
iteratorDecl ::= 'iterator' symbol ['*'] [genericParams] paramList [pragma] iteratorDecl ::= 'iterator' routineDecl
['=' stmt] templateDecl ::= 'template' routineDecl
templateDecl ::= 'template' symbol ['*'] [genericParams] paramList [pragma] methodDecl ::= 'method' routineDecl
['=' stmt]
colonAndEquals ::= [':' typeDesc] '=' expr colonAndEquals ::= [':' typeDesc] '=' expr
constDecl ::= symbol ['*'] [pragma] colonAndEquals [COMMENT | IND COMMENT] constDecl ::= symbol ['*'] [pragma] colonAndEquals [COMMENT | IND COMMENT]
| COMMENT | COMMENT
constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop
typeDef ::= typeDesc | objectDef | enumDef | 'abstract' typeDesc typeDef ::= typeDesc | objectDef | enumDef | 'distinct' typeDesc
objectField ::= symbol ['*'] [pragma] objectField ::= symbol ['*'] [pragma]
objectIdentPart ::= objectIdentPart ::= objectField (comma objectField)* ':' typeDesc
objectField (comma objectField)* ':' typeDesc [COMMENT|IND COMMENT] [COMMENT|IND COMMENT]
objectWhen ::= 'when' expr ':' [COMMENT] objectPart objectWhen ::= 'when' expr ':' [COMMENT] objectPart
('elif' expr ':' [COMMENT] objectPart)* ('elif' expr ':' [COMMENT] objectPart)*

View file

@ -66,8 +66,7 @@ Generic Operating System Services
* `os <os.html>`_ * `os <os.html>`_
Basic operating system facilities like retrieving environment variables, Basic operating system facilities like retrieving environment variables,
reading command line arguments, working with directories, running shell reading command line arguments, working with directories, running shell
commands, etc. This module is -- like any other basic library -- commands, etc.
platform independant.
* `osproc <osproc.html>`_ * `osproc <osproc.html>`_
Module for process communication beyond ``os.execShellCmd``. Module for process communication beyond ``os.execShellCmd``.

View file

@ -342,7 +342,7 @@ Syntax
====== ======
This section lists Nimrod's standard syntax in ENBF. How the parser receives This section lists Nimrod's standard syntax in ENBF. How the parser receives
indentation tokens is already described in the Lexical Analysis section. indentation tokens is already described in the `Lexical Analysis`_ section.
Nimrod allows user-definable operators. Nimrod allows user-definable operators.
Binary operators have 8 different levels of precedence. For user-defined Binary operators have 8 different levels of precedence. For user-defined
@ -363,8 +363,7 @@ Precedence level Operators First characte
================ ============================================== ================== =============== ================ ============================================== ================== ===============
The grammar's start symbol is ``module``. The grammar is LL(1) and therefore The grammar's start symbol is ``module``.
not ambiguous.
.. include:: grammar.txt .. include:: grammar.txt
:literal: :literal:
@ -875,8 +874,7 @@ Procedural type
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
A `procedural type`:idx: is internally a pointer to a procedure. ``nil`` is A `procedural type`:idx: is internally a pointer to a procedure. ``nil`` is
an allowed value for variables of a procedural type. Nimrod uses procedural an allowed value for variables of a procedural type. Nimrod uses procedural
types to achieve `functional`:idx: programming techniques. Dynamic dispatch types to achieve `functional`:idx: programming techniques.
for OOP constructs can also be implemented with procedural types.
Example: Example:
@ -946,6 +944,16 @@ each other:
Most calling conventions exist only for the Windows 32-bit platform. Most calling conventions exist only for the Windows 32-bit platform.
Assigning/passing a procedure to a procedural variable is only allowed if one
of the following conditions hold:
1) The procedure that is accessed resists in the current module.
2) The procedure is marked with the ``procvar`` pragma (see `procvar pragma`_).
3) The procedure has a calling convention that differs from ``nimcall``.
4) The procedure is anonymous.
These rules should prevent the case that extending a non-``procvar``
procedure with default parameters breaks client code.
Distinct type Distinct type
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -1054,7 +1062,7 @@ describe the type checking done by the compiler.
Type equality Type equality
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
Nimrod uses structural type equivalence for most types. Only for objects, Nimrod uses structural type equivalence for most types. Only for objects,
enumerations and abstract types name equivalence is used. The following enumerations and distinct types name equivalence is used. The following
algorithm determines type equality: algorithm determines type equality:
.. code-block:: nimrod .. code-block:: nimrod
@ -1800,6 +1808,77 @@ Even more elegant is to use `tuple unpacking`:idx: to access the tuple's fields:
assert y == 3 assert y == 3
Multi-methods
~~~~~~~~~~~~~
Procedures always use static dispatch. Dynamic dispatch is achieved by
`multi-methods`:idx:.
.. code-block:: nimrod
type
TExpr = object ## abstract base class for an expression
TLiteral = object of TExpr
x: int
TPlusExpr = object of TExpr
a, b: ref TExpr
method eval(e: ref TExpr): int =
# override this base method
quit "to override!"
method eval(e: ref TLiteral): int = return e.x
method eval(e: ref TPlusExpr): int =
# watch out: relies on dynamic binding
return eval(e.a) + eval(e.b)
proc newLit(x: int): ref TLiteral =
new(result)
result.x = x
proc newPlus(a, b: ref TExpr): ref TPlusExpr =
new(result)
result.a = a
result.b = b
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
In the example the constructors ``newLit`` and ``newPlus`` are procs
because they should use static binding, but ``eval`` is a method because it
requires dynamic binding.
In a multi-method all parameters that have an object type are used for the
dispatching:
.. code-block:: nimrod
type
TThing = object
TUnit = object of TThing
x: int
method collide(a, b: TThing) {.inline.} =
quit "to override!"
method collide(a: TThing, b: TUnit) {.inline.} =
echo "1"
method collide(a: TUnit, b: TThing) {.inline.} =
echo "2"
var
a, b: TUnit
collide(a, b) # output: 2
Invokation of a multi-method cannot be ambiguous: Collide 2 is prefered over
collide 1 because the resolution works from left to right.
Thus ``TUnit, TThing`` is prefered over ``TThing, TUnit``.
**Perfomance note**: Nimrod does not produce a virtual method table, but
generates dispatch trees. This avoids the expensive indirect branch for method
calls and enables inlining. However, other optimizations like compile time
evaluation or dead code elimination do not work with methods.
Iterators and the for statement Iterators and the for statement
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -2275,6 +2354,12 @@ error to mark a proc/iterator to have no side effect if the compiler cannot
verify this. verify this.
procvar pragma
--------------
The `procvar`:idx: pragma is used to mark a proc that it can be passed to a
procedural variable.
compileTime pragma compileTime pragma
------------------ ------------------
The `compileTime`:idx: pragma is used to mark a proc to be used at compile The `compileTime`:idx: pragma is used to mark a proc to be used at compile

View file

@ -72,8 +72,7 @@ New Pragmas and Options
----------------------- -----------------------
Because Nimrod generates C code it needs some "red tape" to work properly. Because Nimrod generates C code it needs some "red tape" to work properly.
Thus lots of options and pragmas for tweaking the generated C code are Lots of options and pragmas for tweaking the generated C code are available.
available.
Importc Pragma Importc Pragma
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
@ -137,7 +136,7 @@ and instead the generated code should contain an ``#include``:
PFile {.importc: "FILE*", header: "<stdio.h>".} = distinct pointer PFile {.importc: "FILE*", header: "<stdio.h>".} = distinct pointer
# import C's FILE* type; Nimrod will treat it as a new pointer type # import C's FILE* type; Nimrod will treat it as a new pointer type
The ``header`` pragma expects always a string constant. The string contant The ``header`` pragma always expects a string constant. The string contant
contains the header file: As usual for C, a system header file is enclosed contains the header file: As usual for C, a system header file is enclosed
in angle brackets: ``<>``. If no angle brackets are given, Nimrod in angle brackets: ``<>``. If no angle brackets are given, Nimrod
encloses the header file in ``""`` in the generated C code. encloses the header file in ``""`` in the generated C code.
@ -145,9 +144,9 @@ encloses the header file in ``""`` in the generated C code.
Varargs Pragma Varargs Pragma
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
The `varargs`:idx: pragma can be applied to procedures only. It tells Nimrod The `varargs`:idx: pragma can be applied to procedures only (and procedure
that the proc can take a variable number of parameters after the last types). It tells Nimrod that the proc can take a variable number of parameters
specified parameter. Nimrod string values will be converted to C after the last specified parameter. Nimrod string values will be converted to C
strings automatically: strings automatically:
.. code-block:: Nimrod .. code-block:: Nimrod
@ -218,7 +217,7 @@ collector to not consider objects of this type as part of a cycle:
data: string data: string
In the example a tree structure is declared with the ``TNode`` type. Note that In the example a tree structure is declared with the ``TNode`` type. Note that
the type definition is recursive thus the GC has to assume that objects of the type definition is recursive and the GC has to assume that objects of
this type may form a cyclic graph. The ``acyclic`` pragma passes the this type may form a cyclic graph. The ``acyclic`` pragma passes the
information that this cannot happen to the GC. If the programmer uses the information that this cannot happen to the GC. If the programmer uses the
``acyclic`` pragma for data types that are in reality cyclic, the GC may leak ``acyclic`` pragma for data types that are in reality cyclic, the GC may leak

180
doc/pegdocs.txt Executable file
View file

@ -0,0 +1,180 @@
PEG syntax and semantics
========================
A PEG (Parsing expression grammar) is a simple deterministic grammar, that can
be directly used for parsing. The current implementation has been designed as
a more powerful replacement for regular expressions. UTF-8 is supported.
The notation used for a PEG is similar to that of EBNF:
=============== ============================================================
notation meaning
=============== ============================================================
``A / ... / Z`` Ordered choice: Apply expressions `A`, ..., `Z`, in this
order, to the text ahead, until one of them succeeds and
possibly consumes some text. Indicate success if one of
expressions succeeded. Otherwise do not consume any text
and indicate failure.
``A ... Z`` Sequence: Apply expressions `A`, ..., `Z`, in this order,
to consume consecutive portions of the text ahead, as long
as they succeed. Indicate success if all succeeded.
Otherwise do not consume any text and indicate failure.
The sequence's precedence is higher than that of ordered
choice: ``A B / C`` means ``(A B) / Z`` and
not ``A (B / Z)``.
``(E)`` Grouping: Parenthesis can be used to change
operator priority.
``{E}`` Capture: Apply expression `E` and store the substring
that matched `E` into a *capture* that can be accessed
after the matching process.
``&E`` And predicate: Indicate success if expression `E` matches
the text ahead; otherwise indicate failure. Do not consume
any text.
``!E`` Not predicate: Indicate failure if expression E matches the
text ahead; otherwise indicate success. Do not consume any
text.
``E+`` One or more: Apply expression `E` repeatedly to match
the text ahead, as long as it succeeds. Consume the matched
text (if any) and indicate success if there was at least
one match. Otherwise indicate failure.
``E*`` Zero or more: Apply expression `E` repeatedly to match
the text ahead, as long as it succeeds. Consume the matched
text (if any). Always indicate success.
``E?`` Zero or one: If expression `E` matches the text ahead,
consume it. Always indicate success.
``[s]`` Character class: If the character ahead appears in the
string `s`, consume it and indicate success. Otherwise
indicate failure.
``[a-b]`` Character range: If the character ahead is one from the
range `a` through `b`, consume it and indicate success.
Otherwise indicate failure.
``'s'`` String: If the text ahead is the string `s`, consume it
and indicate success. Otherwise indicate failure.
``i's'`` String match ignoring case.
``y's'`` String match ignoring style.
``v's'`` Verbatim string match: Use this to override a global
``\i`` or ``\y`` modifier.
``.`` Any character: If there is a character ahead, consume it
and indicate success. Otherwise (that is, at the end of
input) indicate failure.
``_`` Any Unicode character: If there is an UTF-8 character
ahead, consume it and indicate success. Otherwise indicate
failure.
``A <- E`` Rule: Bind the expression `E` to the *nonterminal symbol*
`A`. **Left recursive rules are not possible and crash the
matching engine.**
``\identifier`` Built-in macro for a longer expression.
``\ddd`` Character with decimal code *ddd*.
``\"``, etc Literal ``"``, etc.
=============== ============================================================
Built-in macros
---------------
============== ============================================================
macro meaning
============== ============================================================
``\d`` any decimal digit: ``[0-9]``
``\D`` any character that is not a decimal digit: ``[^0-9]``
``\s`` any whitespace character: ``[ \9-\13]``
``\S`` any character that is not a whitespace character:
``[^ \9-\13]``
``\w`` any "word" character: ``[a-zA-Z_]``
``\W`` any "non-word" character: ``[^a-zA-Z_]``
``\n`` any newline combination: ``\10 / \13\10 / \13``
``\i`` ignore case for matching; use this at the start of the PEG
``\y`` ignore style for matching; use this at the start of the PEG
``\ident`` a standard ASCII identifier: ``[a-zA-Z_][a-zA-Z_0-9]*``
============== ============================================================
A backslash followed by a letter is a built-in macro, otherwise it
is used for ordinary escaping:
============== ============================================================
notation meaning
============== ============================================================
``\\`` a single backslash
``\*`` same as ``'*'``
``\t`` not a tabulator, but an (unknown) built-in
============== ============================================================
Supported PEG grammar
---------------------
The PEG parser implements this grammar (written in PEG syntax)::
# Example grammar of PEG in PEG syntax.
# Comments start with '#'.
# First symbol is the start symbol.
grammar <- rule* / expr
identifier <- [A-Za-z][A-Za-z0-9_]*
charsetchar <- "\\" . / [^\]]
charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
"'" ("\\" . / [^'])* "'")
builtin <- "\\" identifier / [^\13\10]
comment <- '#' !\n* \n
ig <- (\s / comment)* # things to ignore
rule <- identifier \s* "<-" expr ig
identNoArrow <- identifier !(\s* "<-")
primary <- (ig '&' / ig '!')* ((ig identNoArrow / ig charset / ig stringlit
/ ig builtin / ig '.' / ig '_'
/ (ig "(" expr ig ")"))
(ig '?' / ig '*' / ig '+')*)
# Concatenation has higher priority than choice:
# ``a b / c`` means ``(a b) / c``
seqExpr <- primary+
expr <- seqExpr (ig "/" expr)*
Examples
--------
Check if `s` matches Nimrod's "while" keyword:
.. code-block:: nimrod
s =~ peg" y'while'"
Exchange (key, val)-pairs:
.. code-block:: nimrod
"key: val; key2: val2".replace(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1")
Determine the ``#include``'ed files of a C file:
.. code-block:: nimrod
for line in lines("myfile.c"):
if line =~ peg"""s <- ws '#include' ws '"' {[^"]+} '"' ws
comment <- '/*' (!'*/' . )* '*/' / '//' .*
ws <- (comment / \s+)* """:
echo matches[0]
PEG vs regular expression
-------------------------
As a regular expression ``\[.*\]`` maches longest possible text between ``'['``
and ``']'``. As a PEG it never matches anything, because a PEG is
deterministic: ``.*`` consumes the rest of the input, so ``\]`` never matches.
As a PEG this needs to be written as: ``\[ ( !\] . )* \]``
Note that the regular expression does not behave as intended either:
``*`` should not be greedy, so ``\[.*?\]`` should be used.
PEG construction
----------------
There are two ways to construct a PEG in Nimrod code:
(1) Parsing a string into an AST which consists of `TPeg` nodes with the
`peg` proc.
(2) Constructing the AST directly with proc calls. This method does not
support constructing rules, only simple expressions and is not as
convenient. It's only advantage is that it does not pull in the whole PEG
parser into your executable.

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
======================== ========================
Nimrod Tutorial (Part I) Nimrod Tutorial (Part I)
======================== ========================
@ -10,7 +10,7 @@ Nimrod Tutorial (Part I)
Introduction Introduction
============ ============
"Before you run you must learn to walk." "Der Mensch ist doch ein Augentier -- schöne Dinge wünsch ich mir."
This document is a tutorial for the programming language *Nimrod*. After this This document is a tutorial for the programming language *Nimrod*. After this
tutorial you will have a decent knowledge about Nimrod. This tutorial assumes tutorial you will have a decent knowledge about Nimrod. This tutorial assumes
@ -34,8 +34,8 @@ Save this code to the file "greetings.nim". Now compile and run it::
nimrod compile --run greetings.nim nimrod compile --run greetings.nim
As you see, with the ``--run`` switch Nimrod executes the file automatically With the ``--run`` switch Nimrod executes the file automatically
after compilation. You can even give your program command line arguments by after compilation. You can give your program command line arguments by
appending them after the filename:: appending them after the filename::
nimrod compile --run greetings.nim arg1 arg2 nimrod compile --run greetings.nim arg1 arg2

View file

@ -177,8 +177,7 @@ bound to a class. This has disadvantages:
* Adding a method to a class the programmer has no control over is * Adding a method to a class the programmer has no control over is
impossible or needs ugly workarounds. impossible or needs ugly workarounds.
* Often it is unclear where the method should belong to: Is * Often it is unclear where the method should belong to: Is
``join`` a string method or an array method? Should the complex ``join`` a string method or an array method?
``vertexCover`` algorithm really be a method of the ``graph`` class?
Nimrod avoids these problems by not assigning methods to a class. All methods Nimrod avoids these problems by not assigning methods to a class. All methods
in Nimrod are `multi-methods`:idx:. As we will see later, multi-methods are in Nimrod are `multi-methods`:idx:. As we will see later, multi-methods are
@ -206,7 +205,7 @@ for any type:
(Another way to look at the method call syntax is that it provides the missing (Another way to look at the method call syntax is that it provides the missing
postfix notation.) postfix notation.)
So code that looks "pure object oriented" is easy to write: So "pure object oriented" code is easy to write:
.. code-block:: nimrod .. code-block:: nimrod
import strutils import strutils
@ -277,7 +276,7 @@ already provides ``v[]`` access.
Dynamic dispatch Dynamic dispatch
---------------- ----------------
Procedures always use static dispatch. To get dynamic dispatch, replace the Procedures always use static dispatch. For dynamic dispatch replace the
``proc`` keyword by ``method``: ``proc`` keyword by ``method``:
.. code-block:: nimrod .. code-block:: nimrod

23
examples/filterex.nim Executable file
View file

@ -0,0 +1,23 @@
#! stdtmpl | standard
#proc generateHTMLPage(title, currentTab, content: string,
# tabs: openArray[string]): string =
# result = ""
<head><title>$title</title></head>
<body>
<div id="menu">
<ul>
#for tab in items(tabs):
#if currentTab == tab:
<li><a id="selected"
#else:
<li><a
#end if
href="${tab}.html" title = "$title - $tab">$tab</a></li>
#end for
</ul>
</div>
<div id="content">
$content
A dollar: $$.
</div>
</body>

37
examples/iupex1.nim Executable file
View file

@ -0,0 +1,37 @@
# Example IUP program
# IupTabs: Creates a IupTabs control.
import iup
discard iup.Open(nil, nil)
var vbox1 = Iup.Vbox(Iup.Label("Inside Tab A"), Iup.Button("Button A", ""), nil)
var vbox2 = Iup.Vbox(Iup.Label("Inside Tab B"), Iup.Button("Button B", ""), nil)
Iup.SetAttribute(vbox1, "TABTITLE", "Tab A")
Iup.SetAttribute(vbox2, "TABTITLE", "Tab B")
var tabs1 = Iup.Tabs(vbox1, vbox2, nil)
vbox1 = Iup.Vbox(Iup.Label("Inside Tab C"), Iup.Button("Button C", ""), nil)
vbox2 = Iup.Vbox(Iup.Label("Inside Tab D"), Iup.Button("Button D", ""), nil)
Iup.SetAttribute(vbox1, "TABTITLE", "Tab C")
Iup.SetAttribute(vbox2, "TABTITLE", "Tab D")
var tabs2 = Iup.Tabs(vbox1, vbox2, nil)
Iup.SetAttribute(tabs2, "TABTYPE", "LEFT")
var box = Iup.Hbox(tabs1, tabs2, nil)
Iup.SetAttribute(box, "MARGIN", "10x10")
Iup.SetAttribute(box, "GAP", "10")
var dlg = Iup.Dialog(box)
Iup.SetAttribute(dlg, "TITLE", "IupTabs")
Iup.SetAttribute(dlg, "SIZE", "200x80")
discard ShowXY(dlg, IUP_CENTER, IUP_CENTER)
discard MainLoop()
Close()

7
examples/keyval2.nim Executable file
View file

@ -0,0 +1,7 @@
# Filter key=value pairs from "myfile.txt"
import pegs
for x in lines("myfile.txt"):
if x =~ peg"{\ident} \s* '=' \s* {.*}":
echo "Key: ", matches[1],
" Value: ", matches[2]

8
examples/transff.nim Executable file
View file

@ -0,0 +1,8 @@
# Shows how to transform a file
import pegs
transformFile("infile.txt", "outfile.txt",
[(peg"""S <- {typedesc} \s* {\ident} \s* ','
typedesc <- \ident '*'* """, r"$2: $1")])

View file

@ -44,11 +44,10 @@ though.
Installation on Windows Installation on Windows
----------------------- -----------------------
Install Nimrod by downloading and running Install Nimrod by downloading and running the ``nimrod_$version.exe`` file.
the ``nimrod_$version.exe`` file. As default, the ``GCC`` As default, the ``GCC`` compiler is used that is bundled with this installer.
compiler is used that is bundled with this installer. **You can change **You can change the configuration file** ``config/nimrod.cfg`` **to use
the configuration file** ``config/nimrod.cfg`` **to use another C compiler another C compiler or change the path to GCC.**
or change the path to GCC.**
Currently, the following C compilers are supported under Windows: Currently, the following C compilers are supported under Windows:

View file

@ -12,7 +12,7 @@ from pycompab import *
# --------------------- constants ---------------------------------------- # --------------------- constants ----------------------------------------
NIMROD_VERSION = '0.8.1' NIMROD_VERSION = '0.8.2'
# 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**!
@ -327,8 +327,8 @@ def CogRule(name, filename, dependson):
_nim_exe = os.path.join(os.getcwd(), "bin", ExeExt("nim")) _nim_exe = os.path.join(os.getcwd(), "bin", ExeExt("nim"))
_output_obj = os.path.join(os.getcwd(), "obj") _output_obj = os.path.join(os.getcwd(), "obj")
FPC_CMD = Subs(r"fpc -Cs16777216 -gl -bl -Crtoi -Sgidh -vw -Se1 -o$1 " FPC_CMD = Subs("fpc -Cs16777216 -gl -bl -Crtoi -Sgidh -vw -Se1 -o\"$1\" "
r"-FU$2 $3", _nim_exe, _output_obj, "-FU\"$2\" \"$3\"", _nim_exe, _output_obj,
os.path.join(os.getcwd(), "nim", "nimrod.pas")) os.path.join(os.getcwd(), "nim", "nimrod.pas"))
def buildRod(options): def buildRod(options):
@ -586,6 +586,7 @@ def cmd_clean(dir = "."):
for f in Glob("*.idb"): Remove(f) for f in Glob("*.idb"): Remove(f)
for f in Glob("web/*.html"): Remove(f) for f in Glob("web/*.html"): Remove(f)
for f in Glob("doc/*.html"): Remove(f) for f in Glob("doc/*.html"): Remove(f)
for f in Glob("doc/*.pdf"): Remove(f)
for f in Glob("rod/*.nim"): Remove(f) # remove generated source code for f in Glob("rod/*.nim"): Remove(f) # remove generated source code
def visit(extRegEx, dirname, names): def visit(extRegEx, dirname, names):
if os.path.split(dirname)[1] == "nimcache": if os.path.split(dirname)[1] == "nimcache":

129
lib/impure/db_postgres.nim Executable file
View file

@ -0,0 +1,129 @@
# Nimrod PostgreSQL database wrapper
# (c) 2009 Andreas Rumpf
import strutils, postgres
type
TDbHandle* = PGconn
TRow* = seq[string]
EDb* = object of EIO
proc dbError(db: TDbHandle) {.noreturn.} =
## raises an EDb exception.
var e: ref EDb
new(e)
e.msg = PQerrorMessage(db)
raise e
proc dbError*(msg: string) {.noreturn.} =
## raises an EDb exception with message `msg`.
var e: ref EDb
new(e)
e.msg = msg
raise e
when false:
proc dbQueryOpt*(db: TDbHandle, query: string, args: openarray[string]) =
var stmt = mysql_stmt_init(db)
if stmt == nil: dbError(db)
if mysql_stmt_prepare(stmt, query, len(query)) != 0:
dbError(db)
var
bind: seq[MYSQL_BIND]
discard mysql_stmt_close(stmt)
proc dbQuote(s: string): string =
result = "'"
for c in items(s):
if c == '\'': add(result, "''")
else: add(result, c)
add(result, '\'')
proc dbFormat(formatstr: string, args: openarray[string]): string =
result = ""
var a = 0
for c in items(formatstr):
if c == '?':
add(result, dbQuote(args[a]))
inc(a)
else:
add(result, c)
proc dbTryQuery*(db: TDbHandle, query: string, args: openarray[string]): bool =
var q = dbFormat(query, args)
var res = PQExec(db, q)
result = PQresultStatus(res) == PGRES_COMMAND_OK
PQclear(res)
proc dbQuery*(db: TDbHandle, query: string, args: openarray[string]) =
var q = dbFormat(query, args)
var res = PQExec(db, q)
if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
PQclear(res)
proc dbTryInsertID*(db: TDbHandle, query: string,
args: openarray[string]): int64 =
var q = dbFormat(query, args)
if mysqlRealQuery(db, q, q.len) != 0'i32:
result = -1'i64
else:
result = mysql_insert_id(db)
LAST_INSERT_ID()
proc dbInsertID*(db: TDbHandle, query: string, args: openArray[string]): int64 =
result = dbTryInsertID(db, query, args)
if result < 0: dbError(db)
proc dbQueryAffectedRows*(db: TDbHandle, query: string,
args: openArray[string]): int64 =
## runs the query (typically "UPDATE") and returns the
## number of affected rows
var q = dbFormat(query, args)
var res = PQExec(db, q)
if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
result = parseBiggestInt($PQcmdTuples(res))
PQclear(res)
proc newRow(L: int): TRow =
newSeq(result, L)
for i in 0..L-1: result[i] = ""
iterator dbFastRows*(db: TDbHandle, query: string,
args: openarray[string]): TRow =
var q = dbFormat(query, args)
var res = PQExec(db, q)
if PQresultStatus(res) != PGRES_TUPLES_OK: dbError(db)
var L = int(PQnfields(res))
var result = newRow(L)
for i in 0..PQntuples(res)-1:
for j in 0..L-1:
setLen(result[j], 0)
add(result[j], PQgetvalue(res, i, j))
yield result
PQclear(res)
proc dbGetAllRows*(db: TDbHandle, query: string,
args: openarray[string]): seq[TRow] =
result = @[]
for r in dbFastRows(db, query, args):
result.add(r)
iterator dbRows*(db: TDbHandle, query: string,
args: openarray[string]): TRow =
for r in items(dbGetAllRows(db, query, args)): yield r
proc dbGetValue*(db: TDbHandle, query: string,
args: openarray[string]): string =
result = ""
for row in dbFastRows(db, query, args):
result = row[0]
break
proc dbClose*(db: TDbHandle) =
if db != nil: PQfinish(db)
proc dbOpen*(connection, user, password, database: string): TDbHandle =
result = PQsetdbLogin(nil, nil, nil, nil, database, user, password)
if PQStatus(result) != CONNECTION_OK: result = nil

View file

@ -92,8 +92,8 @@ __TINYC__
# define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name) # define N_FASTCALL_PTR(rettype, name) rettype (__fastcall *name)
# define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name) # define N_SAFECALL_PTR(rettype, name) rettype (__safecall *name)
# define N_LIB_EXPORT __declspec(dllexport) # define N_LIB_EXPORT extern __declspec(dllexport)
# define N_LIB_IMPORT __declspec(dllimport) # define N_LIB_IMPORT extern __declspec(dllimport)
#else #else
# define N_CDECL(rettype, name) rettype name # define N_CDECL(rettype, name) rettype name
# define N_STDCALL(rettype, name) rettype name # define N_STDCALL(rettype, name) rettype name
@ -107,7 +107,7 @@ __TINYC__
# define N_FASTCALL_PTR(rettype, name) rettype (*name) # define N_FASTCALL_PTR(rettype, name) rettype (*name)
# define N_SAFECALL_PTR(rettype, name) rettype (*name) # define N_SAFECALL_PTR(rettype, name) rettype (*name)
# define N_LIB_EXPORT # define N_LIB_EXPORT extern
# define N_LIB_IMPORT extern # define N_LIB_IMPORT extern
#endif #endif

View file

@ -1222,6 +1222,8 @@ var
SC_XOPEN_STREAMS*{.importc: "_SC_XOPEN_STREAMS", header: "<unistd.h>".}: cint SC_XOPEN_STREAMS*{.importc: "_SC_XOPEN_STREAMS", header: "<unistd.h>".}: cint
SC_XOPEN_UNIX*{.importc: "_SC_XOPEN_UNIX", header: "<unistd.h>".}: cint SC_XOPEN_UNIX*{.importc: "_SC_XOPEN_UNIX", header: "<unistd.h>".}: cint
SC_XOPEN_VERSION*{.importc: "_SC_XOPEN_VERSION", header: "<unistd.h>".}: cint SC_XOPEN_VERSION*{.importc: "_SC_XOPEN_VERSION", header: "<unistd.h>".}: cint
SC_NPROCESSORS_ONLN*{.importc: "_SC_NPROCESSORS_ONLN",
header: "<unistd.h>".}: cint
SEM_FAILED* {.importc, header: "<semaphore.h>".}: pointer SEM_FAILED* {.importc, header: "<semaphore.h>".}: pointer
IPC_CREAT* {.importc, header: "<sys/ipc.h>".}: cint IPC_CREAT* {.importc, header: "<sys/ipc.h>".}: cint

163
lib/pure/hashtabs.nim Executable file
View file

@ -0,0 +1,163 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## The ``hashtabs`` module implements an efficient generic hash
## table/dictionary data type.
import
hashes
const
growthFactor = 2
startSize = 8
sham = sizeof(THash)*8-2 # shift amount
mask = 0b11 shl sham
usedSlot = 0b10 shl sham
delSlot = 0b01 shl sham
emptySlot = 0
type
TTable*[TKey, TValue] = object
counter: int
data: seq[tuple[key: TKey, val: TValue, h: THash]]
proc init*(t: var TTable, size = startSize) =
t.counter = 0
newSeq(t.data, size)
proc markUsed(h: THash): THash {.inline.} =
return h and not mask or usedSlot
proc len*(t: TTable): int {.inline.} =
## returns the number of keys in `t`.
result = t.counter
proc mustRehash(length, counter: int): bool =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
proc nextTry(h, maxHash: THash): THash {.inline.} =
result = ((5 * h) + 1) and maxHash
template eq(a, b: expr): expr = a == b
proc rawGet(t: TTable, key: TKey, fullhash: THash): int =
var h = fullhash and high(t.data)
while (t.data[h].h and mask) != 0:
# If it is a deleted entry, the comparison with ``markUsed(fullhash)``
# fails, so there is no need to check for this explicitely.
if t.data[h].h == markUsed(fullhash) and eq(t.data[h].key, key): return h
h = nextTry(h, high(t.data))
result = - 1
proc `[]`*(t: TTable, key: TKey): TValue =
## retrieves the value at ``t[key]``. If `key` is not in `t`,
## `EInvalidValue` is raised.
var index = rawGet(t, key, hash(key))
if index >= 0: result = t.data[index].val
else:
var e: ref EInvalidValue
new(e)
e.msg = "invalid key: " & $key
raise e
proc hasKey*(t: TTable, key: TKey): bool =
## returns true iff `key` is in the table `t`.
result = rawGet(t, key) >= 0
proc rawInsert[TKey, TValue](
data: var seq[tuple[key: TKey, val: TValue, h: THash]],
tup: tuple[key: TKey, val: TValue, h: THash]) =
var h = tup.h and high(data)
while (data[h].h and mask) == usedSlot: h = nextTry(h, high(data))
data[h] = tup
proc enlarge(t: var TTable) =
var n: seq[tuple[key: TKey, val: TValue, h: THash]]
newSeq(n, len(t.data) * growthFactor)
for i in 0..high(t.data):
if (t.data[i].h and mask) == usedSlot: rawInsert(n, t.data[i])
swap(t.data, n)
proc `[]=`*(t: var TTable, key: TKey, val: TValue) =
## puts a (key, value)-pair into `t`.
var fullhash = hash(key)
var index = rawGet(t, key, fullhash)
if index >= 0:
t.data[index].val = val
else:
if mustRehash(len(t.data), t.counter): enlarge(t)
rawInsert(t.data, (key, val, markUsed(fullhash)))
inc(t.counter)
proc add*(t: var TTable, key: TKey, val: TValue) =
## puts a (key, value)-pair into `t`, but does not check if key already
## exists.
if mustRehash(len(t.data), t.counter): enlarge(t)
rawInsert(t.data, (key, val, markUsed(hash(key))))
inc(t.counter)
proc del*(t: var TTable, key: TKey) =
## deletes a (key, val)-pair in `t`.
var index = rawGet(t, key)
if index >= 0:
t.data[index].h = delSlot
proc delAll*(t: var TTable, key: TKey) =
## deletes all (key, val)-pairs in `t`.
while true:
var index = rawGet(t, key)
if index < 0: break
t.data[index].h = delSlot
iterator pairs*(t: TTable): tuple[key: TKey, value: TValue] =
## iterates over any (key, value) pair in the table `t`.
for h in 0..high(t.data):
if (t.data[h].h and mask) == usedSlot:
yield (t.data[h].key, t.data[h].val)
iterator keys*(t: TTable): TKey =
## iterate over any key in the table `t`. If key occurs multiple times, it
## is yielded multiple times.
for h in 0..high(t.data):
if (t.data[h].h and mask) == usedSlot:
yield t.data[h].key
iterator values*(t: TTable): TValue =
## iterate over any value in the table `t`.
for h in 0..high(t.data):
if (t.data[h].h and mask) == usedSlot:
yield t.data[h].val
iterator values*(t: TTable, key: TKey): TValue =
## iterate over any value associated with `key` in `t`.
var fullhash = hash(key)
var h = fullhash and high(t.data)
while (t.data[h].h and mask) != 0:
# If it is a deleted entry, the comparison with ``markUsed(fullhash)``
# fails, so there is no need to check for this explicitely.
if t.data[h].h == markUsed(fullhash) and eq(t.data[h].key, key):
yield t.data[h].val
h = nextTry(h, high(t.data))
proc `$`*[KeyToStr=`$`, ValueToStr=`$`](t: TTable): string =
## turns the table into its string representation. `$` must be available
## for TKey and TValue for this to work.
if t.len == 0:
result = "{:}"
else:
result = "{"
var i = 0
for k, v in pairs(t):
if i > 0: add(result, ", ")
add(result, KeyToStr(k))
add(result, ": ")
add(result, ValueToStr(v))
inc(i)
add(result, "}")

View file

@ -45,27 +45,26 @@ type
nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange, nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange,
nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr, nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr,
nnkElifExpr, nnkElseExpr, nnkLambda, nnkAccQuoted, nnkElifExpr, nnkElseExpr, nnkLambda, nnkAccQuoted,
nnkTableConstr, nnkQualified, nnkBind, nnkSymChoice, nnkTableConstr, nnkBind, nnkSymChoice, nnkHiddenStdConv,
nnkHiddenStdConv, nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast,
nnkCast, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv,
nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange,
nnkChckRange, nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray, nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray, nnkAsgn,
nnkAsgn, nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit,
nnkOfInherit, nnkModule, nnkProcDef, nnkMethodDef, nnkModule, nnkProcDef, nnkMethodDef, nnkConverterDef,
nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch,
nnkOfBranch, nnkElifBranch, nnkExceptBranch, nnkElse, nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt,
nnkMacroStmt, nnkAsmStmt, nnkPragma, nnkIfStmt, nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt,
nnkWhenStmt, nnkForStmt, nnkWhileStmt, nnkCaseStmt, nnkForStmt, nnkWhileStmt, nnkCaseStmt, nnkVarSection,
nnkVarSection, nnkConstSection, nnkConstDef, nnkTypeSection, nnkConstSection, nnkConstDef, nnkTypeSection, nnkTypeDef,
nnkTypeDef, nnkYieldStmt, nnkTryStmt, nnkFinally, nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt,
nnkBlockStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
nnkFromStmt, nnkIncludeStmt, nnkCommentStmt, nnkStmtListExpr, nnkIncludeStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
nnkObjectTy, nnkTupleTy, nnkRecList, nnkRecCase, nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
nnkRecWhen, nnkRefTy, nnkPtrTy, nnkVarTy, nnkRefTy, nnkPtrTy, nnkVarTy, nnkDistinctTy,
nnkDistinctTy, nnkProcTy, nnkEnumTy, nnkEnumFieldDef, nnkProcTy, nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
nnkReturnToken
TNimNodeKinds* = set[TNimrodNodeKind] TNimNodeKinds* = set[TNimrodNodeKind]
TNimrodTypeKind* = enum TNimrodTypeKind* = enum
ntyNone, ntyBool, ntyChar, ntyEmpty, ntyNone, ntyBool, ntyChar, ntyEmpty,
@ -90,7 +89,7 @@ type
#[[[end]]] #[[[end]]]
type type
TNimrodIdent = object of TObject TNimrodIdent* = object of TObject
## represents a Nimrod identifier in the AST ## represents a Nimrod identifier in the AST
TNimrodSymbol {.final.} = object # hidden TNimrodSymbol {.final.} = object # hidden
@ -134,7 +133,7 @@ proc add*(father, child: PNimrodNode) {.magic: "NAdd".}
proc add*(father: PNimrodNode, children: openArray[PNimrodNode]) {. proc add*(father: PNimrodNode, children: openArray[PNimrodNode]) {.
magic: "NAddMultiple".} magic: "NAddMultiple".}
## adds each `children` to the `father` node ## adds each child of `children` to the `father` node
proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".} proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
## deletes `n` children of `father` starting at index `idx`. ## deletes `n` children of `father` starting at index `idx`.

View file

@ -790,7 +790,7 @@ iterator walkFiles*(pattern: string): string =
if res != -1: if res != -1:
while true: while true:
if f.cFileName[0] != '.': if f.cFileName[0] != '.':
yield extractDir(pattern) / extractFilename($f.cFileName) yield splitFile(pattern).dir / extractFilename($f.cFileName)
if findnextFileA(res, f) == 0'i32: break if findnextFileA(res, f) == 0'i32: break
findclose(res) findclose(res)
else: # here we use glob else: # here we use glob
@ -811,8 +811,12 @@ type
TPathComponent* = enum ## Enumeration specifying a path component. TPathComponent* = enum ## Enumeration specifying a path component.
pcFile, ## path refers to a file pcFile, ## path refers to a file
pcLinkToFile, ## path refers to a symbolic link to a file pcLinkToFile, ## path refers to a symbolic link to a file
pcDirectory, ## path refers to a directory pcDir, ## path refers to a directory
pcLinkToDirectory ## path refers to a symbolic link to a directory pcLinkToDir ## path refers to a symbolic link to a directory
const
pcDirectory* {.deprecated.} = pcDir ## deprecated alias
pcLinkToDirectory* {.deprecated.} = pcLinkToDir ## deprecated alias
iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] = iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
## walks over the directory `dir` and yields for each directory or file in ## walks over the directory `dir` and yields for each directory or file in
@ -843,7 +847,7 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
var k = pcFile var k = pcFile
if f.cFilename[0] != '.': if f.cFilename[0] != '.':
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32: if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
k = pcDirectory k = pcDir
yield (k, dir / extractFilename($f.cFilename)) yield (k, dir / extractFilename($f.cFilename))
if findnextFileA(h, f) == 0'i32: break if findnextFileA(h, f) == 0'i32: break
findclose(h) findclose(h)
@ -859,11 +863,33 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
y = dir / y y = dir / y
if stat(y, s) < 0'i32: break if stat(y, s) < 0'i32: break
var k = pcFile var k = pcFile
if S_ISDIR(s.st_mode): k = pcDirectory if S_ISDIR(s.st_mode): k = pcDir
if S_ISLNK(s.st_mode): k = succ(k) if S_ISLNK(s.st_mode): k = succ(k)
yield (k, y) yield (k, y)
discard closeDir(d) discard closeDir(d)
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string =
## walks over the directory `dir` and yields for each file in `dir`. The
## full path for each file is returned.
## Walking is recursive. `filter` controls the behaviour of the iterator:
##
## --------------------- ---------------------------------------------
## filter meaning
## --------------------- ---------------------------------------------
## ``pcFile`` yield real files
## ``pcLinkToFile`` yield symbol links to files
## ``pcDir`` follow real directories
## ``pcLinkToDir`` follow symbol links to directories
## --------------------- ---------------------------------------------
##
var stack = @[dir]
while stack.len > 0:
for k,p in walkDir(stack.pop()):
if k in filter:
case k
of pcFile, pcLinkToFile: yield p
of pcDir, pcLinkToDir: stack.add(p)
proc rawRemoveDir(dir: string) = proc rawRemoveDir(dir: string) =
when defined(windows): when defined(windows):
if RemoveDirectoryA(dir) == 0'i32: OSError() if RemoveDirectoryA(dir) == 0'i32: OSError()
@ -871,12 +897,12 @@ proc rawRemoveDir(dir: string) =
if rmdir(dir) != 0'i32: OSError() if rmdir(dir) != 0'i32: OSError()
proc removeDir*(dir: string) = proc removeDir*(dir: string) =
## Removes the directory `dir` including all subdirectories or files ## Removes the directory `dir` including all subdirectories and files
## in `dir` (recursively). If this fails, `EOS` is raised. ## in `dir` (recursively). If this fails, `EOS` is raised.
for kind, path in walkDir(dir): for kind, path in walkDir(dir):
case kind case kind
of pcFile, pcLinkToFile, pcLinkToDirectory: removeFile(path) of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
of pcDirectory: removeDir(dir) of pcDir: removeDir(dir)
rawRemoveDir(dir) rawRemoveDir(dir)
proc rawCreateDir(dir: string) = proc rawCreateDir(dir: string) =
@ -935,7 +961,7 @@ type
fpOthersRead ## read access for others fpOthersRead ## read access for others
proc getFilePermissions*(filename: string): set[TFilePermission] = proc getFilePermissions*(filename: string): set[TFilePermission] =
## retrives file permissions for `filename`. `OSError` is raised in case of ## retrieves file permissions for `filename`. `OSError` is raised in case of
## an error. On Windows, only the ``readonly`` flag is checked, every other ## an error. On Windows, only the ``readonly`` flag is checked, every other
## permission is available in any case. ## permission is available in any case.
when defined(posix): when defined(posix):
@ -1103,4 +1129,14 @@ proc getApplicationDir*(): string =
## Returns the directory of the application's executable. ## Returns the directory of the application's executable.
result = splitFile(getApplicationFilename()).dir result = splitFile(getApplicationFilename()).dir
proc sleep*(milsecs: int) =
## sleeps `milsecs` milliseconds.
when defined(windows):
winlean.sleep(int32(milsecs))
else:
var a, b: Ttimespec
a.tv_sec = TTime(milsecs div 1000)
a.tv_nsec = (milsecs mod 1000) * 1000
discard posix.nanosleep(a, b)
{.pop.} {.pop.}

View file

@ -9,19 +9,19 @@
## This module implements an advanced facility for executing OS processes ## This module implements an advanced facility for executing OS processes
## and process communication. ## and process communication.
## **On Windows this module does not work properly. Please help!**
import import
os, strtabs, streams strutils, os, strtabs, streams
when defined(windows): when defined(windows):
import winlean import winlean
else:
import posix
type type
TProcess = object of TObject TProcess = object of TObject
when defined(windows): when defined(windows):
FProcessHandle: Thandle FProcessHandle: Thandle
FThreadHandle: Thandle
inputHandle, outputHandle, errorHandle: TFileHandle inputHandle, outputHandle, errorHandle: TFileHandle
else: else:
inputHandle, outputHandle, errorHandle: TFileHandle inputHandle, outputHandle, errorHandle: TFileHandle
@ -31,10 +31,11 @@ type
PProcess* = ref TProcess ## represents an operating system process PProcess* = ref TProcess ## represents an operating system process
TProcessOption* = enum ## options that can be passed `startProcess` TProcessOption* = enum ## options that can be passed `startProcess`
poNone, ## none option poEchoCmd, ## echo the command before execution
poUseShell, ## use the shell to execute the command; NOTE: This poUseShell, ## use the shell to execute the command; NOTE: This
## often creates a security whole! ## often creates a security whole!
poStdErrToStdOut ## merge stdout and stderr to the stdout stream poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
poParentStreams ## use the parent's streams
proc execProcess*(command: string, proc execProcess*(command: string,
options: set[TProcessOption] = {poStdErrToStdOut, options: set[TProcessOption] = {poStdErrToStdOut,
@ -77,18 +78,6 @@ proc startProcess*(command: string,
## Return value: The newly created process object. Nil is never returned, ## Return value: The newly created process object. Nil is never returned,
## but ``EOS`` is raised in case of an error. ## but ``EOS`` is raised in case of an error.
when true:
nil
else:
proc startGUIProcess*(command: string,
workingDir: string = "",
args: openarray[string] = [],
env: PStringTable = nil,
x = -1,
y = -1,
width = -1,
height = -1): PProcess
proc suspend*(p: PProcess) proc suspend*(p: PProcess)
## Suspends the process `p`. ## Suspends the process `p`.
@ -117,13 +106,108 @@ proc outputStream*(p: PProcess): PStream
proc errorStream*(p: PProcess): PStream proc errorStream*(p: PProcess): PStream
## returns ``p``'s output stream for reading from ## returns ``p``'s output stream for reading from
when defined(macosx) or defined(bsd):
const
CTL_HW = 6
HW_AVAILCPU = 25
HW_NCPU = 3
proc sysctl(x: ptr array[0..3, cint], y: cint, z: pointer,
a: var int, b: pointer, c: int): cint {.
importc: "sysctl", header: "<sys/sysctl.h>".}
proc countProcessors*(): int =
## returns the numer of the processors/cores the machine has.
## Returns 0 if it cannot be determined.
when defined(windows):
var x = getenv("NUMBER_OF_PROCESSORS")
if x.len > 0: result = parseInt(x)
elif defined(macosx) or defined(bsd):
var
mib: array[0..3, cint]
len, numCPU: int
mib[0] = CTL_HW
mib[1] = HW_AVAILCPU
len = sizeof(numCPU)
discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
if numCPU < 1:
mib[1] = HW_NCPU
discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
result = numCPU
elif defined(hpux):
result = mpctl(MPC_GETNUMSPUS, nil, nil)
elif defined(irix):
var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
result = sysconf(SC_NPROC_ONLN)
else:
result = sysconf(SC_NPROCESSORS_ONLN)
if result <= 0: result = 1
proc startProcessAux(cmd: string, options: set[TProcessOption]): PProcess =
var c = parseCmdLine(cmd)
var a: seq[string] = @[] # slicing is not yet implemented :-(
for i in 1 .. c.len-1: add(a, c[i])
result = startProcess(command=c[0], args=a, options=options)
proc execProcesses*(cmds: openArray[string],
options = {poStdErrToStdOut, poParentStreams},
n = countProcessors()): int =
## executes the commands `cmds` in parallel. Creates `n` processes
## that execute in parallel. The highest return value of all processes
## is returned.
assert n > 0
if n > 1:
var q: seq[PProcess]
newSeq(q, n)
var m = min(n, cmds.len)
for i in 0..m-1:
q[i] = startProcessAux(cmds[i], options=options)
when defined(noBusyWaiting):
var r = 0
for i in m..high(cmds):
when defined(debugExecProcesses):
var err = ""
var outp = outputStream(q[r])
while running(q[r]) or not outp.atEnd(outp):
err.add(outp.readLine())
err.add("\n")
echo(err)
result = max(waitForExit(q[r]), result)
q[r] = startProcessAux(cmds[i], options=options)
r = (r + 1) mod n
else:
var i = m
while i <= high(cmds):
sleep(50)
for r in 0..n-1:
if not running(q[r]):
#echo(outputStream(q[r]).readLine())
result = max(waitForExit(q[r]), result)
q[r] = startProcessAux(cmds[i], options=options)
inc(i)
if i > high(cmds): break
for i in 0..m-1:
result = max(waitForExit(q[i]), result)
else:
for i in 0..high(cmds):
var p = startProcessAux(cmds[i], options=options)
result = max(waitForExit(p), result)
when true:
nil
else:
proc startGUIProcess*(command: string,
workingDir: string = "",
args: openarray[string] = [],
env: PStringTable = nil,
x = -1,
y = -1,
width = -1,
height = -1): PProcess
proc execProcess(command: string, proc execProcess(command: string,
options: set[TProcessOption] = {poStdErrToStdOut, options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): string = poUseShell}): string =
var c = parseCmdLine(command) var p = startProcessAux(command, options=options)
var a: seq[string] = @[] # slicing is not yet implemented :-(
for i in 1 .. c.len-1: add(a, c[i])
var p = startProcess(command=c[0], args=a, options=options)
var outp = outputStream(p) var outp = outputStream(p)
result = "" result = ""
while running(p) or not outp.atEnd(outp): while running(p) or not outp.atEnd(outp):
@ -147,14 +231,18 @@ when defined(Windows):
atTheEnd: bool atTheEnd: bool
proc hsClose(s: PFileHandleStream) = nil # nothing to do here proc hsClose(s: PFileHandleStream) = nil # nothing to do here
proc hsAtEnd(s: PFileHandleStream): bool = return true proc hsAtEnd(s: PFileHandleStream): bool = return s.atTheEnd
proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int = proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int =
if s.atTheEnd: return 0
var br: int32 var br: int32
var a = winlean.ReadFile(s.handle, buffer, bufLen, br, nil) var a = winlean.ReadFile(s.handle, buffer, bufLen, br, nil)
if a == 0: OSError() # TRUE and zero bytes returned (EOF).
# TRUE and n (>0) bytes returned (good data).
# FALSE and bytes returned undefined (system error).
if a == 0 and br != 0: OSError()
s.atTheEnd = br < bufLen
result = br result = br
#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
@ -170,16 +258,12 @@ when defined(Windows):
result.writeData = hsWriteData result.writeData = hsWriteData
proc buildCommandLine(a: string, args: openarray[string]): cstring = proc buildCommandLine(a: string, args: openarray[string]): cstring =
var L = a.len var res = quoteIfContainsWhite(a)
for i in 0..high(args): inc(L, args[i].len+1)
result = cast[cstring](alloc0(L+1))
copyMem(result, cstring(a), a.len)
L = a.len
for i in 0..high(args): for i in 0..high(args):
result[L] = ' ' res.add(' ')
inc(L) res.add(quoteIfContainsWhite(args[i]))
copyMem(addr(result[L]), cstring(args[i]), args[i].len) result = cast[cstring](alloc0(res.len+1))
inc(L, args[i].len) copyMem(result, cstring(res), res.len)
proc buildEnv(env: PStringTable): cstring = proc buildEnv(env: PStringTable): cstring =
var L = 0 var L = 0
@ -198,27 +282,31 @@ 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: var THandle) = proc CreatePipeHandles(Rdhandle, WrHandle: 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, piInheritablePipe, 0) == 0'i32: if CreatePipe(Rdhandle, Wrhandle, piInheritablePipe, 1024) == 0'i32:
OSError() OSError()
proc startProcess*(command: string, proc fileClose(h: THandle) {.inline.} =
if h > 4: discard CloseHandle(h)
proc startProcess(command: string,
workingDir: string = "", workingDir: string = "",
args: openarray[string] = [], args: openarray[string] = [],
env: PStringTable = nil, env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut}): PProcess = options: set[TProcessOption] = {poStdErrToStdOut}): PProcess =
new(result)
var var
SI: TStartupInfo SI: TStartupInfo
ProcInfo: TProcessInformation ProcInfo: TProcessInformation
success: int success: int
hi, ho, he: THandle hi, ho, he: THandle
new(result)
SI.cb = SizeOf(SI) SI.cb = SizeOf(SI)
SI.dwFlags = STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES if poParentStreams notin options:
SI.dwFlags = STARTF_USESTDHANDLES # STARTF_USESHOWWINDOW or
CreatePipeHandles(SI.hStdInput, HI) CreatePipeHandles(SI.hStdInput, HI)
CreatePipeHandles(HO, Si.hStdOutput) CreatePipeHandles(HO, Si.hStdOutput)
if poStdErrToStdOut in options: if poStdErrToStdOut in options:
@ -229,41 +317,46 @@ when defined(Windows):
result.inputHandle = hi result.inputHandle = hi
result.outputHandle = ho result.outputHandle = ho
result.errorHandle = he result.errorHandle = he
else:
SI.hStdError = GetStdHandle(STD_ERROR_HANDLE)
SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE)
SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
result.inputHandle = si.hStdInput
result.outputHandle = si.hStdOutput
result.errorHandle = si.hStdError
var cmdl: cstring var cmdl: cstring
if poUseShell in options: if false: # poUseShell in options:
var comspec = getEnv("COMSPEC") cmdl = buildCommandLine(getEnv("COMSPEC"), @["/c", command] & args)
var a: seq[string] = @[]
add(a, "/c")
add(a, command)
add(a, args)
cmdl = buildCommandLine(comspec, a)
else: else:
cmdl = buildCommandLine(command, args) cmdl = buildCommandLine(command, args)
var wd: cstring = nil var wd: cstring = nil
var e: cstring = nil
if len(workingDir) > 0: wd = workingDir if len(workingDir) > 0: wd = workingDir
if env == nil: if env != nil: e = buildEnv(env)
if poEchoCmd in options: echo($cmdl)
success = winlean.CreateProcess(nil, success = winlean.CreateProcess(nil,
cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, nil, wd, SI, ProcInfo) cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
else:
var e = buildEnv(env) if poParentStreams notin options:
success = winlean.CreateProcess(nil, FileClose(si.hStdInput)
cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo) FileClose(si.hStdOutput)
dealloc(e) if poStdErrToStdOut notin options:
FileClose(si.hStdError)
if e != nil: dealloc(e)
dealloc(cmdl) dealloc(cmdl)
if success == 0: if success == 0: OSError()
OSError() # Close the handle now so anyone waiting is woken:
# NEW:
# Close the handles now so anyone waiting is woken.
discard closeHandle(procInfo.hThread) discard closeHandle(procInfo.hThread)
result.FProcessHandle = procInfo.hProcess result.FProcessHandle = procInfo.hProcess
result.FThreadHandle = procInfo.hThread
result.id = procInfo.dwProcessID result.id = procInfo.dwProcessID
proc suspend(p: PProcess) = proc suspend(p: PProcess) =
discard SuspendThread(p.FThreadHandle) discard SuspendThread(p.FProcessHandle)
proc resume(p: PProcess) = proc resume(p: PProcess) =
discard ResumeThread(p.FThreadHandle) discard ResumeThread(p.FProcessHandle)
proc running(p: PProcess): bool = proc running(p: PProcess): bool =
var x = waitForSingleObject(p.FProcessHandle, 50) var x = waitForSingleObject(p.FProcessHandle, 50)
@ -274,7 +367,6 @@ when defined(Windows):
discard TerminateProcess(p.FProcessHandle, 0) discard TerminateProcess(p.FProcessHandle, 0)
proc waitForExit(p: PProcess): int = proc waitForExit(p: PProcess): int =
#CloseHandle(p.FThreadHandle)
discard WaitForSingleObject(p.FProcessHandle, Infinite) discard WaitForSingleObject(p.FProcessHandle, Infinite)
var res: int32 var res: int32
discard GetExitCodeProcess(p.FProcessHandle, res) discard GetExitCodeProcess(p.FProcessHandle, res)
@ -314,17 +406,15 @@ when defined(Windows):
discard CloseHandle(Process) discard CloseHandle(Process)
else: else:
import posix
const const
readIdx = 0 readIdx = 0
writeIdx = 1 writeIdx = 1
proc addCmdArgs(command: string, args: openarray[string]): string = proc addCmdArgs(command: string, args: openarray[string]): string =
result = command result = quoteIfContainsWhite(command)
for i in 0 .. high(args): for i in 0 .. high(args):
add(result, " ") add(result, " ")
add(result, args[i]) add(result, quoteIfContainsWhite(args[i]))
proc toCStringArray(b, a: openarray[string]): cstringArray = proc toCStringArray(b, a: openarray[string]): cstringArray =
result = cast[cstringArray](alloc0((a.len + b.len + 1) * sizeof(cstring))) result = cast[cstringArray](alloc0((a.len + b.len + 1) * sizeof(cstring)))
@ -344,14 +434,15 @@ else:
copyMem(result[i], addr(x[0]), x.len+1) copyMem(result[i], addr(x[0]), x.len+1)
inc(i) inc(i)
proc startProcess*(command: string, proc startProcess(command: string,
workingDir: string = "", workingDir: string = "",
args: openarray[string] = [], args: openarray[string] = [],
env: PStringTable = nil, env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut}): PProcess = options: set[TProcessOption] = {poStdErrToStdOut}): PProcess =
new(result)
var var
p_stdin, p_stdout, p_stderr: array [0..1, cint] p_stdin, p_stdout, p_stderr: array [0..1, cint]
new(result)
result.exitCode = 3 # for ``waitForExit``
if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32: if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32:
OSError("failed to create a pipe") OSError("failed to create a pipe")
var Pid = fork() var Pid = fork()
@ -389,6 +480,8 @@ else:
# too risky to raise an exception here: # too risky to raise an exception here:
quit("execve call failed: " & $strerror(errno)) quit("execve call failed: " & $strerror(errno))
# Parent process. Copy process information. # Parent process. Copy process information.
if poEchoCmd in options:
echo(command & " " & join(args, " "))
result.id = pid result.id = pid
result.inputHandle = p_stdin[writeIdx] result.inputHandle = p_stdin[writeIdx]
@ -415,9 +508,15 @@ 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): # ``waitPid`` fails if the process is not running anymore. But then
result = p.exitCode # ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is
# initialized with 3, wrong success exit codes are prevented.
var oldExitCode = p.exitCode
if waitPid(p.id, p.exitCode, 0) < 0:
# failed, so restore old exitCode
p.exitCode = oldExitCode
result = int(p.exitCode)
proc inputStream(p: PProcess): PStream = proc inputStream(p: PProcess): PStream =
var f: TFile var f: TFile
@ -440,4 +539,5 @@ else:
result = csystem(command) result = csystem(command)
when isMainModule: when isMainModule:
echo execCmd("gcc -v") var x = execProcess("gcc -v")
echo "ECHO ", x

1320
lib/pure/pegs.nim Executable file

File diff suppressed because it is too large Load diff

353
lib/pure/re.nim Executable file
View file

@ -0,0 +1,353 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Regular expression support for Nimrod. Consider using the pegs module
## instead.
{.compile: "tre/tre_all.c".}
from strutils import addf
type
TRegExDesc {.pure, final.} = object
re_nsub: int # Number of parenthesized subexpressions.
value: pointer # For internal use only.
TRegEx* = ref TRegExDesc ## a compiled regular expression
EInvalidRegEx* = object of EInvalidValue
## is raised if the pattern is no valid regular expression.
TRegMatch {.pure.} = object
so, eo: cint
const
MaxSubpatterns* = 10
## defines the maximum number of subpatterns that can be captured.
## More subpatterns cannot be captured!
proc regnexec(preg: ptr TRegExDesc, s: cstring, len, nmatch: int,
pmatch: ptr array [0..maxSubpatterns-1, TRegMatch],
eflags: cint): cint {.importc.}
proc regncomp(preg: ptr TRegExDesc, regex: cstring, n: int,
cflags: cint): cint {.importc.}
proc regfree(preg: ptr TRegExDesc) {.importc.}
const
# POSIX regcomp() flags
REG_EXTENDED = 1
REG_ICASE = (REG_EXTENDED shl 1)
REG_NEWLINE = (REG_ICASE shl 1)
REG_NOSUB = (REG_NEWLINE shl 1)
# Extra regcomp() flags
REG_BASIC = 0
REG_LITERAL = (REG_NOSUB shl 1)
REG_RIGHT_ASSOC = (REG_LITERAL shl 1)
REG_UNGREEDY = (REG_RIGHT_ASSOC shl 1)
# POSIX regexec() flags
REG_NOTBOL = 1
REG_NOTEOL = (REG_NOTBOL shl 1)
# Extra regexec() flags
REG_APPROX_MATCHER = (REG_NOTEOL shl 1)
REG_BACKTRACKING_MATCHER = (REG_APPROX_MATCHER shl 1)
ErrorMessages = [
"No error",
"No match",
"Invalid regexp",
"Unknown collating element",
"Unknown character class name",
"Trailing backslash",
"Invalid back reference",
"Missing ']'",
"Missing ')'",
"Missing '}'",
"Invalid contents of {}",
"Invalid character range",
"Out of memory",
"Invalid use of repetition operators"
]
proc finalizeRegEx(x: TRegEx) = regfree(addr(x^))
proc re*(s: string): TRegEx =
## Constructor of regular expressions. Note that Nimrod's
## extended raw string literals supports this syntax ``re"[abc]"`` as
## a short form for ``re(r"[abc]")``.
new(result, finalizeRegEx)
var err = int(regncomp(addr(result^), s, s.len,
cint(REG_EXTENDED or REG_NEWLINE)))
if err != 0:
var e: ref EInvalidRegEx
new(e)
e.msg = ErrorMessages[err]
raise e
proc xre*(pattern: string): TRegEx =
## deletes whitespace from a pattern that is not escaped or in a character
## class. Then it constructs a regular expresion object via `re`.
## This is modelled after Perl's ``/x`` modifier.
var p = ""
var i = 0
while i < pattern.len:
case pattern[i]
of ' ', '\t':
inc i
of '\\':
add p, '\\'
add p, pattern[i+1]
inc i, 2
of '[':
while pattern[i] != ']' and pattern[i] != '\0':
add p, pattern[i]
inc i
else:
add p, pattern[i]
inc i
result = re(p)
proc rawmatch(s: string, pattern: TRegEx, matches: var openarray[string],
start: int): tuple[first, last: int] =
var
rawMatches: array [0..maxSubpatterns-1, TRegMatch]
cs = cstring(s)
res = int(regnexec(addr(pattern^), cast[cstring](addr(cs[start])),
s.len-start, maxSubpatterns, addr(rawMatches), cint(0)))
if res == 0:
for i in 0..min(matches.len, int(pattern.re_nsub))-1:
var
a = int(rawMatches[i].so)
b = int(rawMatches[i].eo)
echo "a: ", a, " b: ", b
if a >= 0 and b >= 0:
matches[i] = copy(s, a+start, b - 1 + start)
else:
matches[i] = ""
return (int(rawMatches[0].so), int(rawMatches[0].eo)-1)
return (-1, -1)
proc match*(s: string, pattern: TRegEx, matches: var openarray[string],
start = 0): bool =
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
## the captured substrings in the array ``matches``. If it does not
## match, nothing is written into ``matches`` and ``false`` is
## returned.
result = rawmatch(s, pattern, matches, start).first == 0
proc match*(s: string, pattern: TRegEx, start: int = 0): bool =
## returns ``true`` if ``s`` matches the ``pattern`` beginning
## from ``start``.
var matches: array [0..0, string]
result = rawmatch(s, pattern, matches, start).first == 0
proc matchLen*(s: string, pattern: TRegEx, matches: var openarray[string],
start = 0): int =
## the same as ``match``, but it returns the length of the match,
## if there is no match, -1 is returned. Note that a match length
## of zero can happen.
var (a, b) = rawmatch(s, pattern, matches, start)
result = a - b + 1
proc matchLen*(s: string, pattern: TRegEx, start = 0): int =
## the same as ``match``, but it returns the length of the match,
## if there is no match, -1 is returned. Note that a match length
## of zero can happen.
var matches: array [0..0, string]
var (a, b) = rawmatch(s, pattern, matches, start)
result = a - b + 1
proc find*(s: string, pattern: TRegEx, matches: var openarray[string],
start = 0): int =
## returns ``true`` if ``pattern`` occurs in ``s`` and the captured
## substrings in the array ``matches``. If it does not match, nothing
## is written into ``matches``.
result = rawmatch(s, pattern, matches, start).first
if result >= 0: inc(result, start)
proc find*(s: string, pattern: TRegEx, start = 0): int =
## returns ``true`` if ``pattern`` occurs in ``s``.
var matches: array [0..0, string]
result = rawmatch(s, pattern, matches, start).first
if result >= 0: inc(result, start)
template `=~`*(s: string, pattern: TRegEx): expr =
## This calls ``match`` with an implicit declared ``matches`` array that
## can be used in the scope of the ``=~`` call:
##
## .. code-block:: nimrod
##
## if line =~ r"\s*(\w+)\s*\=\s*(\w+)":
## # matches a key=value pair:
## echo("Key: ", matches[1])
## echo("Value: ", matches[2])
## elif line =~ r"\s*(\#.*)":
## # matches a comment
## # note that the implicit ``matches`` array is different from the
## # ``matches`` array of the first branch
## echo("comment: ", matches[1])
## else:
## echo("syntax error")
##
when not definedInScope(matches):
var matches: array[0..maxSubPatterns-1, string]
match(s, pattern, matches)
# ------------------------- more string handling ------------------------------
proc contains*(s: string, pattern: TRegEx, start = 0): bool =
## same as ``find(s, pattern, start) >= 0``
return find(s, pattern, start) >= 0
proc contains*(s: string, pattern: TRegEx, matches: var openArray[string],
start = 0): bool =
## same as ``find(s, pattern, matches, start) >= 0``
return find(s, pattern, matches, start) >= 0
proc startsWith*(s: string, prefix: TRegEx): bool =
## returns true if `s` starts with the pattern `prefix`
result = matchLen(s, prefix) >= 0
proc endsWith*(s: string, suffix: TRegEx): bool =
## returns true if `s` ends with the pattern `prefix`
for i in 0 .. s.len-1:
if matchLen(s, suffix, i) == s.len - i: return true
proc replace*(s: string, sub: TRegEx, by: string): string =
## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
##
## .. code-block:: nimrod
## "var1=key; var2=key2".replace(p"{\ident}'='{\ident}", "$1<-$2$2")
##
## Results in:
##
## .. code-block:: nimrod
##
## "var1<-keykey; val2<-key2key2"
result = ""
var i = 0
var caps: array[0..maxSubpatterns-1, string]
while i < s.len:
var x = matchLen(s, sub, caps, i)
if x <= 0:
add(result, s[i])
inc(i)
else:
addf(result, by, caps)
inc(i, x)
# copy the rest:
add(result, copy(s, i))
proc parallelReplace*(s: string, subs: openArray[
tuple[pattern: TRegEx, repl: string]]): string =
## Returns a modified copy of `s` with the substitutions in `subs`
## applied in parallel.
result = ""
var i = 0
var caps: array[0..maxSubpatterns-1, string]
while i < s.len:
block searchSubs:
for j in 0..high(subs):
var x = matchLen(s, subs[j][0], caps, i)
if x > 0:
addf(result, subs[j][1], caps)
inc(i, x)
break searchSubs
add(result, s[i])
inc(i)
# copy the rest:
add(result, copy(s, i))
proc transformFile*(infile, outfile: string,
subs: openArray[tuple[pattern: TRegEx, repl: string]]) =
## reads in the file `infile`, performs a parallel replacement (calls
## `parallelReplace`) and writes back to `outfile`. Calls ``quit`` if an
## error occurs. This is supposed to be used for quick scripting.
var x = readFile(infile)
if not isNil(x):
var f: TFile
if open(f, outfile, fmWrite):
write(f, x.parallelReplace(subs))
close(f)
else:
quit("cannot open for writing: " & outfile)
else:
quit("cannot open for reading: " & infile)
iterator split*(s: string, sep: TRegEx): string =
## Splits the string `s` into substrings.
##
## Substrings are separated by the regular expression `sep`.
## Examples:
##
## .. code-block:: nimrod
## for word in split("00232this02939is39an22example111", re"\d+"):
## writeln(stdout, word)
##
## Results in:
##
## .. code-block:: nimrod
## "this"
## "is"
## "an"
## "example"
##
var
first = 0
last = 0
while last < len(s):
var x = matchLen(s, sep, last)
if x > 0: inc(last, x)
first = last
while last < len(s):
inc(last)
x = matchLen(s, sep, last)
if x > 0: break
if first < last:
yield copy(s, first, last-1)
proc split*(s: string, sep: TRegEx): seq[string] =
## Splits the string `s` into substrings.
accumulateResult(split(s, sep))
const ## common regular expressions
reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b" ## describes an identifier
reNatural* = r"\b\d+\b" ## describes a natural number
reInteger* = r"\b[-+]?\d+\b" ## describes an integer
reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number
reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101)
reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777)
reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
## describes a floating point number
reEmail* = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)" &
r"*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+(?:[a-zA-Z]{2}|com|org|" &
r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b"
## describes a common email address
reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" &
r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
## describes an URL
when isMainModule:
echo matchLen("key", re"[a-zA-Z_][a-zA-Z_0-9]*")
var pattern = re"[a-zA-Z_][a-zA-Z_0-9]*\s*=\s*[a-zA-Z_][a-zA-Z_0-9]*"
echo matchLen("key1= cal9", pattern, 2)
echo find("_____abc_______", re("abc"), 3)
#echo "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
#echo "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
if "abc" =~ re"(a)bc xyz|([a-z]+)":
echo matches[0]
else:
echo "BUG"
# for word in split("00232this02939is39an22example111", peg"\d+"):
# writeln(stdout, word)

View file

@ -8,9 +8,6 @@
# #
## Regular expression support for Nimrod. ## Regular expression support for Nimrod.
## **Deprecated** since version 0.8.2. Use the module `re` instead.
{.deprecated.}
## Currently this module is implemented by providing a wrapper around the ## Currently this module is implemented by providing a wrapper around the
## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_ ## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
## C library. This means that your application will depend on the PRCE ## C library. This means that your application will depend on the PRCE

View file

@ -48,7 +48,8 @@ proc read[T](s: PStream, result: var T) =
proc readChar*(s: PStream): char = proc readChar*(s: PStream): char =
## reads a char from the stream `s`. Raises `EIO` if an error occured. ## reads a char from the stream `s`. Raises `EIO` if an error occured.
read(s, result) ## Returns '\0' as an EOF marker.
discard s.readData(s, addr(result), sizeof(result))
proc readBool*(s: PStream): bool = proc readBool*(s: PStream): bool =
## reads a bool from the stream `s`. Raises `EIO` if an error occured. ## reads a bool from the stream `s`. Raises `EIO` if an error occured.
@ -94,7 +95,7 @@ proc readLine*(s: PStream): string =
if c == '\c': if c == '\c':
c = readChar(s) c = readChar(s)
break break
elif c == '\L': break elif c == '\L' or c == '\0': break
result.add(c) result.add(c)
type type

View file

@ -45,10 +45,6 @@ const
IdentStartChars* = {'a'..'z', 'A'..'Z', '_'} IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
## the set of characters an identifier can start with ## the set of characters an identifier can start with
# strStart* = 0 ## this is only for bootstraping
# ## XXX: remove this someday
# nl* = "\n" ## this is only for bootstraping XXX: remove this someday
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.} proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
## The `substitution`:idx: operator performs string substitutions in ## The `substitution`:idx: operator performs string substitutions in
## `formatstr` and returns a modified `formatstr`. This is often called ## `formatstr` and returns a modified `formatstr`. This is often called
@ -98,51 +94,30 @@ proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect.}
## If `leading` is true, leading whitespace is stripped. ## If `leading` is true, leading whitespace is stripped.
## If `trailing` is true, trailing whitespace is stripped. ## If `trailing` is true, trailing whitespace is stripped.
proc toLower*(s: string): string {.noSideEffect.} proc toLower*(s: string): string {.noSideEffect, procvar.}
## Converts `s` into lower case. This works only for the letters A-Z. ## Converts `s` into lower case. This works only for the letters A-Z.
## See `unicode.toLower` for a version that works for any Unicode character. ## See `unicode.toLower` for a version that works for any Unicode character.
proc toLower*(c: Char): Char {.noSideEffect.} proc toLower*(c: Char): Char {.noSideEffect, procvar.}
## Converts `c` into lower case. This works only for the letters A-Z. ## Converts `c` into lower case. This works only for the letters A-Z.
## See `unicode.toLower` for a version that works for any Unicode character. ## See `unicode.toLower` for a version that works for any Unicode character.
proc toUpper*(s: string): string {.noSideEffect.} proc toUpper*(s: string): string {.noSideEffect, procvar.}
## Converts `s` into upper case. This works only for the letters a-z. ## Converts `s` into upper case. This works only for the letters a-z.
## See `unicode.toUpper` for a version that works for any Unicode character. ## See `unicode.toUpper` for a version that works for any Unicode character.
proc toUpper*(c: Char): Char {.noSideEffect.} proc toUpper*(c: Char): Char {.noSideEffect, procvar.}
## Converts `c` into upper case. This works only for the letters a-z. ## Converts `c` into upper case. This works only for the letters a-z.
## See `unicode.toUpper` for a version that works for any Unicode character. ## See `unicode.toUpper` for a version that works for any Unicode character.
proc capitalize*(s: string): string {.noSideEffect.} proc capitalize*(s: string): string {.noSideEffect, procvar.}
## Converts the first character of `s` into upper case. ## Converts the first character of `s` into upper case.
## This works only for the letters a-z. ## This works only for the letters a-z.
proc normalize*(s: string): string {.noSideEffect.} proc normalize*(s: string): string {.noSideEffect, procvar.}
## Normalizes the string `s`. That means to convert it to lower case and ## Normalizes the string `s`. That means to convert it to lower case and
## remove any '_'. This is needed for Nimrod identifiers for example. ## remove any '_'. This is needed for Nimrod identifiers for example.
proc findSubStr*(sub, s: string, start: int = 0): int {.
noSideEffect, deprecated.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
## this has a different parameter order.
proc findSubStr*(sub: char, s: string, start: int = 0): int {.
noSideEffect, deprecated.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
## this has a different parameter order.
proc findChars*(chars: set[char], s: string, start: int = 0): int {.
noSideEffect, deprecated.}
## Searches for `chars` in `s` starting at position `start`. If `s` contains
## none of the characters in `chars`, -1 is returned.
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
## this has a different parameter order.
proc find*(s, sub: string, start: int = 0): int {.noSideEffect.} proc find*(s, sub: string, start: int = 0): int {.noSideEffect.}
## Searches for `sub` in `s` starting at position `start`. Searching is ## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned. ## case-sensitive. If `sub` is not in `s`, -1 is returned.
@ -322,13 +297,13 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect.}
## | > 0 iff a > b ## | > 0 iff a > b
proc contains*(s: string, c: char): bool {.noSideEffect.} proc contains*(s: string, c: char): bool {.noSideEffect.}
## Same as ``findSubStr(c, s) >= 0``. ## Same as ``find(s, c) >= 0``.
proc contains*(s, sub: string): bool {.noSideEffect.} proc contains*(s, sub: string): bool {.noSideEffect.}
## Same as ``findSubStr(sub, s) >= 0``. ## Same as ``find(s, sub) >= 0``.
proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} proc contains*(s: string, chars: set[char]): bool {.noSideEffect.}
## Same as ``findChars(s, chars) >= 0``. ## Same as ``find(s, chars) >= 0``.
proc toHex*(x: BiggestInt, len: int): string {.noSideEffect.} proc toHex*(x: BiggestInt, len: int): string {.noSideEffect.}
## Converts `x` to its hexadecimal representation. The resulting string ## Converts `x` to its hexadecimal representation. The resulting string
@ -340,15 +315,15 @@ proc intToStr*(x: int, minchars: int = 1): string
## will be minimally `minchars` characters long. This is achieved by ## will be minimally `minchars` characters long. This is achieved by
## adding leading zeros. ## adding leading zeros.
proc ParseInt*(s: string): int {.noSideEffect.} proc ParseInt*(s: string): int {.noSideEffect, procvar.}
## Parses a decimal integer value contained in `s`. If `s` is not ## Parses a decimal integer value contained in `s`. If `s` is not
## a valid integer, `EInvalidValue` is raised. ## a valid integer, `EInvalidValue` is raised.
proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect.} proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect, procvar.}
## Parses a decimal integer value contained in `s`. If `s` is not ## Parses a decimal integer value contained in `s`. If `s` is not
## a valid integer, `EInvalidValue` is raised. ## a valid integer, `EInvalidValue` is raised.
proc ParseFloat*(s: string): float {.noSideEffect.} proc ParseFloat*(s: string): float {.noSideEffect, procvar.}
## Parses a decimal floating point value contained in `s`. If `s` is not ## Parses a decimal floating point value contained in `s`. If `s` is not
## a valid floating point number, `EInvalidValue` is raised. ``NAN``, ## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
## ``INF``, ``-INF`` are also supported (case insensitive comparison). ## ``INF``, ``-INF`` are also supported (case insensitive comparison).
@ -616,7 +591,7 @@ proc preprocessSub(sub: string, a: var TSkipTable) =
for i in 0..0xff: a[chr(i)] = m+1 for i in 0..0xff: a[chr(i)] = m+1
for i in 0..m-1: a[sub[i]] = m-i for i in 0..m-1: a[sub[i]] = m-i
proc findSubStrAux(s, sub: string, start: int, a: TSkipTable): int = proc findAux(s, sub: string, start: int, a: TSkipTable): int =
# fast "quick search" algorithm: # fast "quick search" algorithm:
var var
m = len(sub) m = len(sub)
@ -631,36 +606,10 @@ proc findSubStrAux(s, sub: string, start: int, a: TSkipTable): int =
inc(j, a[s[j+m]]) inc(j, a[s[j+m]])
return -1 return -1
proc findSubStr(sub, s: string, start: int = 0): int =
var a: TSkipTable
preprocessSub(sub, a)
result = findSubStrAux(s, sub, start, a)
# slow linear search:
#var
# i, j, M, N: int
#M = len(sub)
#N = len(s)
#i = start
#j = 0
#if i >= N:
# result = -1
#else:
# while True:
# if s[i] == sub[j]:
# Inc(i)
# Inc(j)
# else:
# i = i - j + 1
# j = 0
# if (j >= M):
# return i - M
# elif (i >= N):
# return -1
proc find(s, sub: string, start: int = 0): int = proc find(s, sub: string, start: int = 0): int =
var a: TSkipTable var a: TSkipTable
preprocessSub(sub, a) preprocessSub(sub, a)
result = findSubStrAux(s, sub, start, a) result = findAux(s, sub, start, a)
proc find(s: string, sub: char, start: int = 0): int = proc find(s: string, sub: char, start: int = 0): int =
for i in start..len(s)-1: for i in start..len(s)-1:
@ -672,16 +621,6 @@ proc find(s: string, chars: set[char], start: int = 0): int =
if s[i] in chars: return i if s[i] in chars: return i
return -1 return -1
proc findSubStr(sub: char, s: string, start: int = 0): int =
for i in start..len(s)-1:
if sub == s[i]: return i
return -1
proc findChars(chars: set[char], s: string, start: int = 0): int =
for i in start..s.len-1:
if s[i] in chars: return i
return -1
proc contains(s: string, chars: set[char]): bool = proc contains(s: string, chars: set[char]): bool =
return find(s, chars) >= 0 return find(s, chars) >= 0
@ -698,7 +637,7 @@ proc replace*(s, sub, by: string): string =
preprocessSub(sub, a) preprocessSub(sub, a)
var i = 0 var i = 0
while true: while true:
var j = findSubStrAux(s, sub, i, a) var j = findAux(s, sub, i, a)
if j < 0: break if j < 0: break
add result, copy(s, i, j - 1) add result, copy(s, i, j - 1)
add result, by add result, by

181
lib/pure/variants.nim Executable file
View file

@ -0,0 +1,181 @@
#
#
# 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 Nimrod's support for the ``variant`` datatype.
## `TVariant` shows how the flexibility of dynamic typing is achieved
## within a static type system.
type
TVarType* = enum
vtNone,
vtBool,
vtChar,
vtEnum,
vtInt,
vtFloat,
vtString,
vtSet,
vtSeq,
vtDict
TVariant* {.final.} = object of TObject
case vtype: TVarType
of vtNone: nil
of vtBool, vtChar, vtEnum, vtInt: vint: int64
of vtFloat: vfloat: float64
of vtString: vstring: string
of vtSet, vtSeq: q: seq[TVariant]
of vtDict: d: seq[tuple[key, val: TVariant]]
iterator objectFields*[T](x: T, skipInherited: bool): tuple[
key: string, val: TVariant] {.magic: "ObjectFields"}
proc `<>`*(x: ordinal): TVariant =
result.kind = vtEnum
result.vint = x
proc `<>`*(x: biggestInt): TVariant =
result.kind = vtInt
result.vint = x
proc `<>`*(x: char): TVariant =
result.kind = vtChar
result.vint = ord(x)
proc `<>`*(x: bool): TVariant =
result.kind = vtBool
result.vint = ord(x)
proc `<>`*(x: biggestFloat): TVariant =
result.kind = vtFloat
result.vfloat = x
proc `<>`*(x: string): TVariant =
result.kind = vtString
result.vstring = x
proc `<>`*[T](x: openArray[T]): TVariant =
result.kind = vtSeq
newSeq(result.q, x.len)
for i in 0..x.len-1: result.q[i] = <>x[i]
proc `<>`*[T](x: set[T]): TVariant =
result.kind = vtSet
result.q = @[]
for a in items(x): result.q.add(<>a)
proc `<>`* [T: object](x: T): TVariant {.magic: "ToVariant".}
## this converts a value to a variant ("boxing")
proc `><`*[T](v: TVariant, typ: T): T {.magic: "FromVariant".}
[<>5, <>67, <>"hallo"]
myVar><int
proc `==`* (x, y: TVariant): bool =
if x.vtype == y.vtype:
case x.vtype
of vtNone: result = true
of vtBool, vtChar, vtEnum, vtInt: result = x.vint == y.vint
of vtFloat: result = x.vfloat == y.vfloat
of vtString: result = x.vstring == y.vstring
of vtSet:
# complicated! We check that each a in x also occurs in y and that the
# counts are identical:
if x.q.len == y.q.len:
for a in items(x.q):
block inner:
for b in items(y.q):
if a == b: break inner
return false
result = true
of vtSeq:
if x.q.len == y.q.len:
for i in 0..x.q.len-1:
if x.q[i] != y.q[i]: return false
result = true
of vtDict:
# it is an ordered dict:
if x.d.len == y.d.len:
for i in 0..x.d.len-1:
if x.d[i].key != y.d[i].key: return false
if x.d[i].val != y.d[i].val: return false
result = true
proc `[]`* (a, b: TVariant): TVariant =
case a.vtype
of vtSeq:
if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
result = a.q[b.vint]
else:
variantError()
of vtDict:
for i in 0..a.d.len-1:
if a.d[i].key == b: return a.d[i].val
if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
result = a.d[b.vint].val
variantError()
else: variantError()
proc `[]=`* (a, b, c: TVariant) =
case a.vtype
of vtSeq:
if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
a.q[b.vint] = b
else:
variantError()
of vtDict:
for i in 0..a.d.len-1:
if a.d[i].key == b:
a.d[i].val = c
return
if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
a.d[b.vint].val = c
variantError()
else: variantError()
proc `[]`* (a: TVariant, b: int): TVariant {.inline} = return a[<>b]
proc `[]`* (a: TVariant, b: string): TVariant {.inline} = return a[<>b]
proc `[]=`* (a: TVariant, b: int, c: TVariant) {.inline} = a[<>b] = c
proc `[]=`* (a: TVariant, b: string, c: TVariant) {.inline} = a[<>b] = c
proc `+`* (x, y: TVariant): TVariant =
case x.vtype
of vtBool, vtChar, vtEnum, vtInt:
if y.vtype == x.vtype:
result.vtype = x.vtype
result.vint = x.vint + y.vint
else:
case y.vtype
of vtBool, vtChar, vtEnum, vtInt:
vint: int64
of vtFloat: vfloat: float64
of vtString: vstring: string
of vtSet, vtSeq: q: seq[TVariant]
of vtDict: d: seq[tuple[key, val: TVariant]]
proc `-`* (x, y: TVariant): TVariant
proc `*`* (x, y: TVariant): TVariant
proc `/`* (x, y: TVariant): TVariant
proc `div`* (x, y: TVariant): TVariant
proc `mod`* (x, y: TVariant): TVariant
proc `&`* (x, y: TVariant): TVariant
proc `$`* (x: TVariant): string =
# uses JS notation
proc parseVariant*(s: string): TVariant
proc `<`* (x, y: TVariant): bool
proc `<=`* (x, y: TVariant): bool
proc hash*(x: TVariant): int =

View file

@ -668,7 +668,7 @@ else:
""" """
proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.} proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
proc add *[T](x: var seq[T], y: seq[T]) {.noSideEffect.} = proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
## Generic proc for adding a data item `y` to a container `x`. ## Generic proc for adding a data item `y` to a container `x`.
## For containers that have an order, `add` means *append*. New generic ## For containers that have an order, `add` means *append*. New generic
## containers should also call their adding proc `add` for consistency. ## containers should also call their adding proc `add` for consistency.
@ -721,7 +721,8 @@ type # these work for most platforms:
## This C type is not supported by Nimrod's code generator ## This C type is not supported by Nimrod's code generator
cstringArray* {.importc: "char**", nodecl.} = ptr array [0..50_000, cstring] cstringArray* {.importc: "char**", nodecl.} = ptr array [0..50_000, cstring]
## This is the same as the type ``char**`` in *C*. ## This is binary compatible to the type ``char**`` in *C*. The array's
## high value is large enough to disable bounds checking in practice.
TEndian* = enum ## is a type describing the endianness of a processor. TEndian* = enum ## is a type describing the endianness of a processor.
littleEndian, bigEndian littleEndian, bigEndian
@ -1072,28 +1073,25 @@ proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
# This is an undocumented pragma that can only be used # This is an undocumented pragma that can only be used
# once in the system module. # once in the system module.
proc `&` *[T](x, y: seq[T]): seq[T] {.noSideEffect.} = proc `&` *[T](x, y: openArray[T]): seq[T] {.noSideEffect.} =
newSeq(result, x.len + y.len) newSeq(result, x.len + y.len)
for i in 0..x.len-1: for i in 0..x.len-1:
result[i] = x[i] result[i] = x[i]
for i in 0..y.len-1: for i in 0..y.len-1:
result[i] = y[i] result[i+x.len] = y[i]
proc `&` *[T](x: seq[T], y: T): seq[T] {.noSideEffect.} = proc `&` *[T](x: openArray[T], y: T): seq[T] {.noSideEffect.} =
newSeq(result, x.len + 1) newSeq(result, x.len + 1)
for i in 0..x.len-1: for i in 0..x.len-1:
result[i] = x[i] result[i] = x[i]
result[x.len] = y result[x.len] = y
proc `&` *[T](x: T, y: seq[T]): seq[T] {.noSideEffect.} = proc `&` *[T](x: T, y: openArray[T]): seq[T] {.noSideEffect.} =
newSeq(result, y.len + 1) newSeq(result, y.len + 1)
for i in 0..y.len-1: for i in 0..y.len-1:
result[i] = y[i] result[i] = y[i]
result[y.len] = x result[y.len] = x
proc `&` *[T](x, y: T): seq[T] {.noSideEffect.} =
return [x, y]
when not defined(NimrodVM): when not defined(NimrodVM):
when not defined(ECMAScript): when not defined(ECMAScript):
proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =

View file

@ -130,6 +130,9 @@ when defined(boehmgc):
include "system/cellsets" include "system/cellsets"
elif defined(nogc): elif defined(nogc):
include "system/alloc"
when false:
proc alloc(size: int): pointer = proc alloc(size: int): pointer =
result = c_malloc(size) result = c_malloc(size)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
@ -139,8 +142,10 @@ elif defined(nogc):
proc realloc(p: Pointer, newsize: int): pointer = proc realloc(p: Pointer, newsize: int): pointer =
result = c_realloc(p, newsize) result = c_realloc(p, newsize)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
proc dealloc(p: Pointer) = proc dealloc(p: Pointer) = c_free(p)
c_free(p) proc getOccupiedMem(): int = return -1
proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1
proc initGC() = nil proc initGC() = nil
proc GC_disable() = nil proc GC_disable() = nil
@ -151,9 +156,6 @@ elif defined(nogc):
proc GC_disableMarkAndSweep() = nil proc GC_disableMarkAndSweep() = nil
proc GC_getStatistics(): string = return "" 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.} = proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
result = alloc0(size) result = alloc0(size)
@ -163,7 +165,6 @@ elif defined(nogc):
cast[PGenericSeq](result).space = len cast[PGenericSeq](result).space = len
proc growObj(old: pointer, newsize: int): pointer = proc growObj(old: pointer, newsize: int): pointer =
result = realloc(old, newsize) result = realloc(old, newsize)
# XXX BUG: we need realloc0 here, but C does not support this...
proc setStackBottom(theStackBottom: pointer) {.compilerproc.} = nil proc setStackBottom(theStackBottom: pointer) {.compilerproc.} = nil
proc nimGCref(p: pointer) {.compilerproc, inline.} = nil proc nimGCref(p: pointer) {.compilerproc, inline.} = nil

View file

@ -59,6 +59,8 @@ const
STD_OUTPUT_HANDLE* = -11'i32 STD_OUTPUT_HANDLE* = -11'i32
STD_ERROR_HANDLE* = -12'i32 STD_ERROR_HANDLE* = -12'i32
DETACHED_PROCESS* = 8'i32
proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32", proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
importc: "CloseHandle".} importc: "CloseHandle".}
@ -184,3 +186,7 @@ proc GetCommandLineA*(): CString {.importc, stdcall, dynlib: "kernel32".}
proc rdFileTime*(f: FILETIME): int64 = proc rdFileTime*(f: FILETIME): int64 =
result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32) result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
proc Sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
importc: "Sleep".}

0
lib/wrappers/tre/config.h Normal file → Executable file
View file

0
lib/wrappers/tre/tre_all.c Normal file → Executable file
View file

0
lib/wrappers/tre/version.txt Normal file → Executable file
View file

View file

@ -78,27 +78,26 @@ type
nkBracket, nkBracketExpr, nkPragmaExpr, nkRange, nkBracket, nkBracketExpr, nkPragmaExpr, nkRange,
nkDotExpr, nkCheckedFieldExpr, nkDerefExpr, nkIfExpr, nkDotExpr, nkCheckedFieldExpr, nkDerefExpr, nkIfExpr,
nkElifExpr, nkElseExpr, nkLambda, nkAccQuoted, nkElifExpr, nkElseExpr, nkLambda, nkAccQuoted,
nkTableConstr, nkQualified, nkBind, nkSymChoice, nkTableConstr, nkBind, nkSymChoice, nkHiddenStdConv,
nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkConv, nkHiddenSubConv, nkHiddenCallConv, nkConv, nkCast,
nkCast, nkAddr, nkHiddenAddr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkHiddenDeref, nkObjDownConv,
nkObjDownConv, nkObjUpConv, nkChckRangeF, nkChckRange64, nkObjUpConv, nkChckRangeF, nkChckRange64, nkChckRange,
nkChckRange, nkStringToCString, nkCStringToString, nkPassAsOpenArray, nkStringToCString, nkCStringToString, nkPassAsOpenArray, nkAsgn,
nkAsgn, nkFastAsgn, nkGenericParams, nkFormalParams, nkFastAsgn, nkGenericParams, nkFormalParams, nkOfInherit,
nkOfInherit, nkModule, nkProcDef, nkMethodDef, nkModule, nkProcDef, nkMethodDef, nkConverterDef,
nkConverterDef, nkMacroDef, nkTemplateDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkIteratorDef, nkOfBranch,
nkOfBranch, nkElifBranch, nkExceptBranch, nkElse, nkElifBranch, nkExceptBranch, nkElse, nkMacroStmt,
nkMacroStmt, nkAsmStmt, nkPragma, nkIfStmt, nkAsmStmt, nkPragma, nkIfStmt, nkWhenStmt,
nkWhenStmt, nkForStmt, nkWhileStmt, nkCaseStmt, nkForStmt, nkWhileStmt, nkCaseStmt, nkVarSection,
nkVarSection, nkConstSection, nkConstDef, nkTypeSection, nkConstSection, nkConstDef, nkTypeSection, nkTypeDef,
nkTypeDef, nkYieldStmt, nkTryStmt, nkFinally, nkYieldStmt, nkTryStmt, nkFinally, nkRaiseStmt,
nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt, nkBlockStmt,
nkBlockStmt, nkDiscardStmt, nkStmtList, nkImportStmt, nkDiscardStmt, nkStmtList, nkImportStmt, nkFromStmt,
nkFromStmt, nkIncludeStmt, nkCommentStmt, nkStmtListExpr, nkIncludeStmt, nkCommentStmt, nkStmtListExpr, nkBlockExpr,
nkBlockExpr, nkStmtListType, nkBlockType, nkTypeOfExpr, nkStmtListType, nkBlockType, nkTypeOfExpr, nkObjectTy,
nkObjectTy, nkTupleTy, nkRecList, nkRecCase, nkTupleTy, nkRecList, nkRecCase, nkRecWhen,
nkRecWhen, nkRefTy, nkPtrTy, nkVarTy, nkRefTy, nkPtrTy, nkVarTy, nkDistinctTy,
nkDistinctTy, nkProcTy, nkEnumTy, nkEnumFieldDef, nkProcTy, nkEnumTy, nkEnumFieldDef, nkReturnToken);
nkReturnToken);
TNodeKinds = set of TNodeKind; TNodeKinds = set of TNodeKind;
const const
NodeKindToStr: array [TNodeKind] of string = ( NodeKindToStr: array [TNodeKind] of string = (
@ -113,27 +112,26 @@ const
'nkBracket', 'nkBracketExpr', 'nkPragmaExpr', 'nkRange', 'nkBracket', 'nkBracketExpr', 'nkPragmaExpr', 'nkRange',
'nkDotExpr', 'nkCheckedFieldExpr', 'nkDerefExpr', 'nkIfExpr', 'nkDotExpr', 'nkCheckedFieldExpr', 'nkDerefExpr', 'nkIfExpr',
'nkElifExpr', 'nkElseExpr', 'nkLambda', 'nkAccQuoted', 'nkElifExpr', 'nkElseExpr', 'nkLambda', 'nkAccQuoted',
'nkTableConstr', 'nkQualified', 'nkBind', 'nkSymChoice', 'nkTableConstr', 'nkBind', 'nkSymChoice', 'nkHiddenStdConv',
'nkHiddenStdConv', 'nkHiddenSubConv', 'nkHiddenCallConv', 'nkConv', 'nkHiddenSubConv', 'nkHiddenCallConv', 'nkConv', 'nkCast',
'nkCast', 'nkAddr', 'nkHiddenAddr', 'nkHiddenDeref', 'nkAddr', 'nkHiddenAddr', 'nkHiddenDeref', 'nkObjDownConv',
'nkObjDownConv', 'nkObjUpConv', 'nkChckRangeF', 'nkChckRange64', 'nkObjUpConv', 'nkChckRangeF', 'nkChckRange64', 'nkChckRange',
'nkChckRange', 'nkStringToCString', 'nkCStringToString', 'nkPassAsOpenArray', 'nkStringToCString', 'nkCStringToString', 'nkPassAsOpenArray', 'nkAsgn',
'nkAsgn', 'nkFastAsgn', 'nkGenericParams', 'nkFormalParams', 'nkFastAsgn', 'nkGenericParams', 'nkFormalParams', 'nkOfInherit',
'nkOfInherit', 'nkModule', 'nkProcDef', 'nkMethodDef', 'nkModule', 'nkProcDef', 'nkMethodDef', 'nkConverterDef',
'nkConverterDef', 'nkMacroDef', 'nkTemplateDef', 'nkIteratorDef', 'nkMacroDef', 'nkTemplateDef', 'nkIteratorDef', 'nkOfBranch',
'nkOfBranch', 'nkElifBranch', 'nkExceptBranch', 'nkElse', 'nkElifBranch', 'nkExceptBranch', 'nkElse', 'nkMacroStmt',
'nkMacroStmt', 'nkAsmStmt', 'nkPragma', 'nkIfStmt', 'nkAsmStmt', 'nkPragma', 'nkIfStmt', 'nkWhenStmt',
'nkWhenStmt', 'nkForStmt', 'nkWhileStmt', 'nkCaseStmt', 'nkForStmt', 'nkWhileStmt', 'nkCaseStmt', 'nkVarSection',
'nkVarSection', 'nkConstSection', 'nkConstDef', 'nkTypeSection', 'nkConstSection', 'nkConstDef', 'nkTypeSection', 'nkTypeDef',
'nkTypeDef', 'nkYieldStmt', 'nkTryStmt', 'nkFinally', 'nkYieldStmt', 'nkTryStmt', 'nkFinally', 'nkRaiseStmt',
'nkRaiseStmt', 'nkReturnStmt', 'nkBreakStmt', 'nkContinueStmt', 'nkReturnStmt', 'nkBreakStmt', 'nkContinueStmt', 'nkBlockStmt',
'nkBlockStmt', 'nkDiscardStmt', 'nkStmtList', 'nkImportStmt', 'nkDiscardStmt', 'nkStmtList', 'nkImportStmt', 'nkFromStmt',
'nkFromStmt', 'nkIncludeStmt', 'nkCommentStmt', 'nkStmtListExpr', 'nkIncludeStmt', 'nkCommentStmt', 'nkStmtListExpr', 'nkBlockExpr',
'nkBlockExpr', 'nkStmtListType', 'nkBlockType', 'nkTypeOfExpr', 'nkStmtListType', 'nkBlockType', 'nkTypeOfExpr', 'nkObjectTy',
'nkObjectTy', 'nkTupleTy', 'nkRecList', 'nkRecCase', 'nkTupleTy', 'nkRecList', 'nkRecCase', 'nkRecWhen',
'nkRecWhen', 'nkRefTy', 'nkPtrTy', 'nkVarTy', 'nkRefTy', 'nkPtrTy', 'nkVarTy', 'nkDistinctTy',
'nkDistinctTy', 'nkProcTy', 'nkEnumTy', 'nkEnumFieldDef', 'nkProcTy', 'nkEnumTy', 'nkEnumFieldDef', 'nkReturnToken');
'nkReturnToken');
type type
TSymFlag = ( TSymFlag = (
sfUsed, sfStar, sfMinus, sfInInterface, sfUsed, sfStar, sfMinus, sfInInterface,
@ -141,7 +139,7 @@ type
sfExportc, sfVolatile, sfRegister, sfPure, sfExportc, sfVolatile, sfRegister, sfPure,
sfResult, sfNoSideEffect, sfSideEffect, sfMainModule, sfResult, sfNoSideEffect, sfSideEffect, sfMainModule,
sfSystemModule, sfNoReturn, sfAddrTaken, sfCompilerProc, sfSystemModule, sfNoReturn, sfAddrTaken, sfCompilerProc,
sfCppMethod, sfDiscriminant, sfDeprecated, sfInClosure, sfProcvar, sfDiscriminant, sfDeprecated, sfInClosure,
sfTypeCheck, sfCompileTime, sfThreadVar, sfMerge, sfTypeCheck, sfCompileTime, sfThreadVar, sfMerge,
sfDeadCodeElim, sfBorrow); sfDeadCodeElim, sfBorrow);
TSymFlags = set of TSymFlag; TSymFlags = set of TSymFlag;
@ -152,7 +150,7 @@ const
'sfExportc', 'sfVolatile', 'sfRegister', 'sfPure', 'sfExportc', 'sfVolatile', 'sfRegister', 'sfPure',
'sfResult', 'sfNoSideEffect', 'sfSideEffect', 'sfMainModule', 'sfResult', 'sfNoSideEffect', 'sfSideEffect', 'sfMainModule',
'sfSystemModule', 'sfNoReturn', 'sfAddrTaken', 'sfCompilerProc', 'sfSystemModule', 'sfNoReturn', 'sfAddrTaken', 'sfCompilerProc',
'sfCppMethod', 'sfDiscriminant', 'sfDeprecated', 'sfInClosure', 'sfProcvar', 'sfDiscriminant', 'sfDeprecated', 'sfInClosure',
'sfTypeCheck', 'sfCompileTime', 'sfThreadVar', 'sfMerge', 'sfTypeCheck', 'sfCompileTime', 'sfThreadVar', 'sfMerge',
'sfDeadCodeElim', 'sfBorrow'); 'sfDeadCodeElim', 'sfBorrow');
type type
@ -340,6 +338,7 @@ type
lfNoDeepCopy, // no need for a deep copy lfNoDeepCopy, // no need for a deep copy
lfNoDecl, // do not declare it in C lfNoDecl, // do not declare it in C
lfDynamicLib, // link symbol to dynamic library lfDynamicLib, // link symbol to dynamic library
lfExportLib, // export symbol for dynamic library generation
lfHeader // include header file for symbol lfHeader // include header file for symbol
); );

View file

@ -1086,6 +1086,8 @@ begin
app(pl, ', ') app(pl, ', ')
end; end;
if (typ.sons[0] <> nil) and invalidRetType then begin if (typ.sons[0] <> nil) and invalidRetType then begin
// XXX (detected by pegs module 64bit): p(result, result) is not
// correct here. Thus we always allocate a temporary:
if d.k = locNone then getTemp(p, typ.sons[0], d); if d.k = locNone then getTemp(p, typ.sons[0], d);
app(pl, addrLoc(d)); app(pl, addrLoc(d));
end; end;
@ -2212,7 +2214,7 @@ begin
'); unknown symbol') '); unknown symbol')
end end
end; end;
nkQualified: expr(p, e.sons[1], d); //nkQualified: expr(p, e.sons[1], d);
nkStrLit..nkTripleStrLit, nkIntLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nkIntLit..nkInt64Lit,
nkFloatLit..nkFloat64Lit, nkNilLit, nkCharLit: begin nkFloatLit..nkFloat64Lit, nkNilLit, nkCharLit: begin
putIntoDest(p, d, e.typ, genLiteral(p, e)); putIntoDest(p, d, e.typ, genLiteral(p, e));

View file

@ -221,7 +221,8 @@ begin
assert(not (sfResult in s.flags)); assert(not (sfResult in s.flags));
case pt.Kind of case pt.Kind of
tyObject: begin tyObject: begin
if (optByRef in s.options) or (getSize(pt) > platform.floatSize) then // XXX quick hack floatSize*2 for the pegs module under 64bit
if (optByRef in s.options) or (getSize(pt) > platform.floatSize*2) then
result := true // requested anyway result := true // requested anyway
else if (tfFinal in pt.flags) and (pt.sons[0] = nil) then else if (tfFinal in pt.flags) and (pt.sons[0] = nil) then
result := false // no need, because no subtyping possible result := false // no need, because no subtyping possible

View file

@ -659,6 +659,8 @@ var
begin begin
p := newProc(prc, m); p := newProc(prc, m);
header := genProcHeader(m, prc); header := genProcHeader(m, prc);
if (gCmd <> cmdCompileToLLVM) and (lfExportLib in prc.loc.flags) then
header := con('N_LIB_EXPORT ', header);
returnStmt := nil; returnStmt := nil;
assert(prc.ast <> nil); assert(prc.ast <> nil);
@ -942,7 +944,11 @@ const
' call void @NimMain()$n' + ' call void @NimMain()$n' +
' ret i32 0$n' + ' ret i32 0$n' +
'}$n'; '}$n';
WinNimDllMain = WinNimMain; WinNimDllMain =
'N_LIB_EXPORT N_CDECL(void, NimMain)(void) {$n' +
' int dummy[8];$n' +{&}
CommonMainBody +{&}
'}$n';
WinCDllMain = WinCDllMain =
'BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n' + 'BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n' +
' LPVOID lpvReserved) {$n' + ' LPVOID lpvReserved) {$n' +

0
nim/cgmeth.pas Normal file → Executable file
View file

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -85,7 +85,7 @@ const
+{&} ' -a, --assertions:on|off code generation for assertions ON|OFF' +{&} nl +{&} ' -a, --assertions:on|off code generation for assertions ON|OFF' +{&} nl
+{&} ' --dead_code_elim:on|off whole program dead code elimination ON|OFF' +{&} nl +{&} ' --dead_code_elim:on|off whole program dead code elimination ON|OFF' +{&} nl
+{&} ' --opt:none|speed|size optimize not at all or for speed|size' +{&} nl +{&} ' --opt:none|speed|size optimize not at all or for speed|size' +{&} nl
+{&} ' --app:console|gui generate a console|GUI application' +{&} nl +{&} ' --app:console|gui|lib generate a console|GUI application|dynamic library' +{&} nl
+{&} ' -r, --run run the compiled program with given arguments' +{&} nl +{&} ' -r, --run run the compiled program with given arguments' +{&} nl
+{&} ' --advanced show advanced command line switches' +{&} nl +{&} ' --advanced show advanced command line switches' +{&} nl
+{&} ' -h, --help show this help' +{&} nl +{&} ' -h, --help show this help' +{&} nl
@ -127,10 +127,12 @@ 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 +{&} ' --gc:refc|boehm|none use Nimrod''s native GC|Boehm GC|no 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
+{&} ' --parallel_build=0|1|... perform a parallel build' +{&} nl
+{&} ' value = number of processors (0 for auto-detect)' +{&} nl
+{&} ' --verbosity:0|1|2|3 set Nimrod''s verbosity level (0 is default)' +{&} nl +{&} ' --verbosity:0|1|2|3 set Nimrod''s verbosity level (0 is default)' +{&} nl
+{&} ' -v, --version show detailed version information' +{&} nl +{&} ' -v, --version show detailed version information' +{&} nl
//[[[end]]] //[[[end]]]
@ -523,6 +525,10 @@ begin
expectArg(switch, arg, pass, info); expectArg(switch, arg, pass, info);
gVerbosity := parseInt(arg); gVerbosity := parseInt(arg);
end; end;
wParallelBuild: begin
expectArg(switch, arg, pass, info);
gNumberOfProcessors := parseInt(arg);
end;
wVersion, wV: begin wVersion, wV: begin
expectNoArg(switch, arg, pass, info); expectNoArg(switch, arg, pass, info);
writeVersionInfo(pass); writeVersionInfo(pass);

View file

@ -51,12 +51,12 @@ begin
case n.kind of case n.kind of
nkImportStmt: begin nkImportStmt: begin
for i := 0 to sonsLen(n)-1 do begin for i := 0 to sonsLen(n)-1 do begin
imported := getFileTrunk(getModuleFile(n.sons[i])); imported := extractFileTrunk(getModuleFile(n.sons[i]));
addDependencyAux(g.module.name.s, imported); addDependencyAux(g.module.name.s, imported);
end end
end; end;
nkFromStmt: begin nkFromStmt: begin
imported := getFileTrunk(getModuleFile(n.sons[0])); imported := extractFileTrunk(getModuleFile(n.sons[0]));
addDependencyAux(g.module.name.s, imported); addDependencyAux(g.module.name.s, imported);
end; end;
nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr: begin nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr: begin

View file

@ -19,7 +19,7 @@ interface
uses uses
nsystem, charsets, ast, astalgo, strutils, nhashes, 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, syntaxes, rnimsyn, scanner, rst, ntime,
highlite; highlite;
procedure CommandDoc(const filename: string); procedure CommandDoc(const filename: string);
@ -85,7 +85,7 @@ var
dummyHasToc: bool; dummyHasToc: bool;
begin begin
if gIndexFile = '' then exit; if gIndexFile = '' then exit;
gIndexFile := appendFileExt(gIndexFile, 'txt'); gIndexFile := addFileExt(gIndexFile, 'txt');
d.indexValFilename := changeFileExt(extractFilename(d.filename), HtmlExt); d.indexValFilename := changeFileExt(extractFilename(d.filename), HtmlExt);
if ExistsFile(gIndexFile) then begin if ExistsFile(gIndexFile) then begin
d.indexFile := rstParse(readFile(gIndexFile), false, gIndexFile, 0, 1, d.indexFile := rstParse(readFile(gIndexFile), false, gIndexFile, 0, 1,
@ -1135,7 +1135,7 @@ var
ast: PNode; ast: PNode;
d: PDoc; d: PDoc;
begin begin
ast := parseFile(appendFileExt(filename, nimExt)); ast := parseFile(addFileExt(filename, nimExt));
if ast = nil then exit; if ast = nil then exit;
d := newDocumentor(filename); d := newDocumentor(filename);
initIndexFile(d); initIndexFile(d);
@ -1152,7 +1152,7 @@ var
rst: PRstNode; rst: PRstNode;
code: PRope; code: PRope;
begin begin
filen := appendFileExt(filename, 'txt'); filen := addFileExt(filename, 'txt');
d := newDocumentor(filen); d := newDocumentor(filen);
initIndexFile(d); initIndexFile(d);
rst := rstParse(readFile(filen), false, filen, 0, 1, d.hasToc); rst := rstParse(readFile(filen), false, filen, 0, 1, d.hasToc);

View file

@ -1096,7 +1096,7 @@ begin
end; end;
end; end;
nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r); nkCheckedFieldExpr: genCheckedFieldAddr(p, n, r);
nkDotExpr, nkQualified: genFieldAddr(p, n, r); nkDotExpr: genFieldAddr(p, n, r);
nkBracketExpr: genArrayAddr(p, n, r); nkBracketExpr: genArrayAddr(p, n, r);
else InternalError(n.info, 'genAddr'); else InternalError(n.info, 'genAddr');
end end

View file

@ -1324,7 +1324,7 @@ begin
nkTemplateDef, nkConstSection, nkIteratorDef, nkConverterDef, nkTemplateDef, nkConstSection, nkIteratorDef, nkConverterDef,
nkIncludeStmt, nkImportStmt, nkFromStmt: begin end; nkIncludeStmt, nkImportStmt, nkFromStmt: begin end;
nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
nkQualified, nkLambda, nkContinueStmt, nkIdent: nkLambda, nkContinueStmt, nkIdent:
stackTrace(c, n, errCannotInterpretNodeX, nodeKindToStr[n.kind]); stackTrace(c, n, errCannotInterpretNodeX, nodeKindToStr[n.kind]);
else InternalError(n.info, 'evalAux: ' + nodekindToStr[n.kind]); else InternalError(n.info, 'evalAux: ' + nodekindToStr[n.kind]);
end; end;

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -304,10 +304,10 @@ procedure setCC(const ccname: string);
var var
i: TSystemCC; i: TSystemCC;
begin begin
linkOptions := '';
ccompiler := nameToCC(ccname); ccompiler := nameToCC(ccname);
if ccompiler = ccNone then rawMessage(errUnknownCcompiler, ccname); if ccompiler = ccNone then rawMessage(errUnknownCcompiler, ccname);
compileOptions := getConfigVar(CC[ccompiler].name + '.options.always'); compileOptions := getConfigVar(CC[ccompiler].name + '.options.always');
linkOptions := getConfigVar(CC[ccompiler].name + '.options.linker');
ccompilerpath := getConfigVar(CC[ccompiler].name + '.path'); ccompilerpath := getConfigVar(CC[ccompiler].name + '.path');
for i := low(CC) to high(CC) do undefSymbol(CC[i].name); for i := low(CC) to high(CC) do undefSymbol(CC[i].name);
defineSymbol(CC[ccompiler].name); defineSymbol(CC[ccompiler].name);
@ -323,6 +323,7 @@ begin
if gCmd = cmdCompileToCpp then if gCmd = cmdCompileToCpp then
cExt := '.cpp'; cExt := '.cpp';
addCompileOption(getConfigVar(CC[ccompiler].name + '.options.always')); addCompileOption(getConfigVar(CC[ccompiler].name + '.options.always'));
addLinkOption(getConfigVar(CC[ccompiler].name + '.options.linker'));
if length(ccompilerPath) = 0 then if length(ccompilerPath) = 0 then
ccompilerpath := getConfigVar(CC[ccompiler].name + '.path'); ccompilerpath := getConfigVar(CC[ccompiler].name + '.path');
end; end;
@ -387,8 +388,8 @@ end;
procedure execExternalProgram(const cmd: string); 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(cmd);
if executeCommand(cmd) <> 0 then if execCmd(cmd) <> 0 then
rawMessage(errExecutionOfProgramFailed); rawMessage(errExecutionOfProgramFailed);
end; end;
@ -398,7 +399,7 @@ var
begin begin
splitPath(projectFile, path, scriptname); splitPath(projectFile, path, scriptname);
SplitFilename(scriptname, name, ext); SplitFilename(scriptname, name, ext);
name := appendFileExt('compile_' + name, platform.os[targetOS].scriptExt); name := addFileExt('compile_' + name, platform.os[targetOS].scriptExt);
WriteRope(script, joinPath(path, name)); WriteRope(script, joinPath(path, name));
end; end;
@ -437,7 +438,7 @@ var
begin begin
c := ccompiler; c := ccompiler;
options := compileOptions; options := compileOptions;
trunk := getFileTrunk(cfilename); trunk := extractFileTrunk(cfilename);
if optCDebug in gGlobalOptions then begin if optCDebug in gGlobalOptions then begin
key := trunk + '.debug'; key := trunk + '.debug';
if existsConfigVar(key) then if existsConfigVar(key) then
@ -468,7 +469,7 @@ begin
key := cc[c].name + '.exe'; key := cc[c].name + '.exe';
if existsConfigVar(key) then if existsConfigVar(key) then
exe := getConfigVar(key); exe := getConfigVar(key);
if targetOS = osWindows then exe := appendFileExt(exe, 'exe'); if targetOS = osWindows then exe := addFileExt(exe, 'exe');
if (optGenDynLib in gGlobalOptions) if (optGenDynLib in gGlobalOptions)
and (ospNeedsPIC in platform.OS[targetOS].props) then and (ospNeedsPIC in platform.OS[targetOS].props) then
@ -494,7 +495,7 @@ begin
objfile := toObjFile(cfile) objfile := toObjFile(cfile)
else else
objfile := completeCFilePath(toObjFile(cfile)); objfile := completeCFilePath(toObjFile(cfile));
cfile := quoteIfContainsWhite(AppendFileExt(cfile, cExt)); cfile := quoteIfContainsWhite(AddFileExt(cfile, cExt));
objfile := quoteIfContainsWhite(objfile); objfile := quoteIfContainsWhite(objfile);
result := quoteIfContainsWhite(format(compilePattern, result := quoteIfContainsWhite(format(compilePattern,
@ -517,7 +518,9 @@ begin
end; end;
procedure CompileCFile(const list: TLinkedList; procedure CompileCFile(const list: TLinkedList;
var script: PRope; isExternal: Boolean); var script: PRope;
var cmds: TStringSeq;
isExternal: Boolean);
var var
it: PStrEntry; it: PStrEntry;
compileCmd: string; compileCmd: string;
@ -528,7 +531,7 @@ begin
// call the C compiler for the .c file: // call the C compiler for the .c file:
compileCmd := getCompileCFileCmd(it.data, isExternal); compileCmd := getCompileCFileCmd(it.data, isExternal);
if not (optCompileOnly in gGlobalOptions) then if not (optCompileOnly in gGlobalOptions) then
execExternalProgram(compileCmd); add(cmds, compileCmd); //execExternalProgram(compileCmd);
if (optGenScript in gGlobalOptions) then begin if (optGenScript in gGlobalOptions) then begin
app(script, compileCmd); app(script, compileCmd);
app(script, tnl); app(script, tnl);
@ -543,6 +546,8 @@ var
linkCmd, objfiles, exefile, buildgui, builddll, linkerExe: string; linkCmd, objfiles, exefile, buildgui, builddll, linkerExe: string;
c: TSystemCC; // an alias to ccompiler c: TSystemCC; // an alias to ccompiler
script: PRope; script: PRope;
cmds: TStringSeq;
res, i: int;
begin begin
if (gGlobalOptions * [optCompileOnly, optGenScript] = [optCompileOnly]) then if (gGlobalOptions * [optCompileOnly, optGenScript] = [optCompileOnly]) then
exit; // speed up that call if only compiling and no script shall be exit; // speed up that call if only compiling and no script shall be
@ -551,41 +556,53 @@ begin
fileCounter := 0; fileCounter := 0;
c := ccompiler; c := ccompiler;
script := nil; script := nil;
CompileCFile(toCompile, script, false); cmds := {@ignore} nil {@emit @[]};
CompileCFile(externalToCompile, script, true); CompileCFile(toCompile, script, cmds, false);
CompileCFile(externalToCompile, script, cmds, true);
if not (optCompileOnly in gGlobalOptions) then begin
if gNumberOfProcessors = 0 then
gNumberOfProcessors := countProcessors();
if gNumberOfProcessors <= 1 then begin
res := 0;
for i := 0 to high(cmds) do res := max(execCmd(cmds[i]), res);
end
else if (optListCmd in gGlobalOptions) or (gVerbosity > 0) then
res := execProcesses(cmds, {@set}[poEchoCmd, poUseShell, poParentStreams],
gNumberOfProcessors)
else
res := execProcesses(cmds, {@set}[poUseShell, poParentStreams],
gNumberOfProcessors);
if res <> 0 then
rawMessage(errExecutionOfProgramFailed);
end;
if not (optNoLinking in gGlobalOptions) then begin if not (optNoLinking in gGlobalOptions) then begin
// call the linker: // call the linker:
linkerExe := getConfigVar(cc[c].name + '.linkerexe'); linkerExe := getConfigVar(cc[c].name + '.linkerexe');
if length(linkerExe) = 0 then linkerExe := cc[c].linkerExe; if length(linkerExe) = 0 then linkerExe := cc[c].linkerExe;
if targetOS = osWindows then linkerExe := appendFileExt(linkerExe, 'exe'); if targetOS = osWindows then linkerExe := addFileExt(linkerExe, 'exe');
if (platform.hostOS <> targetOS) then if (platform.hostOS <> targetOS) then
linkCmd := quoteIfContainsWhite(linkerExe) linkCmd := quoteIfContainsWhite(linkerExe)
else else
linkCmd := quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe)); linkCmd := quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe));
if optGenDynLib in gGlobalOptions then
buildDll := cc[c].buildDll
else
buildDll := '';
if optGenGuiApp in gGlobalOptions then if optGenGuiApp in gGlobalOptions then
buildGui := cc[c].buildGui buildGui := cc[c].buildGui
else else
buildGui := ''; buildGui := '';
if optGenDynLib in gGlobalOptions then if optGenDynLib in gGlobalOptions then begin
exefile := platform.os[targetOS].dllPrefix exefile := format(platform.os[targetOS].dllFrmt,
else [extractFileTrunk(projectFile)]);
exefile := ''; buildDll := cc[c].buildDll;
end
else begin
exefile := extractFileTrunk(projectFile) +{&} platform.os[targetOS].exeExt;
buildDll := '';
end;
if targetOS = platform.hostOS then if targetOS = platform.hostOS then
add(exefile, projectFile) exefile := joinPath(extractDir(projectFile), exefile);
else
add(exefile, extractFileName(projectFile));
if optGenDynLib in gGlobalOptions then
add(exefile, platform.os[targetOS].dllExt)
else
add(exefile, platform.os[targetOS].exeExt);
exefile := quoteIfContainsWhite(exefile); exefile := quoteIfContainsWhite(exefile);
it := PStrEntry(toLink.head); it := PStrEntry(toLink.head);
@ -639,7 +656,7 @@ begin
result := nil; result := nil;
it := PStrEntry(list.head); it := PStrEntry(list.head);
while it <> nil do begin while it <> nil do begin
appf(result, '--file:r"$1"$n', [toRope(AppendFileExt(it.data, cExt))]); appf(result, '--file:r"$1"$n', [toRope(AddFileExt(it.data, cExt))]);
it := PStrEntry(it.next); it := PStrEntry(it.next);
end; end;
end; end;

137
nim/filters.pas Executable file
View file

@ -0,0 +1,137 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit filters;
// This module implements Nimrod's simple filters and helpers for filters.
{$include config.inc}
interface
uses
nsystem, llstream, nos, charsets, wordrecg, idents, strutils,
ast, astalgo, msgs, options, rnimsyn;
function filterReplace(input: PLLStream; const filename: string;
call: PNode): PLLStream;
function filterStrip(input: PLLStream; const filename: string;
call: PNode): PLLStream;
// helpers to retrieve arguments:
function charArg(n: PNode; const name: string; pos: int; default: Char): Char;
function strArg(n: PNode; const name: string; pos: int;
const default: string): string;
function boolArg(n: PNode; const name: string; pos: int; default: bool): bool;
implementation
procedure invalidPragma(n: PNode);
begin
liMessage(n.info, errXNotAllowedHere, renderTree(n, {@set}[renderNoComments]));
end;
function getArg(n: PNode; const name: string; pos: int): PNode;
var
i: int;
begin
result := nil;
if n.kind in [nkEmpty..nkNilLit] then exit;
for i := 1 to sonsLen(n)-1 do
if n.sons[i].kind = nkExprEqExpr then begin
if n.sons[i].sons[0].kind <> nkIdent then invalidPragma(n);
if IdentEq(n.sons[i].sons[0].ident, name) then begin
result := n.sons[i].sons[1];
exit
end
end
else if i = pos then begin
result := n.sons[i]; exit
end
end;
function charArg(n: PNode; const name: string; pos: int; default: Char): Char;
var
x: PNode;
begin
x := getArg(n, name, pos);
if x = nil then result := default
else if x.kind = nkCharLit then result := chr(int(x.intVal))
else invalidPragma(n);
end;
function strArg(n: PNode; const name: string; pos: int;
const default: string): string;
var
x: PNode;
begin
x := getArg(n, name, pos);
if x = nil then result := default
else if x.kind in [nkStrLit..nkTripleStrLit] then result := x.strVal
else invalidPragma(n);
end;
function boolArg(n: PNode; const name: string; pos: int; default: bool): bool;
var
x: PNode;
begin
x := getArg(n, name, pos);
if x = nil then result := default
else if (x.kind = nkIdent) and IdentEq(x.ident, 'true') then result := true
else if (x.kind = nkIdent) and IdentEq(x.ident, 'false') then result := false
else invalidPragma(n);
end;
// -------------------------- strip filter -----------------------------------
function filterStrip(input: PLLStream; const filename: string;
call: PNode): PLLStream;
var
line, pattern, stripped: string;
leading, trailing: bool;
begin
pattern := strArg(call, 'startswith', 1, '');
leading := boolArg(call, 'leading', 2, true);
trailing := boolArg(call, 'trailing', 3, true);
result := LLStreamOpen('');
while not LLStreamAtEnd(input) do begin
line := LLStreamReadLine(input);
{@ignore}
stripped := strip(line);
{@emit
stripped := strip(line, leading, trailing);
}
if (length(pattern) = 0) or startsWith(stripped, pattern) then
LLStreamWriteln(result, stripped)
else
LLStreamWriteln(result, line)
end;
LLStreamClose(input);
end;
// -------------------------- replace filter ---------------------------------
function filterReplace(input: PLLStream; const filename: string;
call: PNode): PLLStream;
var
line, sub, by: string;
begin
sub := strArg(call, 'sub', 1, '');
if length(sub) = 0 then invalidPragma(call);
by := strArg(call, 'by', 2, '');
result := LLStreamOpen('');
while not LLStreamAtEnd(input) do begin
line := LLStreamReadLine(input);
LLStreamWriteln(result, replace(line, sub, by))
end;
LLStreamClose(input);
end;
end.

View file

@ -29,9 +29,8 @@ implementation
function findModule(const info: TLineInfo; const modulename: string): string; function findModule(const info: TLineInfo; const modulename: string): string;
// returns path to module // returns path to module
begin begin
result := options.FindFile(AppendFileExt(modulename, nimExt)); result := options.FindFile(AddFileExt(modulename, nimExt));
if result = '' then if result = '' then liMessage(info, errCannotOpenFile, modulename);
liMessage(info, errCannotOpenFile, modulename);
end; end;
function getModuleFile(n: PNode): string; function getModuleFile(n: PNode): string;

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -25,7 +25,7 @@ type
kind: TLLStreamKind; // accessible for low-level access (lexbase uses this) kind: TLLStreamKind; // accessible for low-level access (lexbase uses this)
f: TBinaryFile; f: TBinaryFile;
s: string; s: string;
pos: int; // for string streams rd, wr: int; // for string streams
end; end;
PLLStream = ^TLLStream; PLLStream = ^TLLStream;
@ -46,6 +46,8 @@ procedure LLStreamWrite(s: PLLStream; const data: string); overload;
procedure LLStreamWrite(s: PLLStream; data: Char); overload; procedure LLStreamWrite(s: PLLStream; data: Char); overload;
procedure LLStreamWrite(s: PLLStream; buf: pointer; buflen: int); overload; procedure LLStreamWrite(s: PLLStream; buf: pointer; buflen: int); overload;
procedure LLStreamWriteln(s: PLLStream; const data: string);
function LLStreamAtEnd(s: PLLStream): bool; function LLStreamAtEnd(s: PLLStream): bool;
implementation implementation
@ -97,7 +99,6 @@ begin
{@emit} {@emit}
result.kind := llsStdIn; result.kind := llsStdIn;
result.s := ''; result.s := '';
result.pos := -1;
end; end;
procedure LLStreamClose(s: PLLStream); procedure LLStreamClose(s: PLLStream);
@ -114,7 +115,7 @@ var
L: int; L: int;
begin begin
s.s := ''; s.s := '';
s.pos := 0; s.rd := 0;
while true do begin while true do begin
write(output, 'Nimrod> '); write(output, 'Nimrod> ');
line := readLine(input); line := readLine(input);
@ -123,10 +124,10 @@ begin
add(s.s, nl); add(s.s, nl);
if (L > 0) and (line[L-1+strStart] = '#') then break; if (L > 0) and (line[L-1+strStart] = '#') then break;
end; end;
result := min(bufLen, length(s.s)-s.pos); result := min(bufLen, length(s.s)-s.rd);
if result > 0 then begin if result > 0 then begin
copyMem(buf, addr(s.s[strStart+s.pos]), result); copyMem(buf, addr(s.s[strStart+s.rd]), result);
inc(s.pos, result) inc(s.rd, result)
end end
end; end;
@ -135,10 +136,10 @@ begin
case s.kind of case s.kind of
llsNone: result := 0; llsNone: result := 0;
llsString: begin llsString: begin
result := min(bufLen, length(s.s)-s.pos); result := min(bufLen, length(s.s)-s.rd);
if result > 0 then begin if result > 0 then begin
copyMem(buf, addr(s.s[strStart+s.pos]), result); copyMem(buf, addr(s.s[strStart+s.rd]), result);
inc(s.pos, result) inc(s.rd, result)
end end
end; end;
llsFile: result := readBuffer(s.f, buf, bufLen); llsFile: result := readBuffer(s.f, buf, bufLen);
@ -152,17 +153,17 @@ begin
llsNone: result := ''; llsNone: result := '';
llsString: begin llsString: begin
result := ''; result := '';
while s.pos < length(s.s) do begin while s.rd < length(s.s) do begin
case s.s[s.pos+strStart] of case s.s[s.rd+strStart] of
#13: begin #13: begin
inc(s.pos); inc(s.rd);
if s.s[s.pos+strStart] = #10 then inc(s.pos); if s.s[s.rd+strStart] = #10 then inc(s.rd);
break break
end; end;
#10: begin inc(s.pos); break end; #10: begin inc(s.rd); break end;
else begin else begin
addChar(result, s.s[s.pos+strStart]); addChar(result, s.s[s.rd+strStart]);
inc(s.pos); inc(s.rd);
end end
end end
end end
@ -176,7 +177,7 @@ function LLStreamAtEnd(s: PLLStream): bool;
begin begin
case s.kind of case s.kind of
llsNone: result := true; llsNone: result := true;
llsString: result := s.pos < length(s.s); llsString: result := s.rd >= length(s.s);
llsFile: result := endOfFile(s.f); llsFile: result := endOfFile(s.f);
llsStdIn: result := false; llsStdIn: result := false;
end end
@ -186,9 +187,15 @@ procedure LLStreamWrite(s: PLLStream; const data: string); overload;
begin begin
case s.kind of case s.kind of
llsNone, llsStdIn: begin end; llsNone, llsStdIn: begin end;
llsString: add(s.s, data); llsString: begin add(s.s, data); inc(s.wr, length(data)) end;
llsFile: nimWrite(s.f, data); llsFile: nimWrite(s.f, data);
end end;
end;
procedure LLStreamWriteln(s: PLLStream; const data: string);
begin
LLStreamWrite(s, data);
LLStreamWrite(s, nl);
end; end;
procedure LLStreamWrite(s: PLLStream; data: Char); overload; procedure LLStreamWrite(s: PLLStream; data: Char); overload;
@ -197,7 +204,7 @@ var
begin begin
case s.kind of case s.kind of
llsNone, llsStdIn: begin end; llsNone, llsStdIn: begin end;
llsString: addChar(s.s, data); llsString: begin addChar(s.s, data); inc(s.wr); end;
llsFile: begin llsFile: begin
c := data; c := data;
{@discard} writeBuffer(s.f, addr(c), sizeof(c)); {@discard} writeBuffer(s.f, addr(c), sizeof(c));
@ -212,8 +219,8 @@ begin
llsString: begin llsString: begin
if bufLen > 0 then begin if bufLen > 0 then begin
setLength(s.s, length(s.s) + bufLen); setLength(s.s, length(s.s) + bufLen);
copyMem(addr(s.s[strStart+s.pos]), buf, bufLen); copyMem(addr(s.s[strStart+s.wr]), buf, bufLen);
inc(s.pos, bufLen); inc(s.wr, bufLen);
end end
end; end;
llsFile: {@discard} writeBuffer(s.f, buf, bufLen); llsFile: {@discard} writeBuffer(s.f, buf, bufLen);
@ -229,9 +236,9 @@ begin
case s.kind of case s.kind of
llsNone, llsStdIn: result := ''; llsNone, llsStdIn: result := '';
llsString: begin llsString: begin
if s.pos = 0 then result := s.s if s.rd = 0 then result := s.s
else result := ncopy(s.s, s.pos+strStart); else result := ncopy(s.s, s.rd+strStart);
s.pos := length(s.s); s.rd := length(s.s);
end; end;
llsFile: begin llsFile: begin
result := newString(bufSize); result := newString(bufSize);

View file

@ -177,7 +177,7 @@ begin
if ambiguousCheck and IntSetContains(c.AmbiguousSymbols, result.id) then if ambiguousCheck and IntSetContains(c.AmbiguousSymbols, result.id) then
liMessage(n.info, errUseQualifier, n.sym.name.s) liMessage(n.info, errUseQualifier, n.sym.name.s)
end; end;
nkDotExpr, nkQualified: begin nkDotExpr: begin
result := nil; result := nil;
m := qualifiedLookUp(c, n.sons[0], false); m := qualifiedLookUp(c, n.sons[0], false);
if (m <> nil) and (m.kind = skModule) then begin if (m <> nil) and (m.kind = skModule) then begin
@ -237,7 +237,7 @@ begin
result := InitIdentIter(o.it, c.tab.stack[o.stackPtr], n.sym.name); result := InitIdentIter(o.it, c.tab.stack[o.stackPtr], n.sym.name);
end; *) end; *)
end; end;
nkDotExpr, nkQualified: begin nkDotExpr: begin
o.mode := oimOtherModule; o.mode := oimOtherModule;
o.m := qualifiedLookUp(c, n.sons[0], false); o.m := qualifiedLookUp(c, n.sons[0], false);
if (o.m <> nil) and (o.m.kind = skModule) then begin if (o.m <> nil) and (o.m.kind = skModule) then begin

View file

@ -15,10 +15,10 @@ unit main;
interface interface
uses uses
nsystem, llstream, strutils, ast, astalgo, scanner, pnimsyn, rnimsyn, nsystem, llstream, strutils, ast, astalgo, scanner, syntaxes, rnimsyn,
options, msgs, nos, lists, condsyms, paslex, pasparse, rodread, rodwrite, options, msgs, nos, lists, condsyms, paslex, pasparse, rodread, rodwrite,
ropes, trees, wordrecg, sem, semdata, idents, passes, docgen, ropes, trees, wordrecg, sem, semdata, idents, passes, docgen,
extccomp, cgen, ecmasgen, platform, ptmplsyn, interact, nimconf, importer, extccomp, cgen, ecmasgen, platform, interact, nimconf, importer,
passaux, depends, transf, evals, types; passaux, depends, transf, evals, types;
procedure MainCommand(const cmd, filename: string); procedure MainCommand(const cmd, filename: string);
@ -69,7 +69,7 @@ begin
{@emit} {@emit}
result.id := -1; // for better error checking result.id := -1; // for better error checking
result.kind := skModule; result.kind := skModule;
result.name := getIdent(getFileTrunk(filename)); result.name := getIdent(extractFileTrunk(filename));
result.owner := result; // a module belongs to itself result.owner := result; // a module belongs to itself
result.info := newLineInfo(filename, 1, 1); result.info := newLineInfo(filename, 1, 1);
include(result.flags, sfUsed); include(result.flags, sfUsed);
@ -101,7 +101,7 @@ var
f: string; f: string;
begin begin
rd := nil; rd := nil;
f := appendFileExt(filename, nimExt); f := addFileExt(filename, nimExt);
result := newModule(filename); result := newModule(filename);
if isMainFile then include(result.flags, sfMainModule); if isMainFile then include(result.flags, sfMainModule);
if isSystemFile then include(result.flags, sfSystemModule); if isSystemFile then include(result.flags, sfSystemModule);
@ -118,8 +118,8 @@ end;
procedure CompileProject(const filename: string); procedure CompileProject(const filename: string);
begin begin
{@discard} CompileModule( {@discard} CompileModule(
JoinPath(options.libpath, appendFileExt('system', nimExt)), false, true); JoinPath(options.libpath, addFileExt('system', nimExt)), false, true);
{@discard} CompileModule(appendFileExt(filename, nimExt), true, false); {@discard} CompileModule(addFileExt(filename, nimExt), true, false);
end; end;
procedure semanticPasses; procedure semanticPasses;
@ -185,7 +185,7 @@ begin
// load system module: // load system module:
{@discard} CompileModule( {@discard} CompileModule(
JoinPath(options.libpath, appendFileExt('system', nimExt)), false, true); JoinPath(options.libpath, addFileExt('system', nimExt)), false, true);
m := newModule('stdin'); m := newModule('stdin');
m.id := getID(); m.id := getID();
@ -228,7 +228,7 @@ procedure CommandExportSymbols(const filename: string);
var var
module: PNode; module: PNode;
begin begin
module := parseFile(appendFileExt(filename, NimExt)); module := parseFile(addFileExt(filename, NimExt));
if module <> nil then begin if module <> nil then begin
exSymbols(module); exSymbols(module);
renderModule(module, getOutFile(filename, 'pretty.'+NimExt)); renderModule(module, getOutFile(filename, 'pretty.'+NimExt));
@ -239,7 +239,7 @@ procedure CommandPretty(const filename: string);
var var
module: PNode; module: PNode;
begin begin
module := parseFile(appendFileExt(filename, NimExt)); module := parseFile(addFileExt(filename, NimExt));
if module <> nil then if module <> nil then
renderModule(module, getOutFile(filename, 'pretty.'+NimExt)); renderModule(module, getOutFile(filename, 'pretty.'+NimExt));
end; end;
@ -255,7 +255,7 @@ begin
fillChar(tok, sizeof(tok), 0); fillChar(tok, sizeof(tok), 0);
fillChar(L, sizeof(L), 0); fillChar(L, sizeof(L), 0);
{@emit} {@emit}
f := appendFileExt(filename, 'pas'); f := addFileExt(filename, 'pas');
stream := LLStreamOpen(f, fmRead); stream := LLStreamOpen(f, fmRead);
if stream <> nil then begin if stream <> nil then begin
OpenLexer(L, f, stream); OpenLexer(L, f, stream);
@ -277,7 +277,7 @@ var
f: string; f: string;
stream: PLLStream; stream: PLLStream;
begin begin
f := appendFileExt(filename, 'pas'); f := addFileExt(filename, 'pas');
stream := LLStreamOpen(f, fmRead); stream := LLStreamOpen(f, fmRead);
if stream <> nil then begin if stream <> nil then begin
OpenPasParser(p, f, stream); OpenPasParser(p, f, stream);
@ -300,7 +300,7 @@ begin
{@ignore} {@ignore}
fillChar(tok^, sizeof(tok^), 0); fillChar(tok^, sizeof(tok^), 0);
{@emit} {@emit}
f := appendFileExt(filename, nimExt); f := addFileExt(filename, nimExt);
stream := LLStreamOpen(f, fmRead); stream := LLStreamOpen(f, fmRead);
if stream <> nil then begin if stream <> nil then begin
openLexer(L, f, stream); openLexer(L, f, stream);
@ -331,8 +331,7 @@ begin
prependStr(searchPaths, dir); prependStr(searchPaths, dir);
end; end;
setID(100); setID(100);
passes.gIncludeFile := parseFile; passes.gIncludeFile := syntaxes.parseFile;
passes.gIncludeTmplFile := ptmplsyn.parseTmplFile;
passes.gImportModule := importModule; passes.gImportModule := importModule;
case whichKeyword(cmd) of case whichKeyword(cmd) of
@ -408,7 +407,7 @@ begin
wParse: begin wParse: begin
gCmd := cmdParse; gCmd := cmdParse;
wantFile(filename); wantFile(filename);
{@discard} parseFile(appendFileExt(filename, nimExt)); {@discard} parseFile(addFileExt(filename, nimExt));
end; end;
wScan: begin wScan: begin
gCmd := cmdScan; gCmd := cmdScan;

View file

@ -127,7 +127,7 @@ type
errOverOrUnderflow, errOverOrUnderflow,
errCannotEvalXBecauseIncompletelyDefined, errCannotEvalXBecauseIncompletelyDefined,
errChrExpectsRange0_255, errChrExpectsRange0_255,
errDotRequiresRecordOrObjectType, errDynlibRequiresExportc,
errUndeclaredFieldX, errUndeclaredFieldX,
errNilAccess, errNilAccess,
errIndexOutOfBounds, errIndexOutOfBounds,
@ -182,7 +182,7 @@ type
errButExpectedX, errButExpectedX,
errAmbiguousCallXYZ, errAmbiguousCallXYZ,
errWrongNumberOfArguments, errWrongNumberOfArguments,
errInlineProcHasNoAddress, errXCannotBePassedToProcVar,
errXCannotBeInParamDecl, errXCannotBeInParamDecl,
errPragmaOnlyInHeaderOfProc, errPragmaOnlyInHeaderOfProc,
errImplOfXNotAllowed, errImplOfXNotAllowed,
@ -215,7 +215,6 @@ type
errATypeHasNoValue, errATypeHasNoValue,
errXisNoType, errXisNoType,
errCircumNeedsPointer, errCircumNeedsPointer,
errInvalidContextForBuiltinX,
errInvalidExpression, errInvalidExpression,
errInvalidExpressionX, errInvalidExpressionX,
errEnumHasNoValueX, errEnumHasNoValueX,
@ -272,6 +271,7 @@ type
warnUnknownSubstitutionX, warnUnknownSubstitutionX,
warnLanguageXNotSupported, warnLanguageXNotSupported,
warnCommentXIgnored, warnCommentXIgnored,
warnXisPassedToProcVar,
warnUser, warnUser,
hintSuccess, hintSuccess,
hintSuccessX, hintSuccessX,
@ -365,7 +365,7 @@ const
'over- or underflow', 'over- or underflow',
'cannot evalutate ''$1'' because type is not defined completely', 'cannot evalutate ''$1'' because type is not defined completely',
'''chr'' expects an int in the range 0..255', '''chr'' expects an int in the range 0..255',
'''.'' requires a record or object type', '''dynlib'' requires ''exportc''',
'undeclared field: ''$1''', 'undeclared field: ''$1''',
'attempt to access a nil address', 'attempt to access a nil address',
'index out of bounds', 'index out of bounds',
@ -420,7 +420,7 @@ const
'but expected ''$1''', 'but expected ''$1''',
'ambiguous call; both $1 and $2 match for: $3', 'ambiguous call; both $1 and $2 match for: $3',
'wrong number of arguments', 'wrong number of arguments',
'an inline proc has no address', '''$1'' cannot be passed to a procvar',
'$1 cannot be declared in parameter declaration', '$1 cannot be declared in parameter declaration',
'pragmas are only in the header of a proc allowed', 'pragmas are only in the header of a proc allowed',
'implementation of ''$1'' is not allowed', 'implementation of ''$1'' is not allowed',
@ -453,7 +453,6 @@ const
'a type has no value', 'a type has no value',
'invalid type: ''$1''', 'invalid type: ''$1''',
'''^'' needs a pointer or reference type', '''^'' needs a pointer or reference type',
'invalid context for builtin ''$1''',
'invalid expression', 'invalid expression',
'invalid expression: ''$1''', 'invalid expression: ''$1''',
'enum has no value ''$1''', 'enum has no value ''$1''',
@ -510,6 +509,7 @@ const
'unknown substitution ''$1'' [UnknownSubstitutionX]', 'unknown substitution ''$1'' [UnknownSubstitutionX]',
'language ''$1'' not supported [LanguageXNotSupported]', 'language ''$1'' not supported [LanguageXNotSupported]',
'comment ''$1'' ignored [CommentXIgnored]', 'comment ''$1'' ignored [CommentXIgnored]',
'''$1'' is passed to a procvar; deprecated [XisPassedToProcVar]',
'$1 [User]', '$1 [User]',
'operation successful [Success]', 'operation successful [Success]',
'operation successful ($1 lines compiled; $2 sec total) [SuccessX]', 'operation successful ($1 lines compiled; $2 sec total) [SuccessX]',
@ -526,7 +526,7 @@ const
'$1 [User]' '$1 [User]'
); );
const const
WarningsToStr: array [0..13] of string = ( WarningsToStr: array [0..14] of string = (
'CannotOpenFile', 'CannotOpenFile',
'OctalEscape', 'OctalEscape',
'XIsNeverRead', 'XIsNeverRead',
@ -540,6 +540,7 @@ const
'UnknownSubstitutionX', 'UnknownSubstitutionX',
'LanguageXNotSupported', 'LanguageXNotSupported',
'CommentXIgnored', 'CommentXIgnored',
'XisPassedToProcVar',
'User' 'User'
); );
const const

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (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.
@ -28,14 +28,26 @@ var
procedure ProcessCmdLine(pass: TCmdLinePass; var command, filename: string); procedure ProcessCmdLine(pass: TCmdLinePass; var command, filename: string);
var var
p: TOptParser; p: TOptParser;
bracketLe: int;
key, val: string;
begin begin
p := parseopt.init(); p := parseopt.init();
while true do begin while true do begin
parseopt.next(p); parseopt.next(p);
case p.kind of case p.kind of
cmdEnd: break; cmdEnd: break;
cmdLongOption, cmdShortOption: cmdLongOption, cmdShortOption: begin
// hint[X]:off is parsed as (p.key = "hint[X]", p.val = "off")
// we fix this here
bracketLe := strutils.find(p.key, '[');
if bracketLe >= strStart then begin
key := ncopy(p.key, strStart, bracketLe-1);
val := ncopy(p.key, bracketLe+1) +{&} ':' +{&} p.val;
ProcessSwitch(key, val, pass, cmdLineInfo);
end
else
ProcessSwitch(p.key, p.val, pass, cmdLineInfo); ProcessSwitch(p.key, p.val, pass, cmdLineInfo);
end;
cmdArgument: begin cmdArgument: begin
if command = '' then command := p.key if command = '' then command := p.key
else if filename = '' then begin else if filename = '' then begin

View file

@ -74,7 +74,7 @@ function GetConfigDir(): string;
procedure SplitFilename(const filename: string; out name, extension: string); procedure SplitFilename(const filename: string; out name, extension: string);
function ExistsFile(const filename: string): Boolean; function ExistsFile(const filename: string): Boolean;
function AppendFileExt(const filename, ext: string): string; function AddFileExt(const filename, ext: string): string;
function ChangeFileExt(const filename, ext: string): string; function ChangeFileExt(const filename, ext: string): string;
procedure createDir(const dir: string); procedure createDir(const dir: string);
@ -84,8 +84,20 @@ function UnixToNativePath(const path: string): string;
function sameFile(const path1, path2: string): boolean; function sameFile(const path1, path2: string): boolean;
function extractFileTrunk(const filename: string): string;
implementation implementation
function extractFileTrunk(const filename: string): string;
var
f, e, dir: string;
begin
splitPath(filename, dir, f);
splitFilename(f, result, e);
end;
function GetConfigDir(): string; function GetConfigDir(): string;
begin begin
{$ifdef windows} {$ifdef windows}
@ -103,7 +115,7 @@ end;
function UnixToNativePath(const path: string): string; function UnixToNativePath(const path: string): string;
begin begin
if dirSep <> '/' then if dirSep <> '/' then
result := replaceStr(path, '/', dirSep) result := replace(path, '/', dirSep)
else else
result := path; result := path;
end; end;
@ -150,7 +162,7 @@ begin
result := extSep + ext result := extSep + ext
end; end;
function AppendFileExt(const filename, ext: string): string; function AddFileExt(const filename, ext: string): string;
var var
extPos: int; extPos: int;
begin begin

View file

@ -55,6 +55,11 @@ const
snil = ''; snil = '';
type
TStringSeq = array of string;
TCharSet = set of Char;
type type
Natural = 0..high(int); Natural = 0..high(int);
Positive = 1..high(int); Positive = 1..high(int);
@ -214,9 +219,11 @@ function readFile(const filename: string): string;
procedure nimWrite(var f: tBinaryFile; const str: string); overload; procedure nimWrite(var f: tBinaryFile; const str: string); overload;
procedure add(var x: string; const y: string); procedure add(var x: string; const y: string); overload;
// Pascal version of string appending. Terminating zero is ignored. // Pascal version of string appending. Terminating zero is ignored.
procedure add(var s: TStringSeq; const y: string); overload;
function isNil(s: string): bool; function isNil(s: string): bool;
implementation implementation
@ -238,6 +245,15 @@ begin
else x := x + y; else x := x + y;
end end
end; end;
procedure add(var s: TStringSeq; const y: string); overload;
var
L: int;
begin
L := length(s);
setLength(s, L+1);
s[L] := y;
end;
{@emit} {@emit}
function alloc(size: int): Pointer; function alloc(size: int): Pointer;

View file

@ -31,10 +31,10 @@ const
//cog.outl('VersionMinor = %s;' % ver[1]) //cog.outl('VersionMinor = %s;' % ver[1])
//cog.outl('VersionPatch = %s;' % ver[2]) //cog.outl('VersionPatch = %s;' % ver[2])
//]]] //]]]
VersionAsString = '0.8.1'; VersionAsString = '0.8.2';
VersionMajor = 0; VersionMajor = 0;
VersionMinor = 8; VersionMinor = 8;
VersionPatch = 1; VersionPatch = 2;
//[[[[end]]]] //[[[[end]]]]
implementation implementation

View file

@ -101,6 +101,7 @@ var
gCmd: TCommands = cmdNone; // the command gCmd: TCommands = cmdNone; // the command
gVerbosity: int; // how verbose the compiler is gVerbosity: int; // how verbose the compiler is
gNumberOfProcessors: int; // number of processors
function FindFile(const f: string): string; function FindFile(const f: string): string;
@ -111,7 +112,6 @@ const
HtmlExt = 'html'; HtmlExt = 'html';
TexExt = 'tex'; TexExt = 'tex';
IniExt = 'ini'; IniExt = 'ini';
TmplExt = 'tmpl';
DocConfig = 'nimdoc.cfg'; DocConfig = 'nimdoc.cfg';
DocTexConfig = 'nimdoc.tex.cfg'; DocTexConfig = 'nimdoc.tex.cfg';
@ -124,8 +124,6 @@ function toGeneratedFile(const path, ext: string): string;
function getPrefixDir: string; function getPrefixDir: string;
// gets the application directory // gets the application directory
function getFileTrunk(const filename: string): string;
// additional configuration variables: // additional configuration variables:
var var
gConfigVars: PStringTable; gConfigVars: PStringTable;
@ -185,14 +183,6 @@ begin
SplitPath(appdir, result, bin); SplitPath(appdir, result, bin);
end; end;
function getFileTrunk(const filename: string): string;
var
f, e, dir: string;
begin
splitPath(filename, dir, f);
splitFilename(f, result, e);
end;
function shortenDir(const dir: string): string; function shortenDir(const dir: string): string;
var var
prefix: string; prefix: string;

View file

@ -18,13 +18,41 @@ interface
uses uses
nsystem, nos; nsystem, nos;
function executeCommand(const cmd: string): int; type
TProcessOption = (poEchoCmd, poUseShell, poStdErrToStdOut, poParentStreams);
TProcessOptions = set of TProcessOption;
function execCmd(const cmd: string): int;
function execProcesses(const cmds: array of string;
options: TProcessOptions;
n: int): int;
function countProcessors(): int;
implementation implementation
function executeCommand(const cmd: string): int; function execCmd(const cmd: string): int;
begin begin
writeln(output, cmd);
result := executeShellCommand(cmd); result := executeShellCommand(cmd);
end; end;
function execProcesses(const cmds: array of string;
options: TProcessOptions;
n: int): int;
var
i: int;
begin
result := 0;
for i := 0 to high(cmds) do begin
//if poEchoCmd in options then writeln(output, cmds[i]);
result := max(result, execCmd(cmds[i]))
end
end;
function countProcessors(): int;
begin
result := 1;
end;
end. end.

View file

@ -61,7 +61,7 @@ const
('len', 'length'), ('len', 'length'),
('setlength', 'setlen') ('setlength', 'setlen')
); );
nimReplacements: array [1..34] of TReplaceTuple = ( nimReplacements: array [1..35] of TReplaceTuple = (
('nimread', 'read'), ('nimread', 'read'),
('nimwrite', 'write'), ('nimwrite', 'write'),
('nimclosefile', 'close'), ('nimclosefile', 'close'),
@ -98,7 +98,8 @@ const
('ttextfile', 'tfile'), ('ttextfile', 'tfile'),
('tbinaryfile', 'tfile'), ('tbinaryfile', 'tfile'),
('strstart', '0'+''), ('strstart', '0'+''),
('nl', '"\n"') ('nl', '"\n"'),
('tostring', '$'+'')
{, {,
('NL', '"\n"'), ('NL', '"\n"'),
('tabulator', '''\t'''), ('tabulator', '''\t'''),
@ -455,7 +456,7 @@ begin
skipCom(p, result); skipCom(p, result);
if p.tok.xkind = pxSymbol then begin if p.tok.xkind = pxSymbol then begin
a := result; a := result;
result := newNodeI(nkQualified, a.info); result := newNodeI(nkDotExpr, a.info);
addSon(result, a); addSon(result, a);
addSon(result, createIdentNodeP(p.tok.ident, p)); addSon(result, createIdentNodeP(p.tok.ident, p));
getTok(p); getTok(p);
@ -1279,6 +1280,13 @@ begin
noBody := true; noBody := true;
getTok(p); opt(p, pxSemicolon); getTok(p); opt(p, pxSemicolon);
end; end;
wProcVar: begin
// This is a fake for the Nimrod compiler. There is no ``procvar``
// directive in Pascal.
if result = nil then result := newNodeP(nkPragma, p);
addSon(result, newIdentNodeP(getIdent('procvar'), p));
getTok(p); opt(p, pxSemicolon);
end;
wVarargs: begin wVarargs: begin
if result = nil then result := newNodeP(nkPragma, p); if result = nil then result := newNodeP(nkPragma, p);
addSon(result, newIdentNodeP(getIdent('varargs'), p)); addSon(result, newIdentNodeP(getIdent('varargs'), p));

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -19,7 +19,7 @@ uses
nsystem, charsets, strutils, nsystem, charsets, strutils,
lists, options, ast, astalgo, llstream, lists, options, ast, astalgo, llstream,
msgs, platform, nos, condsyms, idents, rnimsyn, types, msgs, platform, nos, condsyms, idents, rnimsyn, types,
extccomp, nmath, magicsys, nversion, nimsets, pnimsyn, ntime, rodread; extccomp, nmath, magicsys, nversion, nimsets, syntaxes, ntime, rodread;
type type
TPassContext = object(NObject) // the pass's context TPassContext = object(NObject) // the pass's context
@ -61,7 +61,6 @@ function astNeeded(s: PSym): bool;
var var
gImportModule: function (const filename: string): PSym; gImportModule: function (const filename: string): PSym;
gIncludeFile: function (const filename: string): PNode; gIncludeFile: function (const filename: string): PNode;
gIncludeTmplFile: function (const filename: string): PNode;
implementation implementation
@ -165,7 +164,7 @@ end;
procedure processModule(module: PSym; const filename: string; procedure processModule(module: PSym; const filename: string;
stream: PLLStream; rd: PRodReader); stream: PLLStream; rd: PRodReader);
var var
p: TParser; p: TParsers;
n: PNode; n: PNode;
a: TPassContextArray; a: TPassContextArray;
s: PLLStream; s: PLLStream;
@ -183,13 +182,13 @@ begin
else else
s := stream; s := stream;
while true do begin while true do begin
openParser(p, filename, s); openParsers(p, filename, s);
while true do begin while true do begin
n := parseTopLevelStmt(p); n := parseTopLevelStmt(p);
if n = nil then break; if n = nil then break;
processTopLevelStmt(n, a) processTopLevelStmt(n, a)
end; end;
closeParser(p); closeParsers(p);
if s.kind <> llsStdIn then break; if s.kind <> llsStdIn then break;
end; end;
closePasses(a); closePasses(a);

1499
nim/pbraces.pas Executable file

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
@ -59,9 +59,8 @@ type
TInfoOS = record{@tuple} TInfoOS = record{@tuple}
name: string; name: string;
parDir: string; parDir: string;
dllExt: string; dllFrmt: string;
altDirSep: string; altDirSep: string;
dllPrefix: string;
objExt: string; objExt: string;
newLine: string; newLine: string;
pathSep: string; pathSep: string;
@ -77,9 +76,8 @@ const
( (
name: 'DOS'; name: 'DOS';
parDir: '..'; parDir: '..';
dllExt: '.dll'; dllFrmt: '$1.dll';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: '';
objExt: '.obj'; objExt: '.obj';
newLine: #13#10; newLine: #13#10;
pathSep: ';'+''; pathSep: ';'+'';
@ -93,9 +91,8 @@ const
( (
name: 'Windows'; name: 'Windows';
parDir: '..'; parDir: '..';
dllExt: '.dll'; dllFrmt: '$1.dll';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: '';
objExt: '.obj'; objExt: '.obj';
newLine: #13#10; newLine: #13#10;
pathSep: ';'+''; pathSep: ';'+'';
@ -109,9 +106,8 @@ const
( (
name: 'OS2'; name: 'OS2';
parDir: '..'; parDir: '..';
dllExt: '.dll'; dllFrmt: '$1.dll';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: '';
objExt: '.obj'; objExt: '.obj';
newLine: #13#10; newLine: #13#10;
pathSep: ';'+''; pathSep: ';'+'';
@ -125,9 +121,8 @@ const
( (
name: 'Linux'; name: 'Linux';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -141,9 +136,8 @@ const
( (
name: 'MorphOS'; name: 'MorphOS';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -157,9 +151,8 @@ const
( (
name: 'SkyOS'; name: 'SkyOS';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -173,9 +166,8 @@ const
( (
name: 'Solaris'; name: 'Solaris';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -189,9 +181,8 @@ const
( (
name: 'Irix'; name: 'Irix';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -205,9 +196,8 @@ const
( (
name: 'NetBSD'; name: 'NetBSD';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -221,9 +211,8 @@ const
( (
name: 'FreeBSD'; name: 'FreeBSD';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -237,9 +226,8 @@ const
( (
name: 'OpenBSD'; name: 'OpenBSD';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -253,9 +241,8 @@ const
( (
name: 'AIX'; name: 'AIX';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -269,9 +256,8 @@ const
( (
name: 'PalmOS'; name: 'PalmOS';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -285,9 +271,8 @@ const
( (
name: 'QNX'; name: 'QNX';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -301,9 +286,8 @@ const
( (
name: 'Amiga'; name: 'Amiga';
parDir: '..'; parDir: '..';
dllExt: '.library'; dllFrmt: '$1.library';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: '';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -317,9 +301,8 @@ const
( (
name: 'Atari'; name: 'Atari';
parDir: '..'; parDir: '..';
dllExt: '.dll'; dllFrmt: '$1.dll';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: '';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -333,9 +316,8 @@ const
( (
name: 'Netware'; name: 'Netware';
parDir: '..'; parDir: '..';
dllExt: '.nlm'; dllFrmt: '$1.nlm';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: '';
objExt: ''; objExt: '';
newLine: #13#10; newLine: #13#10;
pathSep: ':'+''; pathSep: ':'+'';
@ -349,9 +331,8 @@ const
( (
name: 'MacOS'; name: 'MacOS';
parDir: '::'; parDir: '::';
dllExt: 'Lib'; dllFrmt: '$1Lib';
altDirSep: ':'+''; altDirSep: ':'+'';
dllPrefix: '';
objExt: '.o'; objExt: '.o';
newLine: #13+''; newLine: #13+'';
pathSep: ','+''; pathSep: ','+'';
@ -365,9 +346,8 @@ const
( (
name: 'MacOSX'; name: 'MacOSX';
parDir: '..'; parDir: '..';
dllExt: '.dylib'; dllFrmt: 'lib$1.dylib';
altDirSep: ':'+''; altDirSep: ':'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -381,9 +361,8 @@ const
( (
name: 'EcmaScript'; name: 'EcmaScript';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';
@ -397,9 +376,8 @@ const
( (
name: 'NimrodVM'; name: 'NimrodVM';
parDir: '..'; parDir: '..';
dllExt: '.so'; dllFrmt: 'lib$1.so';
altDirSep: '/'+''; altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o'; objExt: '.o';
newLine: #10+''; newLine: #10+'';
pathSep: ':'+''; pathSep: ':'+'';

View file

@ -22,7 +22,7 @@ interface
uses uses
nsystem, llstream, scanner, idents, strutils, ast, msgs; nsystem, llstream, scanner, idents, strutils, ast, msgs;
function ParseFile(const filename: string): PNode; // function ParseFile(const filename: string): PNode;
type type
TParser = record // a TParser object represents a module that TParser = record // a TParser object represents a module that
@ -31,10 +31,7 @@ type
tok: PToken; // the current token tok: PToken; // the current token
end; end;
function ParseModule(var p: TParser): PNode; function ParseAll(var p: TParser): PNode;
// Note: The module's tree must always be valid.
function parseExpr(var p: TParser): PNode;
function parseStmt(var p: TParser): PNode;
procedure openParser(var p: TParser; const filename: string; procedure openParser(var p: TParser; const filename: string;
inputstream: PLLStream); inputstream: PLLStream);
@ -44,21 +41,43 @@ function parseTopLevelStmt(var p: TParser): PNode;
// implements an iterator. Returns the next top-level statement or nil if end // implements an iterator. Returns the next top-level statement or nil if end
// of stream. // of stream.
implementation
function ParseFile(const filename: string): PNode; // helpers for the other parsers
var function getPrecedence(tok: PToken): int;
p: TParser; function isOperator(tok: PToken): bool;
f: TBinaryFile;
begin procedure getTok(var p: TParser);
if not OpenFile(f, filename) then begin
rawMessage(errCannotOpenFile, filename); procedure parMessage(const p: TParser; const msg: TMsgKind;
exit const arg: string = '');
end; procedure skipComment(var p: TParser; node: PNode);
OpenParser(p, filename, LLStreamOpen(f));
result := ParseModule(p); function newNodeP(kind: TNodeKind; const p: TParser): PNode;
CloseParser(p); function newIntNodeP(kind: TNodeKind; const intVal: BiggestInt;
end; const p: TParser): PNode;
function newFloatNodeP(kind: TNodeKind; const floatVal: BiggestFloat;
const p: TParser): PNode;
function newStrNodeP(kind: TNodeKind; const strVal: string;
const p: TParser): PNode;
function newIdentNodeP(ident: PIdent; const p: TParser): PNode;
procedure expectIdentOrKeyw(const p: TParser);
procedure ExpectIdent(const p: TParser);
procedure expectIdentOrOpr(const p: TParser);
function parLineInfo(const p: TParser): TLineInfo;
procedure Eat(var p: TParser; TokType: TTokType);
procedure skipInd(var p: TParser);
procedure optSad(var p: TParser);
procedure optInd(var p: TParser; n: PNode);
procedure indAndComment(var p: TParser; n: PNode);
function parseSymbol(var p: TParser): PNode;
function accExpr(var p: TParser): PNode;
implementation
procedure initParser(var p: TParser); procedure initParser(var p: TParser);
begin begin
@ -211,6 +230,9 @@ end;
// ------------------- Expression parsing ------------------------------------ // ------------------- Expression parsing ------------------------------------
function parseExpr(var p: TParser): PNode; forward;
function parseStmt(var p: TParser): PNode; forward;
function parseTypeDesc(var p: TParser): PNode; forward; function parseTypeDesc(var p: TParser): PNode; forward;
function parseParamList(var p: TParser): PNode; forward; function parseParamList(var p: TParser): PNode; forward;
@ -364,15 +386,13 @@ begin
addSon(result, optExpr(p)); addSon(result, optExpr(p));
end; end;
function parseTypeDescK(var p: TParser): PNode; forward; function indexExpr(var p: TParser): PNode;
// indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr]
function namedTypeOrExpr(var p: TParser): PNode;
var var
a, b: PNode; a, b: PNode;
begin begin
case p.tok.tokType of if p.tok.tokType = tkDotDot then
tkDotDot: result := dotdotExpr(p); result := dotdotExpr(p)
tkVar, tkRef, tkPtr, tkProc, tkTuple, tkType: result := parseTypeDescK(p);
else begin else begin
a := parseExpr(p); a := parseExpr(p);
case p.tok.tokType of case p.tok.tokType of
@ -380,27 +400,21 @@ begin
result := newNodeP(nkExprEqExpr, p); result := newNodeP(nkExprEqExpr, p);
addSon(result, a); addSon(result, a);
getTok(p); getTok(p);
//optInd(p, result); if p.tok.tokType = tkDotDot then
case p.tok.tokType of addSon(result, dotdotExpr(p))
tkVar, tkRef, tkPtr, tkProc:
addSon(result, parseTypeDescK(p));
tkDotDot: addSon(result, dotdotExpr(p));
else begin else begin
b := parseExpr(p); b := parseExpr(p);
if p.tok.tokType = tkDotDot then if p.tok.tokType = tkDotDot then b := dotdotExpr(p, b);
b := dotdotExpr(p, b);
addSon(result, b); addSon(result, b);
end end
end
end; end;
tkDotDot: result := dotdotExpr(p, a); tkDotDot: result := dotdotExpr(p, a);
else result := a else result := a
end end
end end
end
end; end;
function namedTypeOrExprList(var p: TParser; first: PNode): PNode; function indexExprList(var p: TParser; first: PNode): PNode;
var var
a: PNode; a: PNode;
begin begin
@ -410,7 +424,7 @@ begin
optInd(p, result); optInd(p, result);
while (p.tok.tokType <> tkBracketRi) and (p.tok.tokType <> tkEof) while (p.tok.tokType <> tkBracketRi) and (p.tok.tokType <> tkEof)
and (p.tok.tokType <> tkSad) do begin and (p.tok.tokType <> tkSad) do begin
a := namedTypeOrExpr(p); a := indexExpr(p);
addSon(result, a); addSon(result, a);
if p.tok.tokType <> tkComma then break; if p.tok.tokType <> tkComma then break;
getTok(p); getTok(p);
@ -464,7 +478,7 @@ begin
getTok(p); getTok(p);
optInd(p, result); optInd(p, result);
a := result; a := result;
result := newNodeI(nkQualified, a.info); result := newNodeI(nkDotExpr, a.info);
addSon(result, a); addSon(result, a);
addSon(result, parseSymbol(p)); addSon(result, parseSymbol(p));
end; end;
@ -632,7 +646,6 @@ begin
result := newNodeP(nkNilLit, p); result := newNodeP(nkNilLit, p);
getTok(p); getTok(p);
end; end;
tkParLe: begin // () constructor tkParLe: begin // () constructor
result := exprColonEqExprList(p, nkPar, nkExprColonExpr, tkParRi, result := exprColonEqExprList(p, nkPar, nkExprColonExpr, tkParRi,
tkColon); tkColon);
@ -698,7 +711,7 @@ begin
addSon(result, a); addSon(result, a);
getTok(p); getTok(p);
end; end;
tkBracketLe: result := namedTypeOrExprList(p, result); tkBracketLe: result := indexExprList(p, result);
else break else break
end end
end end
@ -712,11 +725,6 @@ var
begin begin
v := primary(p); v := primary(p);
// expand while operators have priorities higher than 'limit' // expand while operators have priorities higher than 'limit'
(*if p.tok.tokType = tkInd then begin // BUGFIX: parser allowed too much
getTok(p);
if getPrecedence(p.tok) < 0 then
parMessage(p, errOperatorExpected, tokToStr(p.tok));
end; *)
op := p.tok; op := p.tok;
opPred := getPrecedence(p.tok); opPred := getPrecedence(p.tok);
while (opPred > limit) do begin while (opPred > limit) do begin
@ -724,8 +732,6 @@ begin
opNode := newIdentNodeP(op.ident, p); opNode := newIdentNodeP(op.ident, p);
// skip operator: // skip operator:
getTok(p); getTok(p);
//skipComment(p, opNode);
//skipInd(p);
optInd(p, opNode); optInd(p, opNode);
// read sub-expression with higher priority // read sub-expression with higher priority
@ -745,8 +751,6 @@ begin
{@discard} lowestExprAux(p, result, -1); {@discard} lowestExprAux(p, result, -1);
end; end;
function parseLambda(var p: TParser): PNode; forward;
function parseIfExpr(var p: TParser): PNode; function parseIfExpr(var p: TParser): PNode;
var var
branch: PNode; branch: PNode;
@ -755,12 +759,9 @@ begin
while true do begin while true do begin
getTok(p); // skip `if`, `elif` getTok(p); // skip `if`, `elif`
branch := newNodeP(nkElifExpr, p); branch := newNodeP(nkElifExpr, p);
//optInd(p, branch);
addSon(branch, parseExpr(p)); addSon(branch, parseExpr(p));
eat(p, tkColon); eat(p, tkColon);
//optInd(p, branch);
addSon(branch, parseExpr(p)); addSon(branch, parseExpr(p));
//optInd(p, branch);
addSon(result, branch); addSon(result, branch);
if p.tok.tokType <> tkElif then break if p.tok.tokType <> tkElif then break
end; end;
@ -770,16 +771,6 @@ begin
addSon(result, branch); addSon(result, branch);
end; end;
function parseExpr(var p: TParser): PNode;
begin
case p.tok.toktype of
tkLambda: result := parseLambda(p);
tkIf: result := parseIfExpr(p);
else result := lowestExpr(p);
end
end;
// ------------------------- pragma parser -----------------------------------
function parsePragma(var p: TParser): PNode; function parsePragma(var p: TParser): PNode;
var var
a: PNode; a: PNode;
@ -803,12 +794,203 @@ begin
parMessage(p, errTokenExpected, '.}'); parMessage(p, errTokenExpected, '.}');
end; end;
function identVis(var p: TParser): PNode; // identifier with visability
var
a: PNode;
begin
a := parseSymbol(p);
if p.tok.tokType = tkOpr then begin
result := newNodeP(nkPostfix, p);
addSon(result, newIdentNodeP(p.tok.ident, p));
addSon(result, a);
getTok(p);
end
else
result := a;
end;
function identWithPragma(var p: TParser): PNode;
var
a: PNode;
begin
a := identVis(p);
if p.tok.tokType = tkCurlyDotLe then begin
result := newNodeP(nkPragmaExpr, p);
addSon(result, a);
addSon(result, parsePragma(p));
end
else
result := a
end;
type
TDeclaredIdentFlag = (
withPragma, // identifier may have pragma
withBothOptional // both ':' and '=' parts are optional
);
TDeclaredIdentFlags = set of TDeclaredIdentFlag;
function parseIdentColonEquals(var p: TParser;
flags: TDeclaredIdentFlags): PNode;
var
a: PNode;
begin
result := newNodeP(nkIdentDefs, p);
while true do begin
case p.tok.tokType of
tkSymbol, tkAccent: begin
if withPragma in flags then
a := identWithPragma(p)
else
a := parseSymbol(p);
if a = nil then exit;
end;
else break;
end;
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
if p.tok.tokType = tkColon then begin
getTok(p); optInd(p, result);
addSon(result, parseTypeDesc(p));
end
else begin
addSon(result, nil);
if (p.tok.tokType <> tkEquals) and not (withBothOptional in flags) then
parMessage(p, errColonOrEqualsExpected, tokToStr(p.tok))
end;
if p.tok.tokType = tkEquals then begin
getTok(p); optInd(p, result);
addSon(result, parseExpr(p));
end
else
addSon(result, nil);
end;
function parseTuple(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkTupleTy, p);
getTok(p);
eat(p, tkBracketLe);
optInd(p, result);
while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
a := parseIdentColonEquals(p, {@set}[]);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, tkBracketRi);
end;
function parseParamList(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkFormalParams, p);
addSon(result, nil); // return type
if p.tok.tokType = tkParLe then begin
getTok(p);
optInd(p, result);
while true do begin
case p.tok.tokType of
tkSymbol, tkAccent: a := parseIdentColonEquals(p, {@set}[]);
tkParRi: break;
else begin parMessage(p, errTokenExpected, ')'+''); break; end;
end;
//optInd(p, a);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, tkParRi);
end;
if p.tok.tokType = tkColon then begin
getTok(p);
optInd(p, result);
result.sons[0] := parseTypeDesc(p)
end
end;
function parseProcExpr(var p: TParser; isExpr: bool): PNode;
// either a proc type or a anonymous proc
var
pragmas, params: PNode;
info: TLineInfo;
begin
info := parLineInfo(p);
getTok(p);
params := parseParamList(p);
if p.tok.tokType = tkCurlyDotLe then pragmas := parsePragma(p)
else pragmas := nil;
if (p.tok.tokType = tkEquals) and isExpr then begin
result := newNodeI(nkLambda, info);
addSon(result, nil); // no name part
addSon(result, nil); // no generic parameters
addSon(result, params);
addSon(result, pragmas);
getTok(p); skipComment(p, result);
addSon(result, parseStmt(p));
end
else begin
result := newNodeI(nkProcTy, info);
addSon(result, params);
addSon(result, pragmas);
end
end;
function parseTypeDescKAux(var p: TParser; kind: TNodeKind): PNode;
begin
result := newNodeP(kind, p);
getTok(p);
optInd(p, result);
addSon(result, parseTypeDesc(p));
end;
function parseExpr(var p: TParser): PNode;
(*
expr ::= lowestExpr
| 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
| 'var' expr
| 'ref' expr
| 'ptr' expr
| 'type' expr
| 'tuple' tupleDesc
| 'proc' paramList [pragma] ['=' stmt]
*)
begin
case p.tok.toktype of
tkVar: result := parseTypeDescKAux(p, nkVarTy);
tkRef: result := parseTypeDescKAux(p, nkRefTy);
tkPtr: result := parseTypeDescKAux(p, nkPtrTy);
tkType: result := parseTypeDescKAux(p, nkTypeOfExpr);
tkTuple: result := parseTuple(p);
tkProc: result := parseProcExpr(p, true);
tkIf: result := parseIfExpr(p);
else result := lowestExpr(p);
end
end;
function parseTypeDesc(var p: TParser): PNode;
begin
if p.tok.toktype = tkProc then result := parseProcExpr(p, false)
else result := parseExpr(p);
end;
// ---------------------- statement parser ------------------------------------ // ---------------------- statement parser ------------------------------------
function isExprStart(const p: TParser): bool; function isExprStart(const p: TParser): bool;
begin begin
case p.tok.tokType of case p.tok.tokType of
tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkLambda, tkBind, tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkProc, tkBind,
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit: result := true; tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit,
tkVar, tkRef, tkPtr, tkTuple, tkType: result := true;
else result := false; else result := false;
end; end;
end; end;
@ -1051,7 +1233,6 @@ begin
addSon(result, branch); addSon(result, branch);
if p.tok.tokType <> tkElif then break if p.tok.tokType <> tkElif then break
end; end;
//PrintTok(p.tok);
if p.tok.tokType = tkElse then begin if p.tok.tokType = tkElse then begin
branch := newNodeP(nkElse, p); branch := newNodeP(nkElse, p);
eat(p, tkElse); eat(p, tkColon); eat(p, tkElse); eat(p, tkColon);
@ -1201,175 +1382,6 @@ begin
getTok(p); getTok(p);
end; end;
function identVis(var p: TParser): PNode; // identifier with visability
var
a: PNode;
begin
a := parseSymbol(p);
if p.tok.tokType = tkOpr then begin
result := newNodeP(nkPostfix, p);
addSon(result, newIdentNodeP(p.tok.ident, p));
addSon(result, a);
getTok(p);
end
else
result := a;
end;
function identWithPragma(var p: TParser): PNode;
var
a: PNode;
begin
a := identVis(p);
if p.tok.tokType = tkCurlyDotLe then begin
result := newNodeP(nkPragmaExpr, p);
addSon(result, a);
addSon(result, parsePragma(p));
end
else
result := a
end;
type
TDeclaredIdentFlag = (
withPragma, // identifier may have pragma
withBothOptional // both ':' and '=' parts are optional
);
TDeclaredIdentFlags = set of TDeclaredIdentFlag;
function parseIdentColonEquals(var p: TParser;
flags: TDeclaredIdentFlags): PNode;
var
a: PNode;
begin
result := newNodeP(nkIdentDefs, p);
while true do begin
case p.tok.tokType of
tkSymbol, tkAccent: begin
if withPragma in flags then
a := identWithPragma(p)
else
a := parseSymbol(p);
if a = nil then exit;
end;
else break;
end;
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
if p.tok.tokType = tkColon then begin
getTok(p); optInd(p, result);
addSon(result, parseTypeDesc(p));
end
else begin
addSon(result, nil);
if (p.tok.tokType <> tkEquals) and not (withBothOptional in flags) then
parMessage(p, errColonOrEqualsExpected, tokToStr(p.tok))
end;
if p.tok.tokType = tkEquals then begin
getTok(p); optInd(p, result);
addSon(result, parseExpr(p));
end
else
addSon(result, nil);
end;
function parseParamList(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkFormalParams, p);
addSon(result, nil); // return type
if p.tok.tokType = tkParLe then begin
getTok(p);
optInd(p, result);
while true do begin
case p.tok.tokType of
tkSymbol, tkAccent: a := parseIdentColonEquals(p, {@set}[]);
tkParRi: break;
else begin parMessage(p, errTokenExpected, ')'+''); break; end;
end;
//optInd(p, a);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, tkParRi);
end;
if p.tok.tokType = tkColon then begin
getTok(p);
optInd(p, result);
result.sons[0] := parseTypeDesc(p)
end
end;
function parseTypeDescKAux(var p: TParser; kind: TNodeKind): PNode;
begin
result := newNodeP(kind, p);
getTok(p);
optInd(p, result);
addSon(result, parseTypeDesc(p));
end;
function parseTypeDescK(var p: TParser): PNode;
var
a: PNode;
begin
case p.tok.tokType of
tkVar: result := parseTypeDescKAux(p, nkVarTy);
tkRef: result := parseTypeDescKAux(p, nkRefTy);
tkPtr: result := parseTypeDescKAux(p, nkPtrTy);
tkType: begin
result := newNodeP(nkTypeOfExpr, p);
getTok(p);
optInd(p, result);
addSon(result, parseExpr(p))
end;
tkProc: begin
result := newNodeP(nkProcTy, p);
getTok(p);
optInd(p, result);
addSon(result, parseParamList(p));
if p.tok.tokType = tkCurlyDotLe then
addSon(result, parsePragma(p))
else
addSon(result, nil);
end;
tkTuple: begin
result := newNodeP(nkTupleTy, p);
getTok(p);
eat(p, tkBracketLe);
optInd(p, result);
while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
a := parseIdentColonEquals(p, {@set}[]);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, tkBracketRi);
end;
else begin
InternalError(parLineInfo(p), 'pnimsyn.parseTypeDescK');
result := nil
end
end
end;
function parseTypeDesc(var p: TParser): PNode;
begin
case p.tok.tokType of
tkVar, tkRef, tkPtr, tkProc, tkType, tkTuple:
result := parseTypeDescK(p);
else result := primary(p)
end
end;
function parseGenericParamList(var p: TParser): PNode; function parseGenericParamList(var p: TParser): PNode;
var var
a: PNode; a: PNode;
@ -1408,26 +1420,6 @@ begin
indAndComment(p, result); // XXX: document this in the grammar! indAndComment(p, result); // XXX: document this in the grammar!
end; end;
function parseLambda(var p: TParser): PNode;
begin
result := newNodeP(nkLambda, p);
getTok(p);
optInd(p, result);
addSon(result, nil); // no name part
addSon(result, nil); // no generic parameters
addSon(result, parseParamList(p));
if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p))
else addSon(result, nil);
if p.tok.tokType = tkEquals then begin
getTok(p); skipComment(p, result);
addSon(result, parseStmt(p));
end
else begin
addSon(result, nil);
parMessage(p, errTokenExpected, tokTypeToStr[tkEquals]);
end
end;
function newCommentStmt(var p: TParser): PNode; function newCommentStmt(var p: TParser): PNode;
begin begin
result := newNodeP(nkCommentStmt, p); result := newNodeP(nkCommentStmt, p);
@ -1803,7 +1795,7 @@ begin
end end
end; end;
function parseModule(var p: TParser): PNode; function parseAll(var p: TParser): PNode;
var var
a: PNode; a: PNode;
begin begin

View file

@ -27,7 +27,7 @@ const
procPragmas = {@set}[FirstCallConv..LastCallConv, procPragmas = {@set}[FirstCallConv..LastCallConv,
wImportc, wExportc, wNodecl, wMagic, wNosideEffect, wSideEffect, wImportc, wExportc, wNodecl, wMagic, wNosideEffect, wSideEffect,
wNoreturn, wDynLib, wHeader, wCompilerProc, wPure, wNoreturn, wDynLib, wHeader, wCompilerProc, wPure,
wCppMethod, wDeprecated, wVarargs, wCompileTime, wMerge, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow]; wBorrow];
converterPragmas = procPragmas; converterPragmas = procPragmas;
methodPragmas = procPragmas; methodPragmas = procPragmas;
@ -258,11 +258,13 @@ begin
if (sym = nil) or (sym.kind = skModule) then if (sym = nil) or (sym.kind = skModule) then
POptionEntry(c.optionStack.tail).dynlib := getLib(c, libDynamic, POptionEntry(c.optionStack.tail).dynlib := getLib(c, libDynamic,
expectStrLit(c, n)) expectStrLit(c, n))
else begin else if n.kind = nkExprColonExpr then begin
lib := getLib(c, libDynamic, expectStrLit(c, n)); lib := getLib(c, libDynamic, expectStrLit(c, n));
addToLib(lib, sym); addToLib(lib, sym);
include(sym.loc.flags, lfDynamicLib) include(sym.loc.flags, lfDynamicLib)
end end
else
include(sym.loc.flags, lfExportLib)
end; end;
procedure processNote(c: PContext; n: PNode); procedure processNote(c: PContext; n: PNode);
@ -475,12 +477,7 @@ begin
if n = nil then exit; if n = nil then exit;
for i := 0 to sonsLen(n)-1 do begin for i := 0 to sonsLen(n)-1 do begin
it := n.sons[i]; it := n.sons[i];
if it.kind = nkExprColonExpr then begin if it.kind = nkExprColonExpr then key := it.sons[0] else key := it;
key := it.sons[0];
end
else begin
key := it;
end;
if key.kind = nkIdent then begin if key.kind = nkIdent then begin
k := whichKeyword(key.ident); k := whichKeyword(key.ident);
if k in validPragmas then begin if k in validPragmas then begin
@ -540,9 +537,9 @@ begin
include(sym.flags, sfUsed); // suppress all those stupid warnings include(sym.flags, sfUsed); // suppress all those stupid warnings
registerCompilerProc(sym); registerCompilerProc(sym);
end; end;
wCppMethod: begin wProcvar: begin
makeExternImport(sym, getOptionalStr(c, it, sym.name.s)); noVal(it);
include(sym.flags, sfCppMethod); include(sym.flags, sfProcVar);
end; end;
wDeprecated: begin wDeprecated: begin
noVal(it); noVal(it);
@ -615,6 +612,8 @@ begin
end; end;
end; end;
if (sym <> nil) and (sym.kind <> skModule) then begin if (sym <> nil) and (sym.kind <> skModule) then begin
if (lfExportLib in sym.loc.flags) and not (sfExportc in sym.flags) then
liMessage(n.info, errDynlibRequiresExportc);
lib := POptionEntry(c.optionstack.tail).dynlib; lib := POptionEntry(c.optionstack.tail).dynlib;
if ([lfDynamicLib, lfHeader] * sym.loc.flags = []) and if ([lfDynamicLib, lfHeader] * sym.loc.flags = []) and
(sfImportc in sym.flags) and (sfImportc in sym.flags) and

View file

@ -1,28 +1,31 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
// //
unit ptmplsyn; unit ptmplsyn;
// This module implements the parser of the Nimrod Template files. // This module implements Nimrod's standard template filter.
{$include config.inc} {$include config.inc}
interface interface
uses uses
nsystem, llstream, nos, charsets, wordrecg, strutils, nsystem, llstream, nos, charsets, wordrecg, idents, strutils,
ast, astalgo, msgs, options, pnimsyn; ast, astalgo, msgs, options, rnimsyn, filters;
function ParseTmplFile(const filename: string): PNode; function filterTmpl(input: PLLStream; const filename: string;
call: PNode): PLLStream;
// #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
implementation
type type
TParseState = (psDirective, psMultiDir, psTempl); TParseState = (psDirective, psTempl);
TTmplParser = record TTmplParser = record
inp: PLLStream; inp: PLLStream;
state: TParseState; state: TParseState;
@ -30,19 +33,11 @@ type
indent, par: int; indent, par: int;
x: string; // the current input line x: string; // the current input line
outp: PLLStream; // the ouput will be parsed by pnimsyn outp: PLLStream; // the ouput will be parsed by pnimsyn
subsChar: Char; subsChar, NimDirective: Char;
emit, conc, toStr: string;
end; end;
function ParseTmpl(var p: TTmplParser): PNode;
procedure openTmplParser(var p: TTmplParser; const filename: string;
inputStream: PLLStream);
procedure closeTmplParser(var p: TTmplParser);
implementation
const const
NimDirective = '#';
PatternChars = ['a'..'z', 'A'..'Z', '0'..'9', #128..#255, '.', '_']; PatternChars = ['a'..'z', 'A'..'Z', '0'..'9', #128..#255, '.', '_'];
procedure newLine(var p: TTmplParser); procedure newLine(var p: TTmplParser);
@ -59,29 +54,13 @@ var
begin begin
j := strStart; j := strStart;
while p.x[j] = ' ' do inc(j); while p.x[j] = ' ' do inc(j);
if p.state = psMultiDir then begin if (p.x[strStart] = p.NimDirective) and (p.x[strStart+1] = '!') then
newLine(p); newLine(p)
if p.x[j] = '*' then begin else if (p.x[j] = p.NimDirective) then begin
inc(j);
if p.x[j] = NimDirective then p.state := psTempl;
// ignore the rest of the line
end
else
LLStreamWrite(p.outp, p.x); // simply add the whole line
end
else if p.x[j] = NimDirective then begin
newLine(p); newLine(p);
inc(j); inc(j);
while p.x[j] = ' ' do inc(j); while p.x[j] = ' ' do inc(j);
d := j; d := j;
if p.x[j] = '*' then begin
inc(j);
p.state := psMultiDir;
LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, '#*');
LLStreamWrite(p.outp, ncopy(p.x, j)); // simply add the whole line
end
else begin
keyw := ''; keyw := '';
while p.x[j] in PatternChars do begin while p.x[j] in PatternChars do begin
addChar(keyw, p.x[j]); addChar(keyw, p.x[j]);
@ -98,19 +77,8 @@ begin
LLStreamWrite(p.outp, repeatChar(p.indent)); LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, '#end'); LLStreamWrite(p.outp, '#end');
end; end;
wSubsChar: begin
LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, '#subschar');
while p.x[j] = ' ' do inc(j);
if p.x[j] in ['+', '-', '*', '/', '<', '>', '!', '?', '^', '.',
'|', '=', '%', '&', '$', '@', '~'] then p.subsChar := p.x[j]
else begin
p.info.col := int16(j);
liMessage(p.info, errXNotAllowedHere, p.x[j]+'');
end
end;
wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator, wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
wConverter, wMacro, wTemplate: begin wConverter, wMacro, wTemplate, wMethod: begin
LLStreamWrite(p.outp, repeatChar(p.indent)); LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, ncopy(p.x, d)); LLStreamWrite(p.outp, ncopy(p.x, d));
inc(p.indent, 2); inc(p.indent, 2);
@ -124,8 +92,7 @@ begin
LLStreamWrite(p.outp, ncopy(p.x, d)); LLStreamWrite(p.outp, ncopy(p.x, d));
end end
end; end;
p.state := psDirective; p.state := psDirective
end
end end
else begin else begin
// data line // data line
@ -133,17 +100,18 @@ begin
case p.state of case p.state of
psTempl: begin psTempl: begin
// next line of string literal: // next line of string literal:
LLStreamWrite(p.outp, ' &'+nl); LLStreamWrite(p.outp, p.conc);
LLStreamWrite(p.outp, nl);
LLStreamWrite(p.outp, repeatChar(p.indent + 2)); LLStreamWrite(p.outp, repeatChar(p.indent + 2));
LLStreamWrite(p.outp, '"'+''); LLStreamWrite(p.outp, '"'+'');
end; end;
psDirective: begin psDirective: begin
newLine(p); newLine(p);
LLStreamWrite(p.outp, repeatChar(p.indent)); LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, 'add(result, "'); LLStreamWrite(p.outp, p.emit);
LLStreamWrite(p.outp, '("');
inc(p.par); inc(p.par);
end; end
else InternalError(p.info, 'parser in invalid state');
end; end;
p.state := psTempl; p.state := psTempl;
while true do begin while true do begin
@ -162,7 +130,10 @@ begin
case p.x[j] of case p.x[j] of
'{': begin '{': begin
p.info.col := int16(j); p.info.col := int16(j);
LLStreamWrite(p.outp, '" & $('); LLStreamWrite(p.outp, '"');
LLStreamWrite(p.outp, p.conc);
LLStreamWrite(p.outp, p.toStr);
LLStreamWrite(p.outp, '(');
inc(j); inc(j);
curly := 0; curly := 0;
while true do begin while true do begin
@ -171,13 +142,13 @@ begin
'{': begin '{': begin
inc(j); inc(j);
inc(curly); inc(curly);
LLStreamWrite(p.outp, '{'+''); LLStreamWrite(p.outp, '{');
end; end;
'}': begin '}': begin
inc(j); inc(j);
if curly = 0 then break; if curly = 0 then break;
if curly > 0 then dec(curly); if curly > 0 then dec(curly);
LLStreamWrite(p.outp, '}'+''); LLStreamWrite(p.outp, '}');
end; end;
else begin else begin
LLStreamWrite(p.outp, p.x[j]); LLStreamWrite(p.outp, p.x[j]);
@ -185,15 +156,22 @@ begin
end end
end end
end; end;
LLStreamWrite(p.outp, ') & "') LLStreamWrite(p.outp, ')');
LLStreamWrite(p.outp, p.conc);
LLStreamWrite(p.outp, '"');
end; end;
'A'..'Z', 'a'..'z', '_': begin 'a'..'z', 'A'..'Z', #128..#255: begin
LLStreamWrite(p.outp, '" & $'); LLStreamWrite(p.outp, '"');
LLStreamWrite(p.outp, p.conc);
LLStreamWrite(p.outp, p.toStr);
LLStreamWrite(p.outp, '(');
while p.x[j] in PatternChars do begin while p.x[j] in PatternChars do begin
LLStreamWrite(p.outp, p.x[j]); LLStreamWrite(p.outp, p.x[j]);
inc(j) inc(j)
end; end;
LLStreamWrite(p.outp, ' & "') LLStreamWrite(p.outp, ')');
LLStreamWrite(p.outp, p.conc);
LLStreamWrite(p.outp, '"')
end; end;
else if p.x[j] = p.subsChar then begin else if p.x[j] = p.subsChar then begin
LLStreamWrite(p.outp, p.subsChar); LLStreamWrite(p.outp, p.subsChar);
@ -203,7 +181,7 @@ begin
p.info.col := int16(j); p.info.col := int16(j);
liMessage(p.info, errInvalidExpression, '$'+''); liMessage(p.info, errInvalidExpression, '$'+'');
end end
end; end
end end
else begin else begin
LLStreamWrite(p.outp, p.x[j]); LLStreamWrite(p.outp, p.x[j]);
@ -215,56 +193,30 @@ begin
end end
end; end;
function ParseTmpl(var p: TTmplParser): PNode; function filterTmpl(input: PLLStream; const filename: string;
call: PNode): PLLStream;
var var
q: TParser; p: TTmplParser;
begin
while not LLStreamAtEnd(p.inp) do begin
p.x := LLStreamReadLine(p.inp) {@ignore} + #0 {@emit};
p.info.line := p.info.line + int16(1);
parseLine(p);
end;
newLine(p);
if gVerbosity >= 2 then begin
rawMessage(hintCodeBegin);
messageOut(p.outp.s);
rawMessage(hintCodeEnd);
end;
openParser(q, toFilename(p.info), p.outp);
result := ParseModule(q);
closeParser(q);
end;
procedure openTmplParser(var p: TTmplParser; const filename: string;
inputStream: PLLStream);
begin begin
{@ignore} {@ignore}
FillChar(p, sizeof(p), 0); FillChar(p, sizeof(p), 0);
{@emit} {@emit}
p.info := newLineInfo(filename, 0, 0); p.info := newLineInfo(filename, 0, 0);
p.outp := LLStreamOpen(''); p.outp := LLStreamOpen('');
p.inp := inputStream; p.inp := input;
p.subsChar := '$'; p.subsChar := charArg(call, 'subschar', 1, '$');
end; p.nimDirective := charArg(call, 'metachar', 2, '#');
p.emit := strArg(call, 'emit', 3, 'result.add');
procedure CloseTmplParser(var p: TTmplParser); p.conc := strArg(call, 'conc', 4, ' & ');
begin p.toStr := strArg(call, 'tostring', 5, '$'+'');
while not LLStreamAtEnd(p.inp) do begin
p.x := LLStreamReadLine(p.inp) {@ignore} + #0 {@emit};
p.info.line := p.info.line + int16(1);
parseLine(p);
end;
newLine(p);
result := p.outp;
LLStreamClose(p.inp); LLStreamClose(p.inp);
end; end;
function ParseTmplFile(const filename: string): PNode;
var
p: TTmplParser;
f: TBinaryFile;
begin
if not OpenFile(f, filename) then begin
rawMessage(errCannotOpenFile, filename);
result := nil;
exit
end;
OpenTmplParser(p, filename, LLStreamOpen(f));
result := ParseTmpl(p);
CloseTmplParser(p);
end;
end. end.

View file

@ -498,7 +498,7 @@ begin
nkPar, nkCurly, nkBracket: result := lcomma(n)+2; nkPar, nkCurly, nkBracket: result := lcomma(n)+2;
nkSymChoice: result := lsons(n) + length('()') + sonsLen(n)-1; nkSymChoice: result := lsons(n) + length('()') + sonsLen(n)-1;
nkTupleTy: result := lcomma(n)+length('tuple[]'); nkTupleTy: result := lcomma(n)+length('tuple[]');
nkQualified, nkDotExpr: result := lsons(n)+1; nkDotExpr: result := lsons(n)+1;
nkBind: result := lsons(n)+length('bind_'); nkBind: result := lsons(n)+length('bind_');
nkCheckedFieldExpr: result := lsub(n.sons[0]); nkCheckedFieldExpr: result := lsub(n.sons[0]);
nkLambda: result := lsons(n)+length('lambda__=_'); nkLambda: result := lsons(n)+length('lambda__=_');
@ -1047,7 +1047,7 @@ begin
gcomma(g, n, c); gcomma(g, n, c);
put(g, tkBracketRi, ']'+''); put(g, tkBracketRi, ']'+'');
end; end;
nkQualified, nkDotExpr: begin nkDotExpr: begin
gsub(g, n.sons[0]); gsub(g, n.sons[0]);
put(g, tkDot, '.'+''); put(g, tkDot, '.'+'');
gsub(g, n.sons[1]); gsub(g, n.sons[1]);

View file

@ -1057,8 +1057,7 @@ begin
// NOTE: we need to process the entire module graph so that no ID will // NOTE: we need to process the entire module graph so that no ID will
// be used twice! However, compilation speed does not suffer much from // be used twice! However, compilation speed does not suffer much from
// this, since results are cached. // this, since results are cached.
res := checkDep(JoinPath(options.libpath, res := checkDep(JoinPath(options.libpath, addFileExt('system', nimExt)));
appendFileExt('system', nimExt)));
if res <> rrNone then result := rrModDeps; if res <> rrNone then result := rrModDeps;
for i := 0 to high(r.modDeps) do begin for i := 0 to high(r.modDeps) do begin
res := checkDep(r.modDeps[i]); res := checkDep(r.modDeps[i]);

View file

@ -314,6 +314,9 @@ begin
len := length(tokens); len := length(tokens);
L.buf := PChar(buffer); L.buf := PChar(buffer);
L.line := 1; L.line := 1;
// skip UTF-8 BOM
if (L.buf[0] = #239) and (L.buf[1] = #187) and (L.buf[2] = #191) then
inc(L.bufpos, 3);
L.skipPounds := skipPounds; L.skipPounds := skipPounds;
if skipPounds then begin if skipPounds then begin
if L.buf[L.bufpos] = '#' then inc(L.bufpos); if L.buf[L.bufpos] = '#' then inc(L.bufpos);

View file

@ -143,19 +143,16 @@ type
// or float literals // or float literals
literal: string; // the parsed (string) literal; and literal: string; // the parsed (string) literal; and
// documentation comments are here too // documentation comments are here too
next: PToken; // next token; used for arbitrary look-ahead next: PToken; // next token; can be used for arbitrary look-ahead
end; end;
PLexer = ^TLexer; PLexer = ^TLexer;
TLexer = object(TBaseLexer) TLexer = object(TBaseLexer)
// lexers can be put into a stack through the next pointer;
// this feature is currently unused, however
filename: string; filename: string;
next: PLexer;
indentStack: array of int; // the indentation stack indentStack: array of int; // the indentation stack
dedent: int; // counter for DED token generation dedent: int; // counter for DED token generation
indentAhead: int; // if > 0 an indendation has already been read indentAhead: int; // if > 0 an indendation has already been read
// this is needed because scanning # comments // this is needed because scanning comments
// needs so much look-ahead // needs so much look-ahead
end; end;
@ -920,7 +917,6 @@ begin
// got an documentation comment or tkIndent, return that: // got an documentation comment or tkIndent, return that:
if tok.toktype <> tkInvalid then exit; if tok.toktype <> tkInvalid then exit;
// to the parser
c := L.buf[L.bufpos]; c := L.buf[L.bufpos];
if c in SymStartChars - ['r', 'R', 'l'] then // common case first if c in SymStartChars - ['r', 'R', 'l'] then // common case first
getSymbol(L, tok) getSymbol(L, tok)

View file

@ -374,15 +374,34 @@ begin
end end
end; end;
function isAssignable(n: PNode): bool; type
TAssignableResult = (
arNone, // no l-value and no discriminant
arLValue, // is an l-value
arDiscriminant // is a discriminant
);
function isAssignable(n: PNode): TAssignableResult;
begin begin
result := false; result := arNone;
case n.kind of case n.kind of
nkSym: result := (n.sym.kind in [skVar, skTemp]); nkSym: begin
nkDotExpr, nkQualified, nkBracketExpr: begin if (n.sym.kind in [skVar, skTemp]) then
result := arLValue
end;
nkDotExpr: begin
checkMinSonsLen(n, 1); checkMinSonsLen(n, 1);
if skipTypes(n.sons[0].typ, abstractInst).kind in [tyVar, tyPtr, tyRef] then if skipTypes(n.sons[0].typ, abstractInst).kind in [tyVar, tyPtr, tyRef] then
result := true result := arLValue
else
result := isAssignable(n.sons[0]);
if (result = arLValue) and (sfDiscriminant in n.sons[1].sym.flags) then
result := arDiscriminant
end;
nkBracketExpr: begin
checkMinSonsLen(n, 1);
if skipTypes(n.sons[0].typ, abstractInst).kind in [tyVar, tyPtr, tyRef] then
result := arLValue
else else
result := isAssignable(n.sons[0]); result := isAssignable(n.sons[0]);
end; end;
@ -393,7 +412,7 @@ begin
if skipTypes(n.typ, abstractPtrs).kind in [tyOpenArray, tyTuple, tyObject] then if skipTypes(n.typ, abstractPtrs).kind in [tyOpenArray, tyTuple, tyObject] then
result := isAssignable(n.sons[1]) result := isAssignable(n.sons[1])
end; end;
nkHiddenDeref, nkDerefExpr: result := true; nkHiddenDeref, nkDerefExpr: result := arLValue;
nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
result := isAssignable(n.sons[0]); result := isAssignable(n.sons[0]);
else begin end else begin end
@ -409,7 +428,7 @@ begin
else begin else begin
result := newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ)); result := newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ));
addSon(result, n); addSon(result, n);
if not isAssignable(n) then begin if isAssignable(n) <> arLValue then begin
liMessage(n.info, errVarForOutParamNeeded); liMessage(n.info, errVarForOutParamNeeded);
end end
end end
@ -425,7 +444,7 @@ begin
result := newHiddenAddrTaken(c, n); result := newHiddenAddrTaken(c, n);
end end
end; end;
nkDotExpr, nkQualified: begin nkDotExpr: begin
checkSonsLen(n, 2); checkSonsLen(n, 2);
if n.sons[1].kind <> nkSym then if n.sons[1].kind <> nkSym then
internalError(n.info, 'analyseIfAddressTaken'); internalError(n.info, 'analyseIfAddressTaken');
@ -499,7 +518,7 @@ begin
prc := n.sons[0]; prc := n.sons[0];
checkMinSonsLen(n, 1); checkMinSonsLen(n, 1);
case n.sons[0].kind of case n.sons[0].kind of
nkDotExpr, nkQualified: begin nkDotExpr: begin
checkSonsLen(n.sons[0], 2); checkSonsLen(n.sons[0], 2);
n.sons[0] := semDotExpr(c, n.sons[0]); n.sons[0] := semDotExpr(c, n.sons[0]);
if n.sons[0].kind = nkDotCall then begin // it is a static call! if n.sons[0].kind = nkDotCall then begin // it is a static call!
@ -595,7 +614,7 @@ begin
else else
result := SymtabGet(c.Tab, n.ident); // no need for stub loading result := SymtabGet(c.Tab, n.ident); // no need for stub loading
end; end;
nkDotExpr, nkQualified: begin nkDotExpr: begin
result := nil; result := nil;
if onlyCurrentScope then exit; if onlyCurrentScope then exit;
checkSonsLen(n, 2); checkSonsLen(n, 2);
@ -1174,8 +1193,14 @@ begin
liMessage(n.info, errATypeHasNoValue); liMessage(n.info, errATypeHasNoValue);
case s.kind of case s.kind of
skProc, skMethod, skIterator, skConverter: begin skProc, skMethod, skIterator, skConverter: begin
if (s.magic <> mNone) then if not (sfProcVar in s.flags)
liMessage(n.info, errInvalidContextForBuiltinX, s.name.s); and (s.typ.callConv = ccDefault)
and (getModule(s).id <> c.module.id) then
liMessage(n.info, warnXisPassedToProcVar, s.name.s);
// XXX change this to errXCannotBePassedToProcVar after version 0.8.2
// TODO VERSION 0.8.4
//if (s.magic <> mNone) then
// liMessage(n.info, errInvalidContextForBuiltinX, s.name.s);
result := symChoice(c, n, s); result := symChoice(c, n, s);
end; end;
skConst: begin skConst: begin
@ -1253,10 +1278,7 @@ begin
(*s := n.sym; (*s := n.sym;
include(s.flags, sfUsed); include(s.flags, sfUsed);
if (s.kind = skType) and not (efAllowType in flags) then if (s.kind = skType) and not (efAllowType in flags) then
liMessage(n.info, errATypeHasNoValue); liMessage(n.info, errATypeHasNoValue);*)
if (s.magic <> mNone) and
(s.kind in [skProc, skMethod, skIterator, skConverter]) then
liMessage(n.info, errInvalidContextForBuiltinX, s.name.s); *)
// because of the changed symbol binding, this does not mean that we // because of the changed symbol binding, this does not mean that we
// don't have to check the symbol for semantics here again! // don't have to check the symbol for semantics here again!
result := semSym(c, n, n.sym, flags); result := semSym(c, n, n.sym, flags);
@ -1279,7 +1301,7 @@ begin
if result.typ = nil then result.typ := getSysType(tyString); if result.typ = nil then result.typ := getSysType(tyString);
nkCharLit: nkCharLit:
if result.typ = nil then result.typ := getSysType(tyChar); if result.typ = nil then result.typ := getSysType(tyChar);
nkQualified, nkDotExpr: begin nkDotExpr: begin
result := semDotExpr(c, n, flags); result := semDotExpr(c, n, flags);
if result.kind = nkDotCall then begin if result.kind = nkDotCall then begin
result.kind := nkCall; result.kind := nkCall;
@ -1364,7 +1386,8 @@ begin
result := n; result := n;
checkSonsLen(n, 1); checkSonsLen(n, 1);
n.sons[0] := semExprWithType(c, n.sons[0]); n.sons[0] := semExprWithType(c, n.sons[0]);
if not isAssignable(n.sons[0]) then liMessage(n.info, errExprHasNoAddress); if isAssignable(n.sons[0]) <> arLValue then
liMessage(n.info, errExprHasNoAddress);
n.typ := makePtrType(c, n.sons[0].typ); n.typ := makePtrType(c, n.sons[0].typ);
end; end;
nkHiddenAddr, nkHiddenDeref: begin nkHiddenAddr, nkHiddenDeref: begin

View file

@ -127,7 +127,7 @@ var
str, sub: string; str, sub: string;
a, b, c: int; a, b, c: int;
e: PSym; e: PSym;
marker: Char; marker: char;
begin begin
result := n; result := n;
checkSonsLen(n, 2); checkSonsLen(n, 2);
@ -243,7 +243,7 @@ begin
checkSonsLen(n, 2); checkSonsLen(n, 2);
a := n.sons[0]; a := n.sons[0];
case a.kind of case a.kind of
nkDotExpr, nkQualified: begin nkDotExpr: begin
// r.f = x // r.f = x
// --> `f=` (r, x) // --> `f=` (r, x)
checkSonsLen(a, 2); checkSonsLen(a, 2);
@ -298,7 +298,8 @@ begin
n.sons[1] := semExprWithType(c, n.sons[1]); n.sons[1] := semExprWithType(c, n.sons[1]);
le := n.sons[0].typ; le := n.sons[0].typ;
if (skipTypes(le, {@set}[tyGenericInst]).kind <> tyVar) if (skipTypes(le, {@set}[tyGenericInst]).kind <> tyVar)
and not IsAssignable(n.sons[0]) then begin and (IsAssignable(n.sons[0]) = arNone) then begin
// Direct assignment to a discriminant is allowed!
liMessage(n.sons[0].info, errXCannotBeAssignedTo, liMessage(n.sons[0].info, errXCannotBeAssignedTo,
renderTree(n.sons[0], {@set}[renderNoComments])); renderTree(n.sons[0], {@set}[renderNoComments]));
end end
@ -1014,8 +1015,7 @@ end;
function evalInclude(c: PContext; n: PNode): PNode; function evalInclude(c: PContext; n: PNode): PNode;
var var
i, fileIndex: int; i, fileIndex: int;
x: PNode; f: string;
f, name, ext: string;
begin begin
result := newNodeI(nkStmtList, n.info); result := newNodeI(nkStmtList, n.info);
addSon(result, n); // the rodwriter needs include information! addSon(result, n); // the rodwriter needs include information!
@ -1024,13 +1024,7 @@ begin
fileIndex := includeFilename(f); fileIndex := includeFilename(f);
if IntSetContainsOrIncl(c.includedFiles, fileIndex) then if IntSetContainsOrIncl(c.includedFiles, fileIndex) then
liMessage(n.info, errRecursiveDependencyX, f); liMessage(n.info, errRecursiveDependencyX, f);
SplitFilename(f, name, ext); addSon(result, semStmt(c, gIncludeFile(f)));
if cmpIgnoreCase(ext, '.'+TmplExt) = 0 then
x := gIncludeTmplFile(f)
else
x := gIncludeFile(f);
x := semStmt(c, x);
addSon(result, x);
IntSetExcl(c.includedFiles, fileIndex); IntSetExcl(c.includedFiles, fileIndex);
end; end;
end; end;

View file

@ -34,7 +34,7 @@ begin
if n = nil then begin result := false; exit end; if n = nil then begin result := false; exit end;
case n.kind of case n.kind of
nkIdent, nkSym, nkType: result := true; nkIdent, nkSym, nkType: result := true;
nkDotExpr, nkQualified, nkBracketExpr: begin nkDotExpr, nkBracketExpr: begin
for i := 0 to sonsLen(n)-1 do for i := 0 to sonsLen(n)-1 do
if not isTypeDesc(n.sons[i]) then begin if not isTypeDesc(n.sons[i]) then begin
result := false; exit result := false; exit

View file

@ -758,6 +758,10 @@ begin
nkTypeOfExpr: begin nkTypeOfExpr: begin
result := semExprWithType(c, n, {@set}[efAllowType]).typ; result := semExprWithType(c, n, {@set}[efAllowType]).typ;
end; end;
nkPar: begin
if sonsLen(n) = 1 then result := semTypeNode(c, n.sons[0], prev)
else liMessage(n.info, errTypeExpected);
end;
nkBracketExpr: begin nkBracketExpr: begin
checkMinSonsLen(n, 2); checkMinSonsLen(n, 2);
s := semTypeIdent(c, n.sons[0]); s := semTypeIdent(c, n.sons[0]);
@ -771,7 +775,7 @@ begin
else result := semGeneric(c, n, s, prev); else result := semGeneric(c, n, s, prev);
end end
end; end;
nkIdent, nkDotExpr, nkQualified, nkAccQuoted: begin nkIdent, nkDotExpr, nkAccQuoted: begin
s := semTypeIdent(c, n); s := semTypeIdent(c, n);
if s.typ = nil then if s.typ = nil then
liMessage(n.info, errTypeExpected); liMessage(n.info, errTypeExpected);

View file

@ -50,7 +50,7 @@ function ToString(b: PChar): string; overload;
function IntToStr(i: BiggestInt; minChars: int): string; function IntToStr(i: BiggestInt; minChars: int): string;
function find(const s, sub: string; start: int = 1): int; overload; function find(const s, sub: string; start: int = 1): int; overload;
function replaceStr(const s, search, by: string): string; function replace(const s, search, by: string): string;
procedure deleteStr(var s: string; first, last: int); procedure deleteStr(var s: string; first, last: int);
function ToLower(const s: string): string; function ToLower(const s: string): string;
@ -63,9 +63,6 @@ function ParseFloat(const s: string; checkEnd: Boolean = True): Real;
function repeatChar(count: int; c: Char = ' '): string; function repeatChar(count: int; c: Char = ' '): string;
type
TStringSeq = array of string;
TCharSet = set of Char;
function split(const s: string; const seps: TCharSet): TStringSeq; function split(const s: string; const seps: TCharSet): TStringSeq;
function startsWith(const s, prefix: string): bool; function startsWith(const s, prefix: string): bool;
@ -278,7 +275,7 @@ begin
end end
end; end;
function replaceStr(const s, search, by: string): string; function replace(const s, search, by: string): string;
var var
i, j: int; i, j: int;
begin begin

View file

@ -1,64 +1,234 @@
// //
// //
// The Nimrod Compiler // The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf // (c) Copyright 2009 Andreas Rumpf
// //
// See the file "copying.txt", included in this // See the file "copying.txt", included in this
// distribution, for details about the copyright. // distribution, for details about the copyright.
// //
unit syntaxes; unit syntaxes;
// Defines the common interface of all parsers & renderers. // Implements the dispatcher for the different parsers.
// All parsers and renderers need to register here!
// This file is currently unused.
{$include 'config.inc'} {$include 'config.inc'}
interface interface
uses uses
nsystem, strutils, ast, scanner, pnimsyn, rnimsyn, options, msgs, nsystem, strutils, llstream, ast, astalgo, idents, scanner, options, msgs,
nos, lists, condsyms, paslex, pasparse, rodgen, ropes, trees; pnimsyn, ptmplsyn, filters, rnimsyn;
// how to handle the different keyword sets?
// PIdent does not support multiple ids! But I want to allow
// constant expressions, else the case-statement wouldn't work
// resulting in ugly code -> let the build system deal with it!
// IDEA: the scanner changes the IDs for its keywords: Won't work!
// How to deal with the `` operator?
type type
TSyntaxes = (synStandard, synCurly, synLisp); TFilterKind = (filtNone, filtTemplate, filtReplace, filtStrip);
TParserKind = (skinStandard, skinBraces, skinEndX);
const
parserNames: array [TParserKind] of string = ('standard', 'braces', 'endx');
filterNames: array [TFilterKind] of string = ('none', 'stdtmpl', 'replace',
'strip');
type
TParsers = record
skin: TParserKind;
parser: TParser;
end;
{@ignore}
function ParseFile(const filename: string): PNode;
{@emit
function ParseFile(const filename: string): PNode; procvar;
}
procedure openParsers(var p: TParsers; const filename: string;
inputstream: PLLStream);
procedure closeParsers(var p: TParsers);
function parseAll(var p: TParsers): PNode;
function parseTopLevelStmt(var p: TParsers): PNode;
// implements an iterator. Returns the next top-level statement or nil if end
// of stream.
function parseFile(const filename: string): PNode;
implementation implementation
type function ParseFile(const filename: string): PNode;
TFileParser = function (const filename: string): PNode;
TBufferParser = function (const buf, filename: string;
line, column: int): PNode;
TRenderer = function (n: PNode): string;
THeadParser = function (const line: string): bool;
TSyntax = record
name: string; // name of the syntax
headParser: THeadParser; // the head parser
parser: TParser; // the parser for the syntax
renderer: TRenderer; // renderer of the syntax; may be nil
end;
var var
syntaxes: array [TSyntaxes] of TSyntax; p: TParsers;
f: TBinaryFile;
procedure addSyntax(const s: TSyntax);
var
len: int;
begin begin
len := length(syntaxes); if not OpenFile(f, filename) then begin
setLength(syntaxes, len+1); rawMessage(errCannotOpenFile, filename);
syntaxes[len] := s; exit
end;
OpenParsers(p, filename, LLStreamOpen(f));
result := ParseAll(p);
CloseParsers(p);
end; end;
initialization function parseAll(var p: TParsers): PNode;
syntaxes[synStandard].name = 'Standard'; begin
case p.skin of
skinStandard: result := pnimsyn.parseAll(p.parser);
skinBraces, skinEndX: InternalError('parser to implement');
// skinBraces: result := pbraces.parseAll(p.parser);
// skinEndX: result := pendx.parseAll(p.parser);
end
end;
function parseTopLevelStmt(var p: TParsers): PNode;
begin
case p.skin of
skinStandard: result := pnimsyn.parseTopLevelStmt(p.parser);
skinBraces, skinEndX: InternalError('parser to implement');
//skinBraces: result := pbraces.parseTopLevelStmt(p.parser);
//skinEndX: result := pendx.parseTopLevelStmt(p.parser);
end
end;
function UTF8_BOM(const s: string): int;
begin
if (s[strStart] = #239) and (s[strStart+1] = #187)
and (s[strStart+2] = #191) then result := 3
else result := 0
end;
function containsShebang(const s: string; i: int): bool;
var
j: int;
begin
result := false;
if (s[i] = '#') and (s[i+1] = '!') then begin
j := i+2;
while s[j] in WhiteSpace do inc(j);
result := s[j] = '/'
end
end;
function parsePipe(const filename: string; inputStream: PLLStream): PNode;
var
line: string;
s: PLLStream;
i: int;
q: TParser;
begin
result := nil;
s := LLStreamOpen(filename, fmRead);
if s <> nil then begin
line := LLStreamReadLine(s) {@ignore} + #0 {@emit};
i := UTF8_Bom(line) + strStart;
if containsShebang(line, i) then begin
line := LLStreamReadLine(s) {@ignore} + #0 {@emit};
i := strStart;
end;
if (line[i] = '#') and (line[i+1] = '!') then begin
inc(i, 2);
while line[i] in WhiteSpace do inc(i);
OpenParser(q, filename, LLStreamOpen(ncopy(line, i)));
result := pnimsyn.parseAll(q);
CloseParser(q);
end;
LLStreamClose(s);
end
end;
function getFilter(ident: PIdent): TFilterKind;
var
i: TFilterKind;
begin
for i := low(TFilterKind) to high(TFilterKind) do
if IdentEq(ident, filterNames[i]) then begin
result := i; exit
end;
result := filtNone
end;
function getParser(ident: PIdent): TParserKind;
var
i: TParserKind;
begin
for i := low(TParserKind) to high(TParserKind) do
if IdentEq(ident, parserNames[i]) then begin
result := i; exit
end;
rawMessage(errInvalidDirectiveX, ident.s);
end;
function getCallee(n: PNode): PIdent;
begin
if (n.kind = nkCall) and (n.sons[0].kind = nkIdent) then
result := n.sons[0].ident
else if n.kind = nkIdent then result := n.ident
else rawMessage(errXNotAllowedHere, renderTree(n));
end;
function applyFilter(var p: TParsers; n: PNode; const filename: string;
input: PLLStream): PLLStream;
var
ident: PIdent;
f: TFilterKind;
begin
ident := getCallee(n);
f := getFilter(ident);
case f of
filtNone: begin
p.skin := getParser(ident);
result := input
end;
filtTemplate: result := filterTmpl(input, filename, n);
filtStrip: result := filterStrip(input, filename, n);
filtReplace: result := filterReplace(input, filename, n);
end;
if f <> filtNone then begin
if gVerbosity >= 2 then begin
rawMessage(hintCodeBegin);
messageOut(result.s);
rawMessage(hintCodeEnd);
end
end
end;
function evalPipe(var p: TParsers; n: PNode; const filename: string;
start: PLLStream): PLLStream;
var
i: int;
begin
result := start;
if n = nil then exit;
if (n.kind = nkInfix) and (n.sons[0].kind = nkIdent)
and IdentEq(n.sons[0].ident, '|'+'') then begin
for i := 1 to 2 do begin
if n.sons[i].kind = nkInfix then
result := evalPipe(p, n.sons[i], filename, result)
else
result := applyFilter(p, n.sons[i], filename, result)
end
end
else if n.kind = nkStmtList then
result := evalPipe(p, n.sons[0], filename, result)
else
result := applyFilter(p, n, filename, result)
end;
procedure openParsers(var p: TParsers; const filename: string;
inputstream: PLLStream);
var
pipe: PNode;
s: PLLStream;
begin
p.skin := skinStandard;
pipe := parsePipe(filename, inputStream);
if pipe <> nil then
s := evalPipe(p, pipe, filename, inputStream)
else
s := inputStream;
case p.skin of
skinStandard, skinBraces, skinEndX:
pnimsyn.openParser(p.parser, filename, s);
end
end;
procedure closeParsers(var p: TParsers);
begin
pnimsyn.closeParser(p.parser);
end;
end. end.

View file

@ -652,7 +652,7 @@ end;
f: proc (x: int, c: PClosure): int f: proc (x: int, c: PClosure): int
proc map(f: PClosure, a: seq[int]): seq[int] = proc map(f: PClosure, a: seq[int]): seq[int] =
result = [] result = @[]
for elem in a: for elem in a:
add result, f.f(a, f) add result, f.f(a, f)
@ -737,6 +737,7 @@ end;
function transformCase(c: PTransf; n: PNode): PNode; function transformCase(c: PTransf; n: PNode): PNode;
// removes `elif` branches of a case stmt // removes `elif` branches of a case stmt
// adds ``else: nil`` if needed for the code generator
var var
len, i, j: int; len, i, j: int;
ifs, elsen: PNode; ifs, elsen: PNode;
@ -754,6 +755,14 @@ begin
setLength(n.sons, i+2); setLength(n.sons, i+2);
addSon(elsen, ifs); addSon(elsen, ifs);
n.sons[i+1] := elsen; n.sons[i+1] := elsen;
end
else if (n.sons[len-1].kind <> nkElse) and
not (skipTypes(n.sons[0].Typ, abstractVarRange).Kind in
[tyInt..tyInt64, tyChar, tyEnum]) then begin
//MessageOut(renderTree(n));
elsen := newNodeI(nkElse, n.info);
addSon(elsen, newNodeI(nkNilLit, n.info));
addSon(n, elsen)
end; end;
result := n; result := n;
for j := 0 to sonsLen(n)-1 do result.sons[j] := transform(c, n.sons[j]); for j := 0 to sonsLen(n)-1 do result.sons[j] := transform(c, n.sons[j]);

View file

@ -63,9 +63,9 @@ type
wMagic, wTypeCheck, wFinal, wProfiler, wMagic, wTypeCheck, wFinal, wProfiler,
wObjChecks, wImportc, wExportc, wAlign, wNodecl, wPure, wObjChecks, wImportc, wExportc, wAlign, wNodecl, wPure,
wVolatile, wRegister, wSideeffect, wHeader, wNosideeffect, wNoreturn, wVolatile, wRegister, wSideeffect, wHeader, wNosideeffect, wNoreturn,
wMerge, wLib, wDynlib, wCompilerproc, wCppmethod, wFatal, wMerge, wLib, wDynlib, wCompilerproc, wProcVar, wFatal,
wError, wWarning, wHint, wLine, wPush, wPop, wError, wWarning, wHint, wLine, wPush, wPop,
wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wPragma, wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wParallelBuild,
wLink, wCompile, wLinksys, wDeprecated, wVarargs, wLink, wCompile, wLinksys, wDeprecated, wVarargs,
wByref, wCallconv, wBreakpoint, wDebugger, wNimcall, wStdcall, wByref, wCallconv, wBreakpoint, wDebugger, wNimcall, wStdcall,
wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure, wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure,
@ -136,9 +136,9 @@ const
'magic', 'typecheck', 'final', 'profiler', 'magic', 'typecheck', 'final', 'profiler',
'objchecks', 'importc', 'exportc', 'align', 'nodecl', 'pure', 'objchecks', 'importc', 'exportc', 'align', 'nodecl', 'pure',
'volatile', 'register', 'sideeffect', 'header', 'nosideeffect', 'noreturn', 'volatile', 'register', 'sideeffect', 'header', 'nosideeffect', 'noreturn',
'merge', 'lib', 'dynlib', 'compilerproc', 'cppmethod', 'fatal', 'merge', 'lib', 'dynlib', 'compilerproc', 'procvar', 'fatal',
'error', 'warning', 'hint', 'line', 'push', 'pop', 'error', 'warning', 'hint', 'line', 'push', 'pop',
'define', 'undef', 'linedir', 'stacktrace', 'linetrace', 'pragma', 'define', 'undef', 'linedir', 'stacktrace', 'linetrace', 'parallelbuild',
'link', 'compile', 'linksys', 'deprecated', 'varargs', 'link', 'compile', 'linksys', 'deprecated', 'varargs',
'byref', 'callconv', 'breakpoint', 'debugger', 'nimcall', 'stdcall', 'byref', 'callconv', 'breakpoint', 'debugger', 'nimcall', 'stdcall',
'cdecl', 'safecall', 'syscall', 'inline', 'noinline', 'fastcall', 'closure', 'cdecl', 'safecall', 'syscall', 'inline', 'noinline', 'fastcall', 'closure',

View file

@ -100,7 +100,7 @@ InstallScript: "yes"
UninstallScript: "yes" UninstallScript: "yes"
[InnoSetup] [InnoSetup]
path = r"c:\programme\innosetup5\iscc.exe" path = r"c:\program files\inno setup 5\iscc.exe"
flags = "/Q" flags = "/Q"
[C_Compiler] [C_Compiler]

View file

@ -1,3 +1,4 @@
#! stdtmpl
#proc sunsetTemplate*(current, ticker, content: string, #proc sunsetTemplate*(current, ticker, content: string,
# tabs: openarray[array[0..1, string]]): string = # tabs: openarray[array[0..1, string]]): string =
# result = "" # result = ""

16
tests/tadrdisc.nim Executable file
View file

@ -0,0 +1,16 @@
# Test that the address of a dicriminants cannot be taken
type
TKind = enum ka, kb, kc
TA = object
case k: TKind
of ka: x, y: int
of kb: a, b: string
of kc: c, d: float
proc setKind(k: var TKind) =
k = kc
var a: TA
setKind(a.k) #ERROR_MSG for a 'var' type a variable needs to be passed

14
tests/tbind1.nim Executable file
View file

@ -0,0 +1,14 @@
# Test the new ``bind`` keyword for templates
proc p1(x: int8, y: int): int = return x + y
template tempBind(x, y: expr): expr =
bind p1(x, y)
proc p1(x: int, y: int8): int = return x - y
# This is tricky: the call to ``p1(1'i8, 2'i8)`` should not fail in line 6,
# because it is not ambiguous there. But it is ambiguous after line 8.
echo tempBind(1'i8, 2'i8) #OUT 3

10
tests/tbind2.nim Executable file
View file

@ -0,0 +1,10 @@
# Test the new ``bind`` keyword for templates
proc p1(x: int8, y: int): int = return x + y
proc p1(x: int, y: int8): int = return x - y
template tempBind(x, y: expr): expr =
bind p1(x, y) #ERROR_MSG ambiguous call
echo tempBind(1'i8, 2'i8)

25
tests/tbintre2.nim Executable file
View file

@ -0,0 +1,25 @@
# Same test, but check module boundaries
import tbintree
var
root: PBinaryTree[string]
x = newNode("hallo")
add(root, x)
add(root, "world")
if find(root, "world"):
for str in items(root):
stdout.write(str)
else:
stdout.writeln("BUG")
var
r2: PBinaryTree[int]
add(r2, newNode(110))
add(r2, 223)
add(r2, 99)
for y in items(r2):
stdout.write(y)
#OUT halloworld99110223

View file

@ -4,29 +4,29 @@ type
tenum = enum eA, eB, eC tenum = enum eA, eB, eC
var var
x: string x: string = "yyy"
y: Tenum = eA y: Tenum = eA
i: int i: int
case y case y
of eA: write(stdout, "a\n") of eA: write(stdout, "a")
of eB, eC: write(stdout, "b oder c\n") of eB, eC: write(stdout, "b or c")
x = readLine(stdin)
case x case x
of "Andreas", "Rumpf": write(stdout, "Hallo Meister!\n") of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
of "aa", "bb": write(stdout, "Du bist nicht mein Meister\n") of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
of "cc", "hash", "when": nil of "cc", "hash", "when": nil
of "will", "it", "finally", "be", "generated": nil of "will", "it", "finally", "be", "generated": nil
else: write(stdout, "das sollte nicht passieren!\N")
case i case i
of 0..5, 8, 9: nil of 1..5, 8, 9: nil
of 6, 7: nil of 6, 7: nil
elif x == "Ha": elif x == "Ha":
nil nil
elif x == "Ho": elif x == "yyy":
nil write(stdout, x)
else: else:
nil nil
#OUT ayyy

11
tests/tcnstseq.nim Executable file
View file

@ -0,0 +1,11 @@
# Test the new implicit conversion from sequences to arrays in a constant
# context.
import strutils
const
myWords = "Angelika Anne Anna Anka Anja".split()
for x in items(myWords):
write(stdout, x) #OUT AngelikaAnneAnnaAnkaAnja

View file

@ -9,7 +9,7 @@ proc main() =
var var
a, b: string a, b: string
p: int p: int
p = findSubStr("=", example) p = find(example, "=")
a = copy(example, 0, p-1) a = copy(example, 0, p-1)
b = copy(example, p+1) b = copy(example, p+1)
writeln(stdout, a & '=' & b) writeln(stdout, a & '=' & b)

View file

@ -157,7 +157,7 @@ proc main(options: string) =
var comp = callCompiler(filename, options) var comp = callCompiler(filename, options)
if sameResults(filename, spec, comp): inc(passed) if sameResults(filename, spec, comp): inc(passed)
inc(total) inc(total)
# ensure that the examples at least compiles # ensure that the examples at least compile
for filename in os.walkFiles("examples/*.nim"): for filename in os.walkFiles("examples/*.nim"):
var comp = callCompiler(filename, options) var comp = callCompiler(filename, options)
var shortfile = os.extractFilename(filename) var shortfile = os.extractFilename(filename)

3
tests/titer4.nim Executable file
View file

@ -0,0 +1,3 @@
for x in {'a'..'z'}: #ERROR_MSG iterator within for loop context expected
nil

23
tests/tmultim1.nim Executable file
View file

@ -0,0 +1,23 @@
# Test multi methods
type
TExpr = object
TLiteral = object of TExpr
x: int
TPlusExpr = object of TExpr
a, b: ref TExpr
method eval(e: ref TExpr): int = quit "to override!"
method eval(e: ref TLiteral): int = return e.x
method eval(e: ref TPlusExpr): int = return eval(e.a) + eval(e.b)
proc newLit(x: int): ref TLiteral =
new(result)
result.x = x
proc newPlus(a, b: ref TExpr): ref TPlusExpr =
new(result)
result.a = a
result.b = b
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) #OUT 7

30
tests/tmultim2.nim Executable file
View file

@ -0,0 +1,30 @@
# Test multi methods
type
TThing = object
TUnit = object of TThing
x: int
TParticle = object of TThing
a, b: int
method collide(a, b: TThing) {.inline.} =
quit "to override!"
method collide(a: TThing, b: TUnit) {.inline.} =
write stdout, "collide: thing, unit "
method collide(a: TUnit, b: TThing) {.inline.} =
write stdout, "collide: unit, thing "
proc test(a, b: TThing) {.inline.} =
collide(a, b)
var
a: TThing
b, c: TUnit
collide(b, c) # ambiguous unit, thing or thing, unit? -> prefer unit, thing!
test(b, c)
collide(a, b)
#OUT collide: unit, thing collide: unit, thing collide: thing, unit

17
tests/tobjcov.nim Executable file
View file

@ -0,0 +1,17 @@
# Covariance is not type safe:
type
TA = object
a: int
TB = object of TA
b: array[0..5000_000, int]
proc ap(x: var TA) = x.a = -1
proc bp(x: var TB) = x.b[high(x.b)] = -1
# in Nimrod proc (x: TB) is compatible to proc (x: TA),
# but this is not type safe:
var f: proc (x: var TA) = bp
var a: TA
f(a) # bp expects a TB, but gets a TA

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