version 0.8.2

This commit is contained in:
rumpf_a@web.de
2009-10-21 10:20:15 +02:00
parent 581572b28c
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) =
echo(cmd)
if executeShellCommand(cmd) != 0: quit("FAILURE")
if execShellCmd(cmd) != 0: quit("FAILURE")
proc writePlatdefC =
var f: TFile
@@ -38,19 +38,19 @@ proc rodsrc =
blacklist = ["nsystem", "nmath", "nos", "osproc", "ntime", "strutils"]
cmd = "nimrod boot --skip_proj_cfg -o:rod/$1.nim nim/$1"
for fi in walkFiles("nim/*.pas"):
var f = extractFileTrunk(fi)
var f = splitFile(fi).name
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):
Exec(cmd % f)
proc boot(args: string) =
writePlatdefC()
rodsrc()
var newExe = appendFileExt("rod/nimrod", ExeExt)
var oldExe = appendFileExt("bin/nimrod", ExeExt)
var newExe = addFileExt("rod/nimrod", ExeExt)
var oldExe = addFileExt("bin/nimrod", ExeExt)
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:
Exec(Bootcmd[i] % args)
if sameFileContent(newExe, oldExe):

View File

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

View File

@@ -28,9 +28,11 @@ Advanced options:
--checkpoints:on|off turn on|off checkpoints; for debugging Nimrod
--skip_cfg do not read the general configuration file
--skip_proj_cfg do not read the project's configuration file
--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
--putenv:key=value set an environment variable
--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)
-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
'sfStar', # symbol has * visibility
'sfMinus', # symbol has - visibility
@@ -34,7 +34,7 @@
'sfAddrTaken', # the variable's address is taken (ex- or implicitely)
'sfCompilerProc', # proc is a compiler proc, that is a C proc that is
# 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
'sfDeprecated', # symbol is deprecated
'sfInClosure', # variable is accessed by a closure
@@ -152,7 +152,6 @@
'nkAccQuoted', # `a` as a node
'nkTableConstr', # a table constructor {expr: expr}
'nkQualified', # describes a.b for qualified identifiers
'nkBind', # ``bind expr`` node
'nkSymChoice', # symbol choice node
'nkHiddenStdConv', # an implicit standard type conversion

View File

@@ -27,7 +27,7 @@ Options:
-a, --assertions:on|off code generation for assertions ON|OFF
--dead_code_elim:on|off whole program dead code elimination ON|OFF
--opt:none|speed|size optimize not at all or for speed|size
--app:console|gui 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
--advanced show advanced command line switches
-h, --help show this help

View File

@@ -92,7 +92,7 @@
{'errCannotEvalXBecauseIncompletelyDefined':
"cannot evalutate '$1' because type is not defined completely"},
{'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'"},
{'errNilAccess': 'attempt to access a nil address'},
{'errIndexOutOfBounds': 'index out of bounds'},
@@ -152,7 +152,7 @@
{'errButExpectedX': "but expected '$1'"},
{'errAmbiguousCallXYZ': 'ambiguous call; both $1 and $2 match for: $3'},
{'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'},
{'errPragmaOnlyInHeaderOfProc':
'pragmas are only in the header of a proc allowed'},
@@ -186,7 +186,6 @@
{'errATypeHasNoValue': 'a type has no value'},
{'errXisNoType': "invalid type: '$1'"},
{'errCircumNeedsPointer': "'^' needs a pointer or reference type"},
{'errInvalidContextForBuiltinX': "invalid context for builtin '$1'"},
{'errInvalidExpression': 'invalid expression'},
{'errInvalidExpressionX': "invalid expression: '$1'"},
{'errEnumHasNoValueX': "enum has no value '$1'"},
@@ -250,6 +249,8 @@
{'warnUnknownSubstitutionX': "unknown substitution '$1'"},
{'warnLanguageXNotSupported': "language '$1' not supported"},
{'warnCommentXIgnored': "comment '$1' ignored"},
{'warnXisPassedToProcVar': "'$1' is passed to a procvar; deprecated"},
# user warning message:
{'warnUser': '$1'},

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

@@ -66,4 +66,7 @@ function func
coordinate coord
rectangle rect
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 implements the top-level command dispatching
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
scanner lexical analyser
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)*
dollarExpr ::= primary (OP7 optInd primary)*
namedTypeOrExpr ::=
'..' [expr]
| expr ['=' (expr ['..' expr] | typeDescK | '..' [expr]) | '..' [expr]]
| typeDescK
indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr]
castExpr ::= 'cast' '[' optInd typeDesc [SAD] ']' '(' optInd expr [SAD] ')'
addrExpr ::= 'addr' '(' optInd expr ')'
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')'
| '[' ']' | '=' | literal)+ '`'
| IDENT
primary ::= ((prefixOperator | 'bind') optInd)* (symbol | constructor |
castExpr | addrExpr) (
'.' optInd symbol
| '(' optInd namedExprList [SAD] ')'
| '[' optInd
[namedTypeOrExpr (comma namedTypeOrExpr)* [comma]]
[SAD] ']'
| '^'
| pragma)*
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
@@ -59,24 +57,21 @@ namedExprList ::= [namedExpr (comma namedExpr)* [comma]]
sliceExpr ::= expr ['..' expr]
sliceExprList ::= [sliceExpr (comma sliceExpr)* [comma]]
anonymousProc ::= 'lambda' paramList [pragma] '=' stmt
expr ::= lowestExpr
| anonymousProc
| 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
exprOrType ::= lowestExpr
| 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
| 'var' exprOrType
| 'ref' exprOrType
| 'ptr' exprOrType
| 'type' exprOrType
| 'tuple' tupleDesc
namedTypeDesc ::= typeDescK | expr ['=' (typeDescK | expr)]
namedTypeDescList ::= [namedTypeDesc (comma namedTypeDesc)* [comma]]
expr ::= exprOrType
| 'proc' paramList [pragma] ['=' stmt]
qualifiedIdent ::= symbol ['.' symbol]
typeDescK ::= 'var' typeDesc
| 'ref' typeDesc
| 'ptr' typeDesc
| 'type' expr
| 'tuple' tupleDesc
| 'proc' paramList [pragma]
typeDesc ::= typeDescK | primary
typeDesc ::= exprOrType
| 'proc' paramList [pragma]
macroStmt ::= ':' [stmt] ('of' [sliceExprList] ':' stmt
|'elif' expr ':' stmt
@@ -84,20 +79,20 @@ macroStmt ::= ':' [stmt] ('of' [sliceExprList] ':' stmt
['else' ':' stmt]
simpleStmt ::= returnStmt
| yieldStmt
| discardStmt
| raiseStmt
| breakStmt
| continueStmt
| pragma
| importStmt
| fromStmt
| includeStmt
| exprStmt
| yieldStmt
| discardStmt
| raiseStmt
| breakStmt
| continueStmt
| pragma
| importStmt
| fromStmt
| includeStmt
| exprStmt
complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
| blockStmt | asmStmt
| procDecl | iteratorDecl | macroDecl | templateDecl
| constSection | typeSection | whenStmt | varSection
| blockStmt | asmStmt
| procDecl | iteratorDecl | macroDecl | templateDecl | methodDecl
| constSection | typeSection | whenStmt | varSection
indPush ::= IND # and push indentation onto the stack
indPop ::= # pop indentation from the stack
@@ -141,25 +136,24 @@ paramList ::= ['(' [param (comma param)*] [SAD] ')'] [':' typeDesc]
genericParam ::= symbol [':' typeDesc] ['=' expr]
genericParams ::= '[' genericParam (comma genericParam)* [SAD] ']'
procDecl ::= 'proc' symbol ['*'] [genericParams] paramList [pragma]
['=' stmt]
macroDecl ::= 'macro' symbol ['*'] [genericParams] paramList [pragma]
['=' stmt]
iteratorDecl ::= 'iterator' symbol ['*'] [genericParams] paramList [pragma]
['=' stmt]
templateDecl ::= 'template' symbol ['*'] [genericParams] paramList [pragma]
['=' stmt]
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 | IND COMMENT]
| COMMENT
constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop
typeDef ::= typeDesc | objectDef | enumDef | 'abstract' typeDesc
typeDef ::= typeDesc | objectDef | enumDef | 'distinct' typeDesc
objectField ::= symbol ['*'] [pragma]
objectIdentPart ::=
objectField (comma objectField)* ':' typeDesc [COMMENT|IND COMMENT]
objectIdentPart ::= objectField (comma objectField)* ':' typeDesc
[COMMENT|IND COMMENT]
objectWhen ::= 'when' expr ':' [COMMENT] objectPart
('elif' expr ':' [COMMENT] objectPart)*

View File

@@ -66,8 +66,7 @@ Generic Operating System Services
* `os <os.html>`_
Basic operating system facilities like retrieving environment variables,
reading command line arguments, working with directories, running shell
commands, etc. This module is -- like any other basic library --
platform independant.
commands, etc.
* `osproc <osproc.html>`_
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
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.
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
not ambiguous.
The grammar's start symbol is ``module``.
.. include:: grammar.txt
:literal:
@@ -875,8 +874,7 @@ Procedural type
~~~~~~~~~~~~~~~
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
types to achieve `functional`:idx: programming techniques. Dynamic dispatch
for OOP constructs can also be implemented with procedural types.
types to achieve `functional`:idx: programming techniques.
Example:
@@ -946,6 +944,16 @@ each other:
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
~~~~~~~~~~~~~
@@ -1054,7 +1062,7 @@ describe the type checking done by the compiler.
Type equality
~~~~~~~~~~~~~
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:
.. 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
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2275,6 +2354,12 @@ error to mark a proc/iterator to have no side effect if the compiler cannot
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
------------------
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.
Thus lots of options and pragmas for tweaking the generated C code are
available.
Lots of options and pragmas for tweaking the generated C code are available.
Importc Pragma
~~~~~~~~~~~~~~
@@ -137,7 +136,7 @@ and instead the generated code should contain an ``#include``:
PFile {.importc: "FILE*", header: "<stdio.h>".} = distinct pointer
# 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
in angle brackets: ``<>``. If no angle brackets are given, Nimrod
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
~~~~~~~~~~~~~~
The `varargs`:idx: pragma can be applied to procedures only. It tells Nimrod
that the proc can take a variable number of parameters after the last
specified parameter. Nimrod string values will be converted to C
The `varargs`:idx: pragma can be applied to procedures only (and procedure
types). It tells Nimrod that the proc can take a variable number of parameters
after the last specified parameter. Nimrod string values will be converted to C
strings automatically:
.. code-block:: Nimrod
@@ -218,7 +217,7 @@ collector to not consider objects of this type as part of a cycle:
data: string
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
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

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)
========================
@@ -10,7 +10,7 @@ Nimrod Tutorial (Part I)
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
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
As you see, with the ``--run`` switch Nimrod executes the file automatically
after compilation. You can even give your program command line arguments by
With the ``--run`` switch Nimrod executes the file automatically
after compilation. You can give your program command line arguments by
appending them after the filename::
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
impossible or needs ugly workarounds.
* Often it is unclear where the method should belong to: Is
``join`` a string method or an array method? Should the complex
``vertexCover`` algorithm really be a method of the ``graph`` class?
``join`` a string method or an array method?
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
@@ -206,7 +205,7 @@ for any type:
(Another way to look at the method call syntax is that it provides the missing
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
import strutils
@@ -277,7 +276,7 @@ already provides ``v[]`` access.
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``:
.. 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
-----------------------
Install Nimrod by downloading and running
the ``nimrod_$version.exe`` file. As default, the ``GCC``
compiler is used that is bundled with this installer. **You can change
the configuration file** ``config/nimrod.cfg`` **to use another C compiler
or change the path to GCC.**
Install Nimrod by downloading and running the ``nimrod_$version.exe`` file.
As default, the ``GCC`` compiler is used that is bundled with this installer.
**You can change the configuration file** ``config/nimrod.cfg`` **to use
another C compiler or change the path to GCC.**
Currently, the following C compilers are supported under Windows:

View File

@@ -12,7 +12,7 @@ from pycompab import *
# --------------------- constants ----------------------------------------
NIMROD_VERSION = '0.8.1'
NIMROD_VERSION = '0.8.2'
# This string contains Nimrod's version. It is the only place
# where the version needs to be updated. The rest is done by
# the build process automatically. It is replaced **everywhere**!
@@ -327,8 +327,8 @@ def CogRule(name, filename, dependson):
_nim_exe = os.path.join(os.getcwd(), "bin", ExeExt("nim"))
_output_obj = os.path.join(os.getcwd(), "obj")
FPC_CMD = Subs(r"fpc -Cs16777216 -gl -bl -Crtoi -Sgidh -vw -Se1 -o$1 "
r"-FU$2 $3", _nim_exe, _output_obj,
FPC_CMD = Subs("fpc -Cs16777216 -gl -bl -Crtoi -Sgidh -vw -Se1 -o\"$1\" "
"-FU\"$2\" \"$3\"", _nim_exe, _output_obj,
os.path.join(os.getcwd(), "nim", "nimrod.pas"))
def buildRod(options):
@@ -586,6 +586,7 @@ def cmd_clean(dir = "."):
for f in Glob("*.idb"): Remove(f)
for f in Glob("web/*.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
def visit(extRegEx, dirname, names):
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_SAFECALL_PTR(rettype, name) rettype (__safecall *name)
# define N_LIB_EXPORT __declspec(dllexport)
# define N_LIB_IMPORT __declspec(dllimport)
# define N_LIB_EXPORT extern __declspec(dllexport)
# define N_LIB_IMPORT extern __declspec(dllimport)
#else
# define N_CDECL(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_SAFECALL_PTR(rettype, name) rettype (*name)
# define N_LIB_EXPORT
# define N_LIB_EXPORT extern
# define N_LIB_IMPORT extern
#endif

View File

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

View File

@@ -790,7 +790,7 @@ iterator walkFiles*(pattern: string): string =
if res != -1:
while true:
if f.cFileName[0] != '.':
yield extractDir(pattern) / extractFilename($f.cFileName)
yield splitFile(pattern).dir / extractFilename($f.cFileName)
if findnextFileA(res, f) == 0'i32: break
findclose(res)
else: # here we use glob
@@ -811,8 +811,12 @@ type
TPathComponent* = enum ## Enumeration specifying a path component.
pcFile, ## path refers to a file
pcLinkToFile, ## path refers to a symbolic link to a file
pcDirectory, ## path refers to a directory
pcLinkToDirectory ## path refers to a symbolic link to a directory
pcDir, ## path refers 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] =
## 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
if f.cFilename[0] != '.':
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
k = pcDirectory
k = pcDir
yield (k, dir / extractFilename($f.cFilename))
if findnextFileA(h, f) == 0'i32: break
findclose(h)
@@ -859,11 +863,33 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
y = dir / y
if stat(y, s) < 0'i32: break
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)
yield (k, y)
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) =
when defined(windows):
if RemoveDirectoryA(dir) == 0'i32: OSError()
@@ -871,12 +897,12 @@ proc rawRemoveDir(dir: string) =
if rmdir(dir) != 0'i32: OSError()
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.
for kind, path in walkDir(dir):
case kind
of pcFile, pcLinkToFile, pcLinkToDirectory: removeFile(path)
of pcDirectory: removeDir(dir)
of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
of pcDir: removeDir(dir)
rawRemoveDir(dir)
proc rawCreateDir(dir: string) =
@@ -935,7 +961,7 @@ type
fpOthersRead ## read access for others
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
## permission is available in any case.
when defined(posix):
@@ -1103,4 +1129,14 @@ proc getApplicationDir*(): string =
## Returns the directory of the application's executable.
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.}

View File

@@ -9,19 +9,19 @@
## This module implements an advanced facility for executing OS processes
## and process communication.
## **On Windows this module does not work properly. Please help!**
import
os, strtabs, streams
strutils, os, strtabs, streams
when defined(windows):
import winlean
else:
import posix
type
TProcess = object of TObject
when defined(windows):
FProcessHandle: Thandle
FThreadHandle: Thandle
inputHandle, outputHandle, errorHandle: TFileHandle
else:
inputHandle, outputHandle, errorHandle: TFileHandle
@@ -31,10 +31,11 @@ type
PProcess* = ref TProcess ## represents an operating system process
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
## 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,
options: set[TProcessOption] = {poStdErrToStdOut,
@@ -77,18 +78,6 @@ proc startProcess*(command: string,
## Return value: The newly created process object. Nil is never returned,
## 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)
## Suspends the process `p`.
@@ -117,13 +106,108 @@ proc outputStream*(p: PProcess): PStream
proc errorStream*(p: PProcess): PStream
## 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,
options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): string =
var c = parseCmdLine(command)
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 p = startProcessAux(command, options=options)
var outp = outputStream(p)
result = ""
while running(p) or not outp.atEnd(outp):
@@ -147,15 +231,19 @@ when defined(Windows):
atTheEnd: bool
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 =
if s.atTheEnd: return 0
var br: int32
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
#atEnd = bytesRead < bufLen
proc hsWriteData(s: PFileHandleStream, buffer: pointer, bufLen: int) =
var bytesWritten: int32
var a = winlean.writeFile(s.handle, buffer, bufLen, bytesWritten, nil)
@@ -168,18 +256,14 @@ when defined(Windows):
result.atEnd = hsAtEnd
result.readData = hsReadData
result.writeData = hsWriteData
proc buildCommandLine(a: string, args: openarray[string]): cstring =
var L = a.len
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):
result[L] = ' '
inc(L)
copyMem(addr(result[L]), cstring(args[i]), args[i].len)
inc(L, args[i].len)
var res = quoteIfContainsWhite(a)
for i in 0..high(args):
res.add(' ')
res.add(quoteIfContainsWhite(args[i]))
result = cast[cstring](alloc0(res.len+1))
copyMem(result, cstring(res), res.len)
proc buildEnv(env: PStringTable): cstring =
var L = 0
@@ -198,72 +282,81 @@ when defined(Windows):
# O_WRONLY {.importc: "_O_WRONLY", 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
piInheritablePipe.nlength = SizeOF(TSecurityAttributes)
piInheritablePipe.lpSecurityDescriptor = nil
piInheritablePipe.Binherithandle = 1
if CreatePipe(Inhandle, Outhandle, piInheritablePipe, 0) == 0'i32:
if CreatePipe(Rdhandle, Wrhandle, piInheritablePipe, 1024) == 0'i32:
OSError()
proc startProcess*(command: string,
proc fileClose(h: THandle) {.inline.} =
if h > 4: discard CloseHandle(h)
proc startProcess(command: string,
workingDir: string = "",
args: openarray[string] = [],
env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut}): PProcess =
new(result)
var
SI: TStartupInfo
ProcInfo: TProcessInformation
success: int
hi, ho, he: THandle
new(result)
SI.cb = SizeOf(SI)
SI.dwFlags = STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES
CreatePipeHandles(SI.hStdInput, HI)
CreatePipeHandles(HO, Si.hStdOutput)
if poStdErrToStdOut in options:
SI.hStdError = SI.hStdOutput
HE = HO
if poParentStreams notin options:
SI.dwFlags = STARTF_USESTDHANDLES # STARTF_USESHOWWINDOW or
CreatePipeHandles(SI.hStdInput, HI)
CreatePipeHandles(HO, Si.hStdOutput)
if poStdErrToStdOut in options:
SI.hStdError = SI.hStdOutput
HE = HO
else:
CreatePipeHandles(HE, Si.hStdError)
result.inputHandle = hi
result.outputHandle = ho
result.errorHandle = he
else:
CreatePipeHandles(HE, Si.hStdError)
result.inputHandle = hi
result.outputHandle = ho
result.errorHandle = he
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
if poUseShell in options:
var comspec = getEnv("COMSPEC")
var a: seq[string] = @[]
add(a, "/c")
add(a, command)
add(a, args)
cmdl = buildCommandLine(comspec, a)
if false: # poUseShell in options:
cmdl = buildCommandLine(getEnv("COMSPEC"), @["/c", command] & args)
else:
cmdl = buildCommandLine(command, args)
var wd: cstring = nil
var e: cstring = nil
if len(workingDir) > 0: wd = workingDir
if env == nil:
success = winlean.CreateProcess(nil,
cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, nil, wd, SI, ProcInfo)
else:
var e = buildEnv(env)
success = winlean.CreateProcess(nil,
cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
dealloc(e)
if env != nil: e = buildEnv(env)
if poEchoCmd in options: echo($cmdl)
success = winlean.CreateProcess(nil,
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
if poParentStreams notin options:
FileClose(si.hStdInput)
FileClose(si.hStdOutput)
if poStdErrToStdOut notin options:
FileClose(si.hStdError)
if e != nil: dealloc(e)
dealloc(cmdl)
if success == 0:
OSError()
# NEW:
# Close the handles now so anyone waiting is woken.
if success == 0: OSError()
# Close the handle now so anyone waiting is woken:
discard closeHandle(procInfo.hThread)
result.FProcessHandle = procInfo.hProcess
result.FThreadHandle = procInfo.hThread
result.id = procInfo.dwProcessID
proc suspend(p: PProcess) =
discard SuspendThread(p.FThreadHandle)
discard SuspendThread(p.FProcessHandle)
proc resume(p: PProcess) =
discard ResumeThread(p.FThreadHandle)
discard ResumeThread(p.FProcessHandle)
proc running(p: PProcess): bool =
var x = waitForSingleObject(p.FProcessHandle, 50)
@@ -274,7 +367,6 @@ when defined(Windows):
discard TerminateProcess(p.FProcessHandle, 0)
proc waitForExit(p: PProcess): int =
#CloseHandle(p.FThreadHandle)
discard WaitForSingleObject(p.FProcessHandle, Infinite)
var res: int32
discard GetExitCodeProcess(p.FProcessHandle, res)
@@ -314,17 +406,15 @@ when defined(Windows):
discard CloseHandle(Process)
else:
import posix
const
readIdx = 0
writeIdx = 1
proc addCmdArgs(command: string, args: openarray[string]): string =
result = command
result = quoteIfContainsWhite(command)
for i in 0 .. high(args):
add(result, " ")
add(result, args[i])
add(result, quoteIfContainsWhite(args[i]))
proc toCStringArray(b, a: openarray[string]): cstringArray =
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)
inc(i)
proc startProcess*(command: string,
proc startProcess(command: string,
workingDir: string = "",
args: openarray[string] = [],
env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut}): PProcess =
new(result)
var
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:
OSError("failed to create a pipe")
var Pid = fork()
@@ -389,6 +480,8 @@ else:
# too risky to raise an exception here:
quit("execve call failed: " & $strerror(errno))
# Parent process. Copy process information.
if poEchoCmd in options:
echo(command & " " & join(args, " "))
result.id = pid
result.inputHandle = p_stdin[writeIdx]
@@ -415,9 +508,15 @@ else:
if running(p): discard kill(p.id, SIGKILL)
proc waitForExit(p: PProcess): int =
result = 1
if waitPid(p.id, p.exitCode, 0) == int(p.id):
result = p.exitCode
#if waitPid(p.id, p.exitCode, 0) == int(p.id):
# ``waitPid`` fails if the process is not running anymore. But then
# ``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 =
var f: TFile
@@ -440,4 +539,5 @@ else:
result = csystem(command)
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.
## **Deprecated** since version 0.8.2. Use the module `re` instead.
{.deprecated.}
## Currently this module is implemented by providing a wrapper around the
## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
## 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 =
## 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 =
## 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':
c = readChar(s)
break
elif c == '\L': break
elif c == '\L' or c == '\0': break
result.add(c)
type

View File

@@ -45,10 +45,6 @@ const
IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
## the set of characters an identifier can start with
# strStart* = 0 ## this is only for bootstraping
# ## XXX: remove this someday
# nl* = "\n" ## this is only for bootstraping XXX: remove this someday
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
## The `substitution`:idx: operator performs string substitutions in
## `formatstr` and returns a modified `formatstr`. This is often called
@@ -98,51 +94,30 @@ proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect.}
## If `leading` is true, leading whitespace is stripped.
## If `trailing` is true, trailing whitespace is stripped.
proc toLower*(s: string): string {.noSideEffect.}
proc toLower*(s: string): string {.noSideEffect, procvar.}
## 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.
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.
## 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.
## 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.
## 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.
## 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
## 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.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## 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
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.}
## Same as ``findSubStr(sub, s) >= 0``.
## Same as ``find(s, sub) >= 0``.
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.}
## 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
## 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
## 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
## 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
## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
## ``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..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:
var
m = len(sub)
@@ -631,36 +606,10 @@ proc findSubStrAux(s, sub: string, start: int, a: TSkipTable): int =
inc(j, a[s[j+m]])
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 =
var a: TSkipTable
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 =
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
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 =
return find(s, chars) >= 0
@@ -698,7 +637,7 @@ proc replace*(s, sub, by: string): string =
preprocessSub(sub, a)
var i = 0
while true:
var j = findSubStrAux(s, sub, i, a)
var j = findAux(s, sub, i, a)
if j < 0: break
add result, copy(s, i, j - 1)
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: 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`.
## For containers that have an order, `add` means *append*. New generic
## 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
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.
littleEndian, bigEndian
@@ -1072,28 +1073,25 @@ proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
# This is an undocumented pragma that can only be used
# once in the system module.
proc `&` *[T](x, y: seq[T]): seq[T] {.noSideEffect.} =
proc `&` *[T](x, y: openArray[T]): seq[T] {.noSideEffect.} =
newSeq(result, x.len + y.len)
for i in 0..x.len-1:
result[i] = x[i]
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)
for i in 0..x.len-1:
result[i] = x[i]
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)
for i in 0..y.len-1:
result[i] = y[i]
result[y.len] = x
proc `&` *[T](x, y: T): seq[T] {.noSideEffect.} =
return [x, y]
when not defined(NimrodVM):
when not defined(ECMAScript):
proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =

View File

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

View File

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

View File

@@ -1086,6 +1086,8 @@ begin
app(pl, ', ')
end;
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);
app(pl, addrLoc(d));
end;
@@ -2212,7 +2214,7 @@ begin
'); unknown symbol')
end
end;
nkQualified: expr(p, e.sons[1], d);
//nkQualified: expr(p, e.sons[1], d);
nkStrLit..nkTripleStrLit, nkIntLit..nkInt64Lit,
nkFloatLit..nkFloat64Lit, nkNilLit, nkCharLit: begin
putIntoDest(p, d, e.typ, genLiteral(p, e));

View File

@@ -221,7 +221,8 @@ begin
assert(not (sfResult in s.flags));
case pt.Kind of
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
else if (tfFinal in pt.flags) and (pt.sons[0] = nil) then
result := false // no need, because no subtyping possible

View File

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

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

View File

@@ -1,7 +1,7 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
@@ -85,7 +85,7 @@ const
+{&} ' -a, --assertions:on|off code generation for assertions ON|OFF' +{&} nl
+{&} ' --dead_code_elim:on|off whole program dead code elimination ON|OFF' +{&} nl
+{&} ' --opt:none|speed|size optimize not at all or for speed|size' +{&} nl
+{&} ' --app:console|gui 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
+{&} ' --advanced show advanced command line switches' +{&} nl
+{&} ' -h, --help show this help' +{&} nl
@@ -127,10 +127,12 @@ const
+{&} ' --checkpoints:on|off turn on|off checkpoints; for debugging Nimrod' +{&} nl
+{&} ' --skip_cfg do not read the general configuration file' +{&} nl
+{&} ' --skip_proj_cfg do not read the project''s configuration file' +{&} nl
+{&} ' --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
+{&} ' --putenv:key=value set an environment variable' +{&} 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
+{&} ' -v, --version show detailed version information' +{&} nl
//[[[end]]]
@@ -152,7 +154,7 @@ begin
if (pass = passCmd1) and not helpWritten then begin
// BUGFIX 19
MessageOut(getCommandLineDesc());
helpWritten := true;
helpWritten := true;
halt(0);
end
end;
@@ -362,10 +364,10 @@ begin
wNoLinking: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optNoLinking);
end;
wNoMain: begin
end;
wNoMain: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optNoMain);
include(gGlobalOptions, optNoMain);
end;
wForceBuild, wF: begin
expectNoArg(switch, arg, pass, info);
@@ -523,6 +525,10 @@ begin
expectArg(switch, arg, pass, info);
gVerbosity := parseInt(arg);
end;
wParallelBuild: begin
expectArg(switch, arg, pass, info);
gNumberOfProcessors := parseInt(arg);
end;
wVersion, wV: begin
expectNoArg(switch, arg, pass, info);
writeVersionInfo(pass);

View File

@@ -51,12 +51,12 @@ begin
case n.kind of
nkImportStmt: 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);
end
end;
nkFromStmt: begin
imported := getFileTrunk(getModuleFile(n.sons[0]));
imported := extractFileTrunk(getModuleFile(n.sons[0]));
addDependencyAux(g.module.name.s, imported);
end;
nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr: begin

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,10 +15,10 @@ unit main;
interface
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,
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;
procedure MainCommand(const cmd, filename: string);
@@ -69,7 +69,7 @@ begin
{@emit}
result.id := -1; // for better error checking
result.kind := skModule;
result.name := getIdent(getFileTrunk(filename));
result.name := getIdent(extractFileTrunk(filename));
result.owner := result; // a module belongs to itself
result.info := newLineInfo(filename, 1, 1);
include(result.flags, sfUsed);
@@ -101,7 +101,7 @@ var
f: string;
begin
rd := nil;
f := appendFileExt(filename, nimExt);
f := addFileExt(filename, nimExt);
result := newModule(filename);
if isMainFile then include(result.flags, sfMainModule);
if isSystemFile then include(result.flags, sfSystemModule);
@@ -118,8 +118,8 @@ end;
procedure CompileProject(const filename: string);
begin
{@discard} CompileModule(
JoinPath(options.libpath, appendFileExt('system', nimExt)), false, true);
{@discard} CompileModule(appendFileExt(filename, nimExt), true, false);
JoinPath(options.libpath, addFileExt('system', nimExt)), false, true);
{@discard} CompileModule(addFileExt(filename, nimExt), true, false);
end;
procedure semanticPasses;
@@ -137,7 +137,7 @@ begin
compileProject(filename);
generateDot(filename);
execExternalProgram('dot -Tpng -o' +{&} changeFileExt(filename, 'png') +{&}
' ' +{&} changeFileExt(filename, 'dot'));
' ' +{&} changeFileExt(filename, 'dot'));
end;
procedure CommandCheck(const filename: string);
@@ -185,7 +185,7 @@ begin
// load system module:
{@discard} CompileModule(
JoinPath(options.libpath, appendFileExt('system', nimExt)), false, true);
JoinPath(options.libpath, addFileExt('system', nimExt)), false, true);
m := newModule('stdin');
m.id := getID();
@@ -228,7 +228,7 @@ procedure CommandExportSymbols(const filename: string);
var
module: PNode;
begin
module := parseFile(appendFileExt(filename, NimExt));
module := parseFile(addFileExt(filename, NimExt));
if module <> nil then begin
exSymbols(module);
renderModule(module, getOutFile(filename, 'pretty.'+NimExt));
@@ -239,7 +239,7 @@ procedure CommandPretty(const filename: string);
var
module: PNode;
begin
module := parseFile(appendFileExt(filename, NimExt));
module := parseFile(addFileExt(filename, NimExt));
if module <> nil then
renderModule(module, getOutFile(filename, 'pretty.'+NimExt));
end;
@@ -255,7 +255,7 @@ begin
fillChar(tok, sizeof(tok), 0);
fillChar(L, sizeof(L), 0);
{@emit}
f := appendFileExt(filename, 'pas');
f := addFileExt(filename, 'pas');
stream := LLStreamOpen(f, fmRead);
if stream <> nil then begin
OpenLexer(L, f, stream);
@@ -277,7 +277,7 @@ var
f: string;
stream: PLLStream;
begin
f := appendFileExt(filename, 'pas');
f := addFileExt(filename, 'pas');
stream := LLStreamOpen(f, fmRead);
if stream <> nil then begin
OpenPasParser(p, f, stream);
@@ -300,7 +300,7 @@ begin
{@ignore}
fillChar(tok^, sizeof(tok^), 0);
{@emit}
f := appendFileExt(filename, nimExt);
f := addFileExt(filename, nimExt);
stream := LLStreamOpen(f, fmRead);
if stream <> nil then begin
openLexer(L, f, stream);
@@ -331,8 +331,7 @@ begin
prependStr(searchPaths, dir);
end;
setID(100);
passes.gIncludeFile := parseFile;
passes.gIncludeTmplFile := ptmplsyn.parseTmplFile;
passes.gIncludeFile := syntaxes.parseFile;
passes.gImportModule := importModule;
case whichKeyword(cmd) of
@@ -408,7 +407,7 @@ begin
wParse: begin
gCmd := cmdParse;
wantFile(filename);
{@discard} parseFile(appendFileExt(filename, nimExt));
{@discard} parseFile(addFileExt(filename, nimExt));
end;
wScan: begin
gCmd := cmdScan;

View File

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

View File

@@ -1,7 +1,7 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2006 Andreas Rumpf
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
@@ -28,15 +28,27 @@ var
procedure ProcessCmdLine(pass: TCmdLinePass; var command, filename: string);
var
p: TOptParser;
bracketLe: int;
key, val: string;
begin
p := parseopt.init();
while true do begin
parseopt.next(p);
case p.kind of
cmdEnd: break;
cmdLongOption, cmdShortOption:
ProcessSwitch(p.key, p.val, pass, cmdLineInfo);
cmdArgument: begin
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);
end;
cmdArgument: begin
if command = '' then command := p.key
else if filename = '' then begin
filename := unixToNativePath(p.key);

View File

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

View File

@@ -55,6 +55,11 @@ const
snil = '';
type
TStringSeq = array of string;
TCharSet = set of Char;
type
Natural = 0..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 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.
procedure add(var s: TStringSeq; const y: string); overload;
function isNil(s: string): bool;
implementation
@@ -238,6 +245,15 @@ begin
else x := x + y;
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}
function alloc(size: int): Pointer;

View File

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

View File

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

View File

@@ -17,14 +17,42 @@ interface
uses
nsystem, nos;
type
TProcessOption = (poEchoCmd, poUseShell, poStdErrToStdOut, poParentStreams);
TProcessOptions = set of TProcessOption;
function executeCommand(const cmd: string): int;
function execCmd(const cmd: string): int;
function execProcesses(const cmds: array of string;
options: TProcessOptions;
n: int): int;
function countProcessors(): int;
implementation
function executeCommand(const cmd: string): int;
begin
function execCmd(const cmd: string): int;
begin
writeln(output, cmd);
result := executeShellCommand(cmd);
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.

View File

@@ -61,7 +61,7 @@ const
('len', 'length'),
('setlength', 'setlen')
);
nimReplacements: array [1..34] of TReplaceTuple = (
nimReplacements: array [1..35] of TReplaceTuple = (
('nimread', 'read'),
('nimwrite', 'write'),
('nimclosefile', 'close'),
@@ -98,7 +98,8 @@ const
('ttextfile', 'tfile'),
('tbinaryfile', 'tfile'),
('strstart', '0'+''),
('nl', '"\n"')
('nl', '"\n"'),
('tostring', '$'+'')
{,
('NL', '"\n"'),
('tabulator', '''\t'''),
@@ -455,7 +456,7 @@ begin
skipCom(p, result);
if p.tok.xkind = pxSymbol then begin
a := result;
result := newNodeI(nkQualified, a.info);
result := newNodeI(nkDotExpr, a.info);
addSon(result, a);
addSon(result, createIdentNodeP(p.tok.ident, p));
getTok(p);
@@ -1279,6 +1280,13 @@ begin
noBody := true;
getTok(p); opt(p, pxSemicolon);
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
if result = nil then result := newNodeP(nkPragma, p);
addSon(result, newIdentNodeP(getIdent('varargs'), p));

View File

@@ -1,7 +1,7 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
@@ -19,7 +19,7 @@ uses
nsystem, charsets, strutils,
lists, options, ast, astalgo, llstream,
msgs, platform, nos, condsyms, idents, rnimsyn, types,
extccomp, nmath, magicsys, nversion, nimsets, pnimsyn, ntime, rodread;
extccomp, nmath, magicsys, nversion, nimsets, syntaxes, ntime, rodread;
type
TPassContext = object(NObject) // the pass's context
@@ -61,7 +61,6 @@ function astNeeded(s: PSym): bool;
var
gImportModule: function (const filename: string): PSym;
gIncludeFile: function (const filename: string): PNode;
gIncludeTmplFile: function (const filename: string): PNode;
implementation
@@ -165,7 +164,7 @@ end;
procedure processModule(module: PSym; const filename: string;
stream: PLLStream; rd: PRodReader);
var
p: TParser;
p: TParsers;
n: PNode;
a: TPassContextArray;
s: PLLStream;
@@ -183,13 +182,13 @@ begin
else
s := stream;
while true do begin
openParser(p, filename, s);
openParsers(p, filename, s);
while true do begin
n := parseTopLevelStmt(p);
if n = nil then break;
processTopLevelStmt(n, a)
end;
closeParser(p);
closeParsers(p);
if s.kind <> llsStdIn then break;
end;
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
// (c) Copyright 2008 Andreas Rumpf
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
@@ -59,9 +59,8 @@ type
TInfoOS = record{@tuple}
name: string;
parDir: string;
dllExt: string;
dllFrmt: string;
altDirSep: string;
dllPrefix: string;
objExt: string;
newLine: string;
pathSep: string;
@@ -77,9 +76,8 @@ const
(
name: 'DOS';
parDir: '..';
dllExt: '.dll';
dllFrmt: '$1.dll';
altDirSep: '/'+'';
dllPrefix: '';
objExt: '.obj';
newLine: #13#10;
pathSep: ';'+'';
@@ -93,9 +91,8 @@ const
(
name: 'Windows';
parDir: '..';
dllExt: '.dll';
dllFrmt: '$1.dll';
altDirSep: '/'+'';
dllPrefix: '';
objExt: '.obj';
newLine: #13#10;
pathSep: ';'+'';
@@ -109,9 +106,8 @@ const
(
name: 'OS2';
parDir: '..';
dllExt: '.dll';
dllFrmt: '$1.dll';
altDirSep: '/'+'';
dllPrefix: '';
objExt: '.obj';
newLine: #13#10;
pathSep: ';'+'';
@@ -125,9 +121,8 @@ const
(
name: 'Linux';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -141,9 +136,8 @@ const
(
name: 'MorphOS';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -157,9 +151,8 @@ const
(
name: 'SkyOS';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -173,9 +166,8 @@ const
(
name: 'Solaris';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -189,9 +181,8 @@ const
(
name: 'Irix';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -205,9 +196,8 @@ const
(
name: 'NetBSD';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -221,9 +211,8 @@ const
(
name: 'FreeBSD';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -237,9 +226,8 @@ const
(
name: 'OpenBSD';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -253,9 +241,8 @@ const
(
name: 'AIX';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -269,9 +256,8 @@ const
(
name: 'PalmOS';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -285,9 +271,8 @@ const
(
name: 'QNX';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -301,9 +286,8 @@ const
(
name: 'Amiga';
parDir: '..';
dllExt: '.library';
dllFrmt: '$1.library';
altDirSep: '/'+'';
dllPrefix: '';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -317,9 +301,8 @@ const
(
name: 'Atari';
parDir: '..';
dllExt: '.dll';
dllFrmt: '$1.dll';
altDirSep: '/'+'';
dllPrefix: '';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -333,9 +316,8 @@ const
(
name: 'Netware';
parDir: '..';
dllExt: '.nlm';
dllFrmt: '$1.nlm';
altDirSep: '/'+'';
dllPrefix: '';
objExt: '';
newLine: #13#10;
pathSep: ':'+'';
@@ -349,9 +331,8 @@ const
(
name: 'MacOS';
parDir: '::';
dllExt: 'Lib';
dllFrmt: '$1Lib';
altDirSep: ':'+'';
dllPrefix: '';
objExt: '.o';
newLine: #13+'';
pathSep: ','+'';
@@ -365,9 +346,8 @@ const
(
name: 'MacOSX';
parDir: '..';
dllExt: '.dylib';
dllFrmt: 'lib$1.dylib';
altDirSep: ':'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -381,9 +361,8 @@ const
(
name: 'EcmaScript';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';
@@ -397,9 +376,8 @@ const
(
name: 'NimrodVM';
parDir: '..';
dllExt: '.so';
dllFrmt: 'lib$1.so';
altDirSep: '/'+'';
dllPrefix: 'lib';
objExt: '.o';
newLine: #10+'';
pathSep: ':'+'';

View File

@@ -22,7 +22,7 @@ interface
uses
nsystem, llstream, scanner, idents, strutils, ast, msgs;
function ParseFile(const filename: string): PNode;
// function ParseFile(const filename: string): PNode;
type
TParser = record // a TParser object represents a module that
@@ -31,10 +31,7 @@ type
tok: PToken; // the current token
end;
function ParseModule(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;
function ParseAll(var p: TParser): PNode;
procedure openParser(var p: TParser; const filename: string;
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
// of stream.
implementation
function ParseFile(const filename: string): PNode;
var
p: TParser;
f: TBinaryFile;
begin
if not OpenFile(f, filename) then begin
rawMessage(errCannotOpenFile, filename);
exit
end;
OpenParser(p, filename, LLStreamOpen(f));
result := ParseModule(p);
CloseParser(p);
end;
// helpers for the other parsers
function getPrecedence(tok: PToken): int;
function isOperator(tok: PToken): bool;
procedure getTok(var p: TParser);
procedure parMessage(const p: TParser; const msg: TMsgKind;
const arg: string = '');
procedure skipComment(var p: TParser; node: PNode);
function newNodeP(kind: TNodeKind; const p: TParser): PNode;
function newIntNodeP(kind: TNodeKind; const intVal: BiggestInt;
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);
begin
@@ -211,6 +230,9 @@ end;
// ------------------- Expression parsing ------------------------------------
function parseExpr(var p: TParser): PNode; forward;
function parseStmt(var p: TParser): PNode; forward;
function parseTypeDesc(var p: TParser): PNode; forward;
function parseParamList(var p: TParser): PNode; forward;
@@ -364,43 +386,35 @@ begin
addSon(result, optExpr(p));
end;
function parseTypeDescK(var p: TParser): PNode; forward;
function namedTypeOrExpr(var p: TParser): PNode;
function indexExpr(var p: TParser): PNode;
// indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr]
var
a, b: PNode;
begin
case p.tok.tokType of
tkDotDot: result := dotdotExpr(p);
tkVar, tkRef, tkPtr, tkProc, tkTuple, tkType: result := parseTypeDescK(p);
else begin
a := parseExpr(p);
case p.tok.tokType of
tkEquals: begin
result := newNodeP(nkExprEqExpr, p);
addSon(result, a);
getTok(p);
//optInd(p, result);
case p.tok.tokType of
tkVar, tkRef, tkPtr, tkProc:
addSon(result, parseTypeDescK(p));
tkDotDot: addSon(result, dotdotExpr(p));
else begin
b := parseExpr(p);
if p.tok.tokType = tkDotDot then
b := dotdotExpr(p, b);
addSon(result, b);
end
end
end;
tkDotDot: result := dotdotExpr(p, a);
else result := a
end
if p.tok.tokType = tkDotDot then
result := dotdotExpr(p)
else begin
a := parseExpr(p);
case p.tok.tokType of
tkEquals: begin
result := newNodeP(nkExprEqExpr, p);
addSon(result, a);
getTok(p);
if p.tok.tokType = tkDotDot then
addSon(result, dotdotExpr(p))
else begin
b := parseExpr(p);
if p.tok.tokType = tkDotDot then b := dotdotExpr(p, b);
addSon(result, b);
end
end;
tkDotDot: result := dotdotExpr(p, a);
else result := a
end
end
end;
function namedTypeOrExprList(var p: TParser; first: PNode): PNode;
function indexExprList(var p: TParser; first: PNode): PNode;
var
a: PNode;
begin
@@ -410,7 +424,7 @@ begin
optInd(p, result);
while (p.tok.tokType <> tkBracketRi) and (p.tok.tokType <> tkEof)
and (p.tok.tokType <> tkSad) do begin
a := namedTypeOrExpr(p);
a := indexExpr(p);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
@@ -464,7 +478,7 @@ begin
getTok(p);
optInd(p, result);
a := result;
result := newNodeI(nkQualified, a.info);
result := newNodeI(nkDotExpr, a.info);
addSon(result, a);
addSon(result, parseSymbol(p));
end;
@@ -632,7 +646,6 @@ begin
result := newNodeP(nkNilLit, p);
getTok(p);
end;
tkParLe: begin // () constructor
result := exprColonEqExprList(p, nkPar, nkExprColonExpr, tkParRi,
tkColon);
@@ -698,7 +711,7 @@ begin
addSon(result, a);
getTok(p);
end;
tkBracketLe: result := namedTypeOrExprList(p, result);
tkBracketLe: result := indexExprList(p, result);
else break
end
end
@@ -712,11 +725,6 @@ var
begin
v := primary(p);
// 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;
opPred := getPrecedence(p.tok);
while (opPred > limit) do begin
@@ -724,8 +732,6 @@ begin
opNode := newIdentNodeP(op.ident, p);
// skip operator:
getTok(p);
//skipComment(p, opNode);
//skipInd(p);
optInd(p, opNode);
// read sub-expression with higher priority
@@ -745,8 +751,6 @@ begin
{@discard} lowestExprAux(p, result, -1);
end;
function parseLambda(var p: TParser): PNode; forward;
function parseIfExpr(var p: TParser): PNode;
var
branch: PNode;
@@ -755,12 +759,9 @@ begin
while true do begin
getTok(p); // skip `if`, `elif`
branch := newNodeP(nkElifExpr, p);
//optInd(p, branch);
addSon(branch, parseExpr(p));
eat(p, tkColon);
//optInd(p, branch);
addSon(branch, parseExpr(p));
//optInd(p, branch);
addSon(result, branch);
if p.tok.tokType <> tkElif then break
end;
@@ -770,16 +771,6 @@ begin
addSon(result, branch);
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;
var
a: PNode;
@@ -803,12 +794,203 @@ begin
parMessage(p, errTokenExpected, '.}');
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 ------------------------------------
function isExprStart(const p: TParser): bool;
begin
case p.tok.tokType of
tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkLambda, tkBind,
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit: result := true;
tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkProc, tkBind,
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit,
tkVar, tkRef, tkPtr, tkTuple, tkType: result := true;
else result := false;
end;
end;
@@ -1051,7 +1233,6 @@ begin
addSon(result, branch);
if p.tok.tokType <> tkElif then break
end;
//PrintTok(p.tok);
if p.tok.tokType = tkElse then begin
branch := newNodeP(nkElse, p);
eat(p, tkElse); eat(p, tkColon);
@@ -1201,175 +1382,6 @@ begin
getTok(p);
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;
var
a: PNode;
@@ -1408,26 +1420,6 @@ begin
indAndComment(p, result); // XXX: document this in the grammar!
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;
begin
result := newNodeP(nkCommentStmt, p);
@@ -1803,7 +1795,7 @@ begin
end
end;
function parseModule(var p: TParser): PNode;
function parseAll(var p: TParser): PNode;
var
a: PNode;
begin

View File

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

View File

@@ -1,28 +1,31 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit ptmplsyn;
// This module implements the parser of the Nimrod Template files.
// This module implements Nimrod's standard template filter.
{$include config.inc}
interface
uses
nsystem, llstream, nos, charsets, wordrecg, strutils,
ast, astalgo, msgs, options, pnimsyn;
nsystem, llstream, nos, charsets, wordrecg, idents, strutils,
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
TParseState = (psDirective, psMultiDir, psTempl);
TParseState = (psDirective, psTempl);
TTmplParser = record
inp: PLLStream;
state: TParseState;
@@ -30,19 +33,11 @@ type
indent, par: int;
x: string; // the current input line
outp: PLLStream; // the ouput will be parsed by pnimsyn
subsChar: Char;
subsChar, NimDirective: Char;
emit, conc, toStr: string;
end;
function ParseTmpl(var p: TTmplParser): PNode;
procedure openTmplParser(var p: TTmplParser; const filename: string;
inputStream: PLLStream);
procedure closeTmplParser(var p: TTmplParser);
implementation
const
NimDirective = '#';
PatternChars = ['a'..'z', 'A'..'Z', '0'..'9', #128..#255, '.', '_'];
procedure newLine(var p: TTmplParser);
@@ -59,73 +54,45 @@ var
begin
j := strStart;
while p.x[j] = ' ' do inc(j);
if p.state = psMultiDir then begin
newLine(p);
if p.x[j] = '*' 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
if (p.x[strStart] = p.NimDirective) and (p.x[strStart+1] = '!') then
newLine(p)
else if (p.x[j] = p.NimDirective) then begin
newLine(p);
inc(j);
while p.x[j] = ' ' do inc(j);
d := j;
if p.x[j] = '*' then begin
keyw := '';
while p.x[j] in PatternChars do begin
addChar(keyw, p.x[j]);
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 := '';
while p.x[j] in PatternChars do begin
addChar(keyw, p.x[j]);
inc(j);
end;
case whichKeyword(keyw) of
wEnd: begin
if p.indent >= 2 then
dec(p.indent, 2)
else begin
p.info.col := int16(j);
liMessage(p.info, errXNotAllowedHere, 'end');
end;
LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, '#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,
wConverter, wMacro, wTemplate: begin
LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, ncopy(p.x, d));
inc(p.indent, 2);
end;
wElif, wOf, wElse, wExcept, wFinally: begin
LLStreamWrite(p.outp, repeatChar(p.indent-2));
LLStreamWrite(p.outp, ncopy(p.x, d));
end
end;
case whichKeyword(keyw) of
wEnd: begin
if p.indent >= 2 then
dec(p.indent, 2)
else begin
LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, ncopy(p.x, d));
end
p.info.col := int16(j);
liMessage(p.info, errXNotAllowedHere, 'end');
end;
LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, '#end');
end;
p.state := psDirective;
end
wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
wConverter, wMacro, wTemplate, wMethod: begin
LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, ncopy(p.x, d));
inc(p.indent, 2);
end;
wElif, wOf, wElse, wExcept, wFinally: begin
LLStreamWrite(p.outp, repeatChar(p.indent-2));
LLStreamWrite(p.outp, ncopy(p.x, d));
end
else begin
LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, ncopy(p.x, d));
end
end;
p.state := psDirective
end
else begin
// data line
@@ -133,17 +100,18 @@ begin
case p.state of
psTempl: begin
// 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, '"'+'');
end;
psDirective: begin
newLine(p);
LLStreamWrite(p.outp, repeatChar(p.indent));
LLStreamWrite(p.outp, 'add(result, "');
LLStreamWrite(p.outp, p.emit);
LLStreamWrite(p.outp, '("');
inc(p.par);
end;
else InternalError(p.info, 'parser in invalid state');
end
end;
p.state := psTempl;
while true do begin
@@ -162,7 +130,10 @@ begin
case p.x[j] of
'{': begin
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);
curly := 0;
while true do begin
@@ -171,13 +142,13 @@ begin
'{': begin
inc(j);
inc(curly);
LLStreamWrite(p.outp, '{'+'');
LLStreamWrite(p.outp, '{');
end;
'}': begin
inc(j);
if curly = 0 then break;
if curly > 0 then dec(curly);
LLStreamWrite(p.outp, '}'+'');
LLStreamWrite(p.outp, '}');
end;
else begin
LLStreamWrite(p.outp, p.x[j]);
@@ -185,15 +156,22 @@ begin
end
end
end;
LLStreamWrite(p.outp, ') & "')
LLStreamWrite(p.outp, ')');
LLStreamWrite(p.outp, p.conc);
LLStreamWrite(p.outp, '"');
end;
'A'..'Z', 'a'..'z', '_': begin
LLStreamWrite(p.outp, '" & $');
'a'..'z', 'A'..'Z', #128..#255: begin
LLStreamWrite(p.outp, '"');
LLStreamWrite(p.outp, p.conc);
LLStreamWrite(p.outp, p.toStr);
LLStreamWrite(p.outp, '(');
while p.x[j] in PatternChars do begin
LLStreamWrite(p.outp, p.x[j]);
inc(j)
end;
LLStreamWrite(p.outp, ' & "')
LLStreamWrite(p.outp, ')');
LLStreamWrite(p.outp, p.conc);
LLStreamWrite(p.outp, '"')
end;
else if p.x[j] = p.subsChar then begin
LLStreamWrite(p.outp, p.subsChar);
@@ -203,7 +181,7 @@ begin
p.info.col := int16(j);
liMessage(p.info, errInvalidExpression, '$'+'');
end
end;
end
end
else begin
LLStreamWrite(p.outp, p.x[j]);
@@ -215,56 +193,30 @@ begin
end
end;
function ParseTmpl(var p: TTmplParser): PNode;
function filterTmpl(input: PLLStream; const filename: string;
call: PNode): PLLStream;
var
q: TParser;
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);
p: TTmplParser;
begin
{@ignore}
FillChar(p, sizeof(p), 0);
{@emit}
p.info := newLineInfo(filename, 0, 0);
p.outp := LLStreamOpen('');
p.inp := inputStream;
p.subsChar := '$';
end;
procedure CloseTmplParser(var p: TTmplParser);
begin
p.inp := input;
p.subsChar := charArg(call, 'subschar', 1, '$');
p.nimDirective := charArg(call, 'metachar', 2, '#');
p.emit := strArg(call, 'emit', 3, 'result.add');
p.conc := strArg(call, 'conc', 4, ' & ');
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);
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.

View File

@@ -498,7 +498,7 @@ begin
nkPar, nkCurly, nkBracket: result := lcomma(n)+2;
nkSymChoice: result := lsons(n) + length('()') + sonsLen(n)-1;
nkTupleTy: result := lcomma(n)+length('tuple[]');
nkQualified, nkDotExpr: result := lsons(n)+1;
nkDotExpr: result := lsons(n)+1;
nkBind: result := lsons(n)+length('bind_');
nkCheckedFieldExpr: result := lsub(n.sons[0]);
nkLambda: result := lsons(n)+length('lambda__=_');
@@ -1047,7 +1047,7 @@ begin
gcomma(g, n, c);
put(g, tkBracketRi, ']'+'');
end;
nkQualified, nkDotExpr: begin
nkDotExpr: begin
gsub(g, n.sons[0]);
put(g, tkDot, '.'+'');
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
// be used twice! However, compilation speed does not suffer much from
// this, since results are cached.
res := checkDep(JoinPath(options.libpath,
appendFileExt('system', nimExt)));
res := checkDep(JoinPath(options.libpath, addFileExt('system', nimExt)));
if res <> rrNone then result := rrModDeps;
for i := 0 to high(r.modDeps) do begin
res := checkDep(r.modDeps[i]);

View File

@@ -314,6 +314,9 @@ begin
len := length(tokens);
L.buf := PChar(buffer);
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;
if skipPounds then begin
if L.buf[L.bufpos] = '#' then inc(L.bufpos);

View File

@@ -143,19 +143,16 @@ type
// or float literals
literal: string; // the parsed (string) literal; and
// 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;
PLexer = ^TLexer;
TLexer = object(TBaseLexer)
// lexers can be put into a stack through the next pointer;
// this feature is currently unused, however
filename: string;
next: PLexer;
indentStack: array of int; // the indentation stack
dedent: int; // counter for DED token generation
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
end;
@@ -920,7 +917,6 @@ begin
// got an documentation comment or tkIndent, return that:
if tok.toktype <> tkInvalid then exit;
// to the parser
c := L.buf[L.bufpos];
if c in SymStartChars - ['r', 'R', 'l'] then // common case first
getSymbol(L, tok)

View File

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

View File

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

View File

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

View File

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

View File

@@ -50,7 +50,7 @@ function ToString(b: PChar): string; overload;
function IntToStr(i: BiggestInt; minChars: int): string;
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);
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;
type
TStringSeq = array of string;
TCharSet = set of Char;
function split(const s: string; const seps: TCharSet): TStringSeq;
function startsWith(const s, prefix: string): bool;
@@ -278,7 +275,7 @@ begin
end
end;
function replaceStr(const s, search, by: string): string;
function replace(const s, search, by: string): string;
var
i, j: int;
begin

View File

@@ -1,64 +1,234 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit syntaxes;
// Defines the common interface of all parsers & renderers.
// All parsers and renderers need to register here!
// This file is currently unused.
// Implements the dispatcher for the different parsers.
{$include 'config.inc'}
interface
uses
nsystem, strutils, ast, scanner, pnimsyn, rnimsyn, options, msgs,
nos, lists, condsyms, paslex, pasparse, rodgen, ropes, trees;
// 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?
nsystem, strutils, llstream, ast, astalgo, idents, scanner, options, msgs,
pnimsyn, ptmplsyn, filters, rnimsyn;
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
type
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;
function ParseFile(const filename: string): PNode;
var
syntaxes: array [TSyntaxes] of TSyntax;
procedure addSyntax(const s: TSyntax);
var
len: int;
p: TParsers;
f: TBinaryFile;
begin
len := length(syntaxes);
setLength(syntaxes, len+1);
syntaxes[len] := s;
if not OpenFile(f, filename) then begin
rawMessage(errCannotOpenFile, filename);
exit
end;
OpenParsers(p, filename, LLStreamOpen(f));
result := ParseAll(p);
CloseParsers(p);
end;
function parseAll(var p: TParsers): PNode;
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;
initialization
syntaxes[synStandard].name = 'Standard';
end.

View File

@@ -652,7 +652,7 @@ end;
f: proc (x: int, c: PClosure): int
proc map(f: PClosure, a: seq[int]): seq[int] =
result = []
result = @[]
for elem in a:
add result, f.f(a, f)
@@ -737,6 +737,7 @@ end;
function transformCase(c: PTransf; n: PNode): PNode;
// removes `elif` branches of a case stmt
// adds ``else: nil`` if needed for the code generator
var
len, i, j: int;
ifs, elsen: PNode;
@@ -754,6 +755,14 @@ begin
setLength(n.sons, i+2);
addSon(elsen, ifs);
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;
result := n;
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,
wObjChecks, wImportc, wExportc, wAlign, wNodecl, wPure,
wVolatile, wRegister, wSideeffect, wHeader, wNosideeffect, wNoreturn,
wMerge, wLib, wDynlib, wCompilerproc, wCppmethod, wFatal,
wMerge, wLib, wDynlib, wCompilerproc, wProcVar, wFatal,
wError, wWarning, wHint, wLine, wPush, wPop,
wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wPragma,
wDefine, wUndef, wLinedir, wStacktrace, wLinetrace, wParallelBuild,
wLink, wCompile, wLinksys, wDeprecated, wVarargs,
wByref, wCallconv, wBreakpoint, wDebugger, wNimcall, wStdcall,
wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure,
@@ -136,9 +136,9 @@ const
'magic', 'typecheck', 'final', 'profiler',
'objchecks', 'importc', 'exportc', 'align', 'nodecl', 'pure',
'volatile', 'register', 'sideeffect', 'header', 'nosideeffect', 'noreturn',
'merge', 'lib', 'dynlib', 'compilerproc', 'cppmethod', 'fatal',
'merge', 'lib', 'dynlib', 'compilerproc', 'procvar', 'fatal',
'error', 'warning', 'hint', 'line', 'push', 'pop',
'define', 'undef', 'linedir', 'stacktrace', 'linetrace', 'pragma',
'define', 'undef', 'linedir', 'stacktrace', 'linetrace', 'parallelbuild',
'link', 'compile', 'linksys', 'deprecated', 'varargs',
'byref', 'callconv', 'breakpoint', 'debugger', 'nimcall', 'stdcall',
'cdecl', 'safecall', 'syscall', 'inline', 'noinline', 'fastcall', 'closure',

View File

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

View File

@@ -1,3 +1,4 @@
#! stdtmpl
#proc sunsetTemplate*(current, ticker, content: string,
# tabs: openarray[array[0..1, string]]): string =
# 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
var
x: string
x: string = "yyy"
y: Tenum = eA
i: int
case y
of eA: write(stdout, "a\n")
of eB, eC: write(stdout, "b oder c\n")
of eA: write(stdout, "a")
of eB, eC: write(stdout, "b or c")
x = readLine(stdin)
case x
of "Andreas", "Rumpf": write(stdout, "Hallo Meister!\n")
of "aa", "bb": write(stdout, "Du bist nicht mein Meister\n")
of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
of "cc", "hash", "when": nil
of "will", "it", "finally", "be", "generated": nil
else: write(stdout, "das sollte nicht passieren!\N")
case i
of 0..5, 8, 9: nil
of 1..5, 8, 9: nil
of 6, 7: nil
elif x == "Ha":
nil
elif x == "Ho":
nil
elif x == "yyy":
write(stdout, x)
else:
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
a, b: string
p: int
p = findSubStr("=", example)
p = find(example, "=")
a = copy(example, 0, p-1)
b = copy(example, p+1)
writeln(stdout, a & '=' & b)

View File

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