diff options
author | rumpf_a@web.de <> | 2009-10-21 10:20:15 +0200 |
---|---|---|
committer | rumpf_a@web.de <> | 2009-10-21 10:20:15 +0200 |
commit | 053309e60aee1eda594a4817ac8ac2fb8c18fb04 (patch) | |
tree | 0f1ce8b0de0b493045eb97eeca6ebf06542de601 | |
parent | 581572b28c65bc9fe47974cfd625210a69be0f3f (diff) | |
download | Nim-053309e60aee1eda594a4817ac8ac2fb8c18fb04.tar.gz |
version 0.8.2
125 files changed, 6557 insertions, 1301 deletions
diff --git a/boot.nim b/boot.nim index b7f3f4ed9..dbcf84399 100755 --- a/boot.nim +++ b/boot.nim @@ -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): diff --git a/config/nimrod.cfg b/config/nimrod.cfg index 6f6f89bc8..c1ad11380 100755 --- a/config/nimrod.cfg +++ b/config/nimrod.cfg @@ -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" diff --git a/data/advopt.txt b/data/advopt.txt index 72e8ffb7c..975c09af8 100755 --- a/data/advopt.txt +++ b/data/advopt.txt @@ -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 diff --git a/data/ast.yml b/data/ast.yml index 805b5dc68..f27b09a18 100755 --- a/data/ast.yml +++ b/data/ast.yml @@ -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 diff --git a/data/basicopt.txt b/data/basicopt.txt index c716f7641..af3f541ad 100755 --- a/data/basicopt.txt +++ b/data/basicopt.txt @@ -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 diff --git a/data/messages.yml b/data/messages.yml index 44e05dca9..ac41b5031 100755 --- a/data/messages.yml +++ b/data/messages.yml @@ -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'}, diff --git a/doc/apis.txt b/doc/apis.txt index 02caa8c24..b926e4988 100644..100755 --- a/doc/apis.txt +++ b/doc/apis.txt @@ -66,4 +66,7 @@ function func coordinate coord rectangle rect point point +symbol sym +identifier ident +indentation indent ------------------- ------------ -------------------------------------- diff --git a/doc/effects.txt b/doc/effects.txt new file mode 100755 index 000000000..85de1ffdf --- /dev/null +++ b/doc/effects.txt @@ -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. + + diff --git a/doc/filelist.txt b/doc/filelist.txt index 0e636652a..c29b3b6aa 100755 --- a/doc/filelist.txt +++ b/doc/filelist.txt @@ -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 diff --git a/doc/filters.txt b/doc/filters.txt new file mode 100755 index 000000000..ffeb6808a --- /dev/null +++ b/doc/filters.txt @@ -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> diff --git a/doc/gramcurl.txt b/doc/gramcurl.txt new file mode 100755 index 000000000..3ac9294c8 --- /dev/null +++ b/doc/gramcurl.txt @@ -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) diff --git a/doc/grammar.txt b/doc/grammar.txt index 2fcb68236..18722116e 100755 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -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)* diff --git a/doc/lib.txt b/doc/lib.txt index c15303b99..b75dd446b 100755 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -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``. diff --git a/doc/manual.txt b/doc/manual.txt index 3c24e4b1a..9cf1f3bc2 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -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 diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index 26bf38a0b..f43994cb8 100755 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -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 diff --git a/doc/pegdocs.txt b/doc/pegdocs.txt new file mode 100755 index 000000000..943acc545 --- /dev/null +++ b/doc/pegdocs.txt @@ -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. + diff --git a/doc/theindex.txt b/doc/theindex.txt index 7eb5f84e3..922dab769 100755 --- a/doc/theindex.txt +++ b/doc/theindex.txt @@ -8,13 +8,12 @@ Index `!`:idx: * `pegs.html#114 <pegs.html#114>`_ - * `macros.html#113 <macros.html#113>`_ + * `macros.html#114 <macros.html#114>`_ `!=`:idx: `system.html#347 <system.html#347>`_ `$`:idx: - * `system.html#418 <system.html#418>`_ * `system.html#419 <system.html#419>`_ * `system.html#420 <system.html#420>`_ * `system.html#421 <system.html#421>`_ @@ -22,10 +21,11 @@ Index * `system.html#423 <system.html#423>`_ * `system.html#424 <system.html#424>`_ * `system.html#425 <system.html#425>`_ + * `system.html#426 <system.html#426>`_ * `times.html#109 <times.html#109>`_ * `times.html#110 <times.html#110>`_ * `pegs.html#148 <pegs.html#148>`_ - * `macros.html#114 <macros.html#114>`_ + * `macros.html#115 <macros.html#115>`_ `%`:idx: * `strutils.html#109 <strutils.html#109>`_ @@ -44,7 +44,6 @@ Index * `system.html#362 <system.html#362>`_ * `system.html#363 <system.html#363>`_ * `system.html#364 <system.html#364>`_ - * `system.html#461 <system.html#461>`_ * `system.html#462 <system.html#462>`_ * `system.html#463 <system.html#463>`_ * `system.html#464 <system.html#464>`_ @@ -182,15 +181,15 @@ Index `times.html#115 <times.html#115>`_ `<=%`:idx: - `unicode.html#103 <unicode.html#103>`_ - - `<=%`:idx: * `system.html#297 <system.html#297>`_ * `system.html#298 <system.html#298>`_ * `system.html#299 <system.html#299>`_ * `system.html#300 <system.html#300>`_ * `system.html#301 <system.html#301>`_ + `<=%`:idx: + `unicode.html#103 <unicode.html#103>`_ + `==`:idx: * `md5.html#107 <md5.html#107>`_ * `system.html#252 <system.html#252>`_ @@ -211,25 +210,25 @@ Index * `system.html#465 <system.html#465>`_ * `complex.html#102 <complex.html#102>`_ * `unicode.html#105 <unicode.html#105>`_ - * `macros.html#115 <macros.html#115>`_ + * `macros.html#116 <macros.html#116>`_ `=~`:idx: - `regexprs.html#111 <regexprs.html#111>`_ + `pegs.html#138 <pegs.html#138>`_ `=~`:idx: - `pegs.html#138 <pegs.html#138>`_ + `regexprs.html#108 <regexprs.html#108>`_ `>`:idx: `system.html#349 <system.html#349>`_ `>%`:idx: - `system.html#417 <system.html#417>`_ + `system.html#418 <system.html#418>`_ `>=`:idx: `system.html#348 <system.html#348>`_ `>=%`:idx: - `system.html#416 <system.html#416>`_ + `system.html#417 <system.html#417>`_ `?`:idx: `pegs.html#110 <pegs.html#110>`_ @@ -241,13 +240,13 @@ Index `strtabs.html#107 <strtabs.html#107>`_ `[]`:idx: - `macros.html#111 <macros.html#111>`_ + `macros.html#112 <macros.html#112>`_ `[]=`:idx: - `strtabs.html#106 <strtabs.html#106>`_ + `macros.html#113 <macros.html#113>`_ `[]=`:idx: - `macros.html#112 <macros.html#112>`_ + `strtabs.html#106 <strtabs.html#106>`_ `[ESC]`:idx: `manual.html#134 <manual.html#134>`_ @@ -280,8 +279,8 @@ Index * `system.html#368 <system.html#368>`_ * `system.html#369 <system.html#369>`_ * `parsesql.html#108 <parsesql.html#108>`_ - * `macros.html#117 <macros.html#117>`_ * `macros.html#118 <macros.html#118>`_ + * `macros.html#119 <macros.html#119>`_ `addf`:idx: `strutils.html#111 <strutils.html#111>`_ @@ -301,13 +300,13 @@ Index `xmlgen.html#109 <xmlgen.html#109>`_ `addSep`:idx: - `strutils.html#152 <strutils.html#152>`_ + `strutils.html#149 <strutils.html#149>`_ `alert`:idx: `manual.html#131 <manual.html#131>`_ `allCharsInSet`:idx: - `strutils.html#153 <strutils.html#153>`_ + `strutils.html#150 <strutils.html#150>`_ `alloc`:idx: `system.html#410 <system.html#410>`_ @@ -382,7 +381,7 @@ Index `manual.html#199 <manual.html#199>`_ `assert`:idx: - `system.html#414 <system.html#414>`_ + `system.html#415 <system.html#415>`_ `AST`:idx: `macros.html#101 <macros.html#101>`_ @@ -404,8 +403,7 @@ Index `xmlgen.html#111 <xmlgen.html#111>`_ `backslash`:idx: - * `manual.html#127 <manual.html#127>`_ - * `regexprs.html#101 <regexprs.html#101>`_ + `manual.html#127 <manual.html#127>`_ `backspace`:idx: `manual.html#132 <manual.html#132>`_ @@ -657,10 +655,10 @@ Index * `system.html#356 <system.html#356>`_ `cmpIgnoreCase`:idx: - `strutils.html#138 <strutils.html#138>`_ + `strutils.html#135 <strutils.html#135>`_ `cmpIgnoreStyle`:idx: - `strutils.html#139 <strutils.html#139>`_ + `strutils.html#136 <strutils.html#136>`_ `cmpPaths`:idx: `os.html#139 <os.html#139>`_ @@ -701,7 +699,7 @@ Index `system.html#391 <system.html#391>`_ `compileTime`:idx: - `manual.html#227 <manual.html#227>`_ + `manual.html#229 <manual.html#229>`_ `complex statements`:idx: `manual.html#178 <manual.html#178>`_ @@ -718,9 +716,9 @@ Index `contains`:idx: * `system.html#350 <system.html#350>`_ - * `strutils.html#140 <strutils.html#140>`_ - * `strutils.html#141 <strutils.html#141>`_ - * `strutils.html#142 <strutils.html#142>`_ + * `strutils.html#137 <strutils.html#137>`_ + * `strutils.html#138 <strutils.html#138>`_ + * `strutils.html#139 <strutils.html#139>`_ * `pegs.html#139 <pegs.html#139>`_ * `pegs.html#140 <pegs.html#140>`_ @@ -738,10 +736,10 @@ Index `system.html#407 <system.html#407>`_ `copyNimNode`:idx: - `macros.html#134 <macros.html#134>`_ + `macros.html#135 <macros.html#135>`_ `copyNimTree`:idx: - `macros.html#135 <macros.html#135>`_ + `macros.html#136 <macros.html#136>`_ `coreAttr`:idx: `xmlgen.html#103 <xmlgen.html#103>`_ @@ -756,16 +754,19 @@ Index `math.html#109 <math.html#109>`_ `countdown`:idx: - `system.html#435 <system.html#435>`_ + `system.html#436 <system.html#436>`_ + + `countProcessors`:idx: + `osproc.html#117 <osproc.html#117>`_ `countup`:idx: - `system.html#436 <system.html#436>`_ + `system.html#437 <system.html#437>`_ `cpuEndian`:idx: `system.html#396 <system.html#396>`_ `createDir`:idx: - * `os.html#156 <os.html#156>`_ + * `os.html#159 <os.html#159>`_ * `zipfiles.html#104 <zipfiles.html#104>`_ `create_random_string`:idx: @@ -1156,7 +1157,7 @@ Index `manual.html#179 <manual.html#179>`_ `dbgLineHook`:idx: - `system.html#431 <system.html#431>`_ + `system.html#432 <system.html#432>`_ `dd`:idx: `xmlgen.html#123 <xmlgen.html#123>`_ @@ -1184,24 +1185,24 @@ Index `del`:idx: * `xmlgen.html#124 <xmlgen.html#124>`_ - * `macros.html#119 <macros.html#119>`_ + * `macros.html#120 <macros.html#120>`_ `delete`:idx: - `strutils.html#159 <strutils.html#159>`_ + `strutils.html#156 <strutils.html#156>`_ `deleteStr`:idx: - * `strutils.html#127 <strutils.html#127>`_ - * `strutils.html#160 <strutils.html#160>`_ + * `strutils.html#124 <strutils.html#124>`_ + * `strutils.html#157 <strutils.html#157>`_ `dfn`:idx: `xmlgen.html#125 <xmlgen.html#125>`_ - `Digits`:idx: - `strutils.html#104 <strutils.html#104>`_ - `digits`:idx: `pegs.html#125 <pegs.html#125>`_ + `Digits`:idx: + `strutils.html#104 <strutils.html#104>`_ + `DirSep`:idx: `os.html#103 <os.html#103>`_ @@ -1220,7 +1221,7 @@ Index `xmlgen.html#127 <xmlgen.html#127>`_ `domain specific languages`:idx: - `manual.html#215 <manual.html#215>`_ + `manual.html#216 <manual.html#216>`_ `dt`:idx: `xmlgen.html#128 <xmlgen.html#128>`_ @@ -1265,7 +1266,7 @@ Index `system.html#151 <system.html#151>`_ `editDistance`:idx: - `strutils.html#166 <strutils.html#166>`_ + `strutils.html#163 <strutils.html#163>`_ `EDivByZero`:idx: `system.html#147 <system.html#147>`_ @@ -1292,7 +1293,7 @@ Index `pegs.html#149 <pegs.html#149>`_ `EInvalidRegEx`:idx: - `regexprs.html#104 <regexprs.html#104>`_ + `regexprs.html#101 <regexprs.html#101>`_ `EInvalidSql`:idx: `parsesql.html#103 <parsesql.html#103>`_ @@ -1320,7 +1321,7 @@ Index * `lexbase.html#101 <lexbase.html#101>`_ `endsWith`:idx: - * `strutils.html#151 <strutils.html#151>`_ + * `strutils.html#148 <strutils.html#148>`_ * `pegs.html#142 <pegs.html#142>`_ `ENoExceptionToReraise`:idx: @@ -1387,9 +1388,9 @@ Index `system.html#145 <system.html#145>`_ `error`:idx: - * `manual.html#225 <manual.html#225>`_ - * `manual.html#228 <manual.html#228>`_ - * `macros.html#136 <macros.html#136>`_ + * `manual.html#226 <manual.html#226>`_ + * `manual.html#230 <manual.html#230>`_ + * `macros.html#137 <macros.html#137>`_ `errorMsg`:idx: `parsexml.html#120 <parsexml.html#120>`_ @@ -1405,7 +1406,7 @@ Index `escape`:idx: * `manual.html#133 <manual.html#133>`_ - * `strutils.html#163 <strutils.html#163>`_ + * `strutils.html#160 <strutils.html#160>`_ `escape sequences`:idx: `manual.html#120 <manual.html#120>`_ @@ -1435,7 +1436,7 @@ Index `system.html#174 <system.html#174>`_ `exclFilePermissions`:idx: - `os.html#162 <os.html#162>`_ + `os.html#165 <os.html#165>`_ `execCmd`:idx: `osproc.html#105 <osproc.html#105>`_ @@ -1443,6 +1444,9 @@ Index `execProcess`:idx: `osproc.html#103 <osproc.html#103>`_ + `execProcesses`:idx: + `osproc.html#118 <osproc.html#118>`_ + `execShellCmd`:idx: `os.html#146 <os.html#146>`_ @@ -1474,13 +1478,13 @@ Index `os.html#132 <os.html#132>`_ `expectKind`:idx: - `macros.html#145 <macros.html#145>`_ + `macros.html#146 <macros.html#146>`_ `expectLen`:idx: - `macros.html#147 <macros.html#147>`_ + `macros.html#148 <macros.html#148>`_ `expectMinLen`:idx: - `macros.html#146 <macros.html#146>`_ + `macros.html#147 <macros.html#147>`_ `exportc`:idx: `nimrodc.html#102 <nimrodc.html#102>`_ @@ -1516,7 +1520,7 @@ Index `unicode.html#108 <unicode.html#108>`_ `fatal`:idx: - `manual.html#229 <manual.html#229>`_ + `manual.html#231 <manual.html#231>`_ `fieldset`:idx: `xmlgen.html#130 <xmlgen.html#130>`_ @@ -1625,20 +1629,13 @@ Index `find`:idx: * `system.html#466 <system.html#466>`_ - * `strutils.html#122 <strutils.html#122>`_ - * `strutils.html#123 <strutils.html#123>`_ - * `strutils.html#124 <strutils.html#124>`_ - * `regexprs.html#109 <regexprs.html#109>`_ - * `regexprs.html#110 <regexprs.html#110>`_ - * `pegs.html#136 <pegs.html#136>`_ - * `pegs.html#137 <pegs.html#137>`_ - - `findChars`:idx: - `strutils.html#121 <strutils.html#121>`_ - - `findSubStr`:idx: * `strutils.html#119 <strutils.html#119>`_ * `strutils.html#120 <strutils.html#120>`_ + * `strutils.html#121 <strutils.html#121>`_ + * `regexprs.html#106 <regexprs.html#106>`_ + * `regexprs.html#107 <regexprs.html#107>`_ + * `pegs.html#136 <pegs.html#136>`_ + * `pegs.html#137 <pegs.html#137>`_ `float`:idx: `system.html#106 <system.html#106>`_ @@ -1650,16 +1647,16 @@ Index `system.html#108 <system.html#108>`_ `floatVal`:idx: - `macros.html#122 <macros.html#122>`_ + `macros.html#123 <macros.html#123>`_ `floatVal=`:idx: - `macros.html#128 <macros.html#128>`_ + `macros.html#129 <macros.html#129>`_ `FlushFile`:idx: `system.html#503 <system.html#503>`_ `for`:idx: - * `manual.html#207 <manual.html#207>`_ + * `manual.html#208 <manual.html#208>`_ * `tut1.html#105 <tut1.html#105>`_ `form`:idx: @@ -1718,18 +1715,15 @@ Index `generalized raw string literal`:idx: `manual.html#136 <manual.html#136>`_ - `generic character types`:idx: - `regexprs.html#102 <regexprs.html#102>`_ - `Generics`:idx: - * `manual.html#211 <manual.html#211>`_ + * `manual.html#212 <manual.html#212>`_ * `tut2.html#109 <tut2.html#109>`_ `getApplicationDir`:idx: - `os.html#166 <os.html#166>`_ + `os.html#169 <os.html#169>`_ `getApplicationFilename`:idx: - `os.html#165 <os.html#165>`_ + `os.html#168 <os.html#168>`_ `getClockStr`:idx: `times.html#112 <times.html#112>`_ @@ -1742,7 +1736,7 @@ Index * `parsexml.html#117 <parsexml.html#117>`_ `getConfigDir`:idx: - `os.html#164 <os.html#164>`_ + `os.html#167 <os.html#167>`_ `getContentLength`:idx: `cgi.html#110 <cgi.html#110>`_ @@ -1757,7 +1751,7 @@ Index `os.html#120 <os.html#120>`_ `getCurrentExceptionMsg`:idx: - `system.html#427 <system.html#427>`_ + `system.html#428 <system.html#428>`_ `getCurrentLine`:idx: `lexbase.html#106 <lexbase.html#106>`_ @@ -1776,7 +1770,7 @@ Index * `parsexml.html#119 <parsexml.html#119>`_ `getFilePermissions`:idx: - `os.html#159 <os.html#159>`_ + `os.html#162 <os.html#162>`_ `getFilePos`:idx: `system.html#523 <system.html#523>`_ @@ -1785,7 +1779,7 @@ Index `system.html#515 <system.html#515>`_ `getFreeMem`:idx: - `system.html#433 <system.html#433>`_ + `system.html#434 <system.html#434>`_ `getGatewayInterface`:idx: `cgi.html#113 <cgi.html#113>`_ @@ -1794,7 +1788,7 @@ Index `times.html#107 <times.html#107>`_ `getHomeDir`:idx: - `os.html#163 <os.html#163>`_ + `os.html#166 <os.html#166>`_ `getHttpAccept`:idx: `cgi.html#114 <cgi.html#114>`_ @@ -1840,7 +1834,7 @@ Index `md5.html#106 <md5.html#106>`_ `getOccupiedMem`:idx: - `system.html#432 <system.html#432>`_ + `system.html#433 <system.html#433>`_ `getopt`:idx: `parseopt.html#108 <parseopt.html#108>`_ @@ -1855,7 +1849,7 @@ Index `cgi.html#125 <cgi.html#125>`_ `getRefcount`:idx: - `system.html#426 <system.html#426>`_ + `system.html#427 <system.html#427>`_ `getRemoteAddr`:idx: `cgi.html#126 <cgi.html#126>`_ @@ -1924,7 +1918,7 @@ Index `times.html#105 <times.html#105>`_ `getTotalMem`:idx: - `system.html#434 <system.html#434>`_ + `system.html#435 <system.html#435>`_ `get_tty_password`:idx: `mysql.html#282 <mysql.html#282>`_ @@ -1991,9 +1985,9 @@ Index `system.html#126 <system.html#126>`_ `hint`:idx: - * `manual.html#223 <manual.html#223>`_ - * `manual.html#231 <manual.html#231>`_ - * `macros.html#138 <macros.html#138>`_ + * `manual.html#224 <manual.html#224>`_ + * `manual.html#233 <manual.html#233>`_ + * `macros.html#139 <macros.html#139>`_ `hostCPU`:idx: `system.html#398 <system.html#398>`_ @@ -2040,10 +2034,10 @@ Index `ident`:idx: * `pegs.html#129 <pegs.html#129>`_ - * `macros.html#124 <macros.html#124>`_ + * `macros.html#125 <macros.html#125>`_ `ident=`:idx: - `macros.html#130 <macros.html#130>`_ + `macros.html#131 <macros.html#131>`_ `identChars`:idx: `pegs.html#127 <pegs.html#127>`_ @@ -2057,12 +2051,12 @@ Index `Identifiers`:idx: `manual.html#116 <manual.html#116>`_ - `IdentStartChars`:idx: - `strutils.html#106 <strutils.html#106>`_ - `identStartChars`:idx: `pegs.html#128 <pegs.html#128>`_ + `IdentStartChars`:idx: + `strutils.html#106 <strutils.html#106>`_ + `if`:idx: `manual.html#183 <manual.html#183>`_ @@ -2070,10 +2064,10 @@ Index `xmlgen.html#142 <xmlgen.html#142>`_ `implicit block`:idx: - `manual.html#209 <manual.html#209>`_ + `manual.html#210 <manual.html#210>`_ `import`:idx: - * `manual.html#219 <manual.html#219>`_ + * `manual.html#220 <manual.html#220>`_ * `tut1.html#128 <tut1.html#128>`_ `importc`:idx: @@ -2089,7 +2083,7 @@ Index `system.html#173 <system.html#173>`_ `inclFilePermissions`:idx: - `os.html#161 <os.html#161>`_ + `os.html#164 <os.html#164>`_ `include`:idx: `tut1.html#129 <tut1.html#129>`_ @@ -2098,10 +2092,10 @@ Index `manual.html#113 <manual.html#113>`_ `inf`:idx: - `system.html#428 <system.html#428>`_ + `system.html#429 <system.html#429>`_ `information hiding`:idx: - * `manual.html#217 <manual.html#217>`_ + * `manual.html#218 <manual.html#218>`_ * `tut1.html#126 <tut1.html#126>`_ `init`:idx: @@ -2141,13 +2135,13 @@ Index `mysql.html#306 <mysql.html#306>`_ `intToStr`:idx: - `strutils.html#144 <strutils.html#144>`_ + `strutils.html#141 <strutils.html#141>`_ `intVal`:idx: - `macros.html#121 <macros.html#121>`_ + `macros.html#122 <macros.html#122>`_ `intVal=`:idx: - `macros.html#127 <macros.html#127>`_ + `macros.html#128 <macros.html#128>`_ `is`:idx: `system.html#353 <system.html#353>`_ @@ -2165,12 +2159,12 @@ Index `system.html#389 <system.html#389>`_ `isNil`:idx: - * `system.html#455 <system.html#455>`_ * `system.html#456 <system.html#456>`_ * `system.html#457 <system.html#457>`_ * `system.html#458 <system.html#458>`_ * `system.html#459 <system.html#459>`_ * `system.html#460 <system.html#460>`_ + * `system.html#461 <system.html#461>`_ `is_not`:idx: `system.html#354 <system.html#354>`_ @@ -2203,22 +2197,22 @@ Index `mysql.html#255 <mysql.html#255>`_ `items`:idx: - * `system.html#449 <system.html#449>`_ * `system.html#450 <system.html#450>`_ * `system.html#451 <system.html#451>`_ * `system.html#452 <system.html#452>`_ * `system.html#453 <system.html#453>`_ * `system.html#454 <system.html#454>`_ + * `system.html#455 <system.html#455>`_ `iterator`:idx: - `manual.html#208 <manual.html#208>`_ + `manual.html#209 <manual.html#209>`_ `iterOverEnvironment`:idx: `os.html#150 <os.html#150>`_ `join`:idx: - * `strutils.html#155 <strutils.html#155>`_ - * `strutils.html#156 <strutils.html#156>`_ + * `strutils.html#152 <strutils.html#152>`_ + * `strutils.html#153 <strutils.html#153>`_ `JoinPath`:idx: * `os.html#122 <os.html#122>`_ @@ -2232,7 +2226,7 @@ Index `kind`:idx: * `parsexml.html#110 <parsexml.html#110>`_ - * `macros.html#120 <macros.html#120>`_ + * `macros.html#121 <macros.html#121>`_ `l-values`:idx: `manual.html#107 <manual.html#107>`_ @@ -2251,7 +2245,7 @@ Index * `system.html#172 <system.html#172>`_ * `strtabs.html#109 <strtabs.html#109>`_ * `parsesql.html#107 <parsesql.html#107>`_ - * `macros.html#116 <macros.html#116>`_ + * `macros.html#117 <macros.html#117>`_ `Letters`:idx: `strutils.html#103 <strutils.html#103>`_ @@ -2326,7 +2320,7 @@ Index `system.html#127 <system.html#127>`_ `Macros`:idx: - `manual.html#214 <manual.html#214>`_ + `manual.html#215 <manual.html#215>`_ `make_password_from_salt`:idx: `mysql.html#281 <mysql.html#281>`_ @@ -2359,24 +2353,24 @@ Index `xmlgen.html#150 <xmlgen.html#150>`_ `match`:idx: - * `regexprs.html#106 <regexprs.html#106>`_ - * `regexprs.html#107 <regexprs.html#107>`_ + * `regexprs.html#103 <regexprs.html#103>`_ + * `regexprs.html#104 <regexprs.html#104>`_ * `pegs.html#132 <pegs.html#132>`_ * `pegs.html#133 <pegs.html#133>`_ `matchLen`:idx: - * `regexprs.html#108 <regexprs.html#108>`_ + * `regexprs.html#105 <regexprs.html#105>`_ * `pegs.html#134 <pegs.html#134>`_ * `pegs.html#135 <pegs.html#135>`_ `max`:idx: * `system.html#318 <system.html#318>`_ - * `system.html#443 <system.html#443>`_ * `system.html#444 <system.html#444>`_ * `system.html#445 <system.html#445>`_ * `system.html#446 <system.html#446>`_ * `system.html#447 <system.html#447>`_ * `system.html#448 <system.html#448>`_ + * `system.html#449 <system.html#449>`_ `MAX_BIGINT_WIDTH`:idx: `mysql.html#194 <mysql.html#194>`_ @@ -2403,7 +2397,7 @@ Index `mysql.html#191 <mysql.html#191>`_ `MaxSubpatterns`:idx: - * `regexprs.html#105 <regexprs.html#105>`_ + * `regexprs.html#102 <regexprs.html#102>`_ * `pegs.html#131 <pegs.html#131>`_ `MAX_TINYINT_WIDTH`:idx: @@ -2441,12 +2435,12 @@ Index `min`:idx: * `system.html#317 <system.html#317>`_ - * `system.html#437 <system.html#437>`_ * `system.html#438 <system.html#438>`_ * `system.html#439 <system.html#439>`_ * `system.html#440 <system.html#440>`_ * `system.html#441 <system.html#441>`_ * `system.html#442 <system.html#442>`_ + * `system.html#443 <system.html#443>`_ `mod`:idx: * `system.html#222 <system.html#222>`_ @@ -2459,7 +2453,7 @@ Index `mysql.html#284 <mysql.html#284>`_ `module`:idx: - * `manual.html#216 <manual.html#216>`_ + * `manual.html#217 <manual.html#217>`_ * `tut1.html#125 <tut1.html#125>`_ `moveFile`:idx: @@ -2469,7 +2463,8 @@ Index `system.html#408 <system.html#408>`_ `multi-methods`:idx: - `tut2.html#104 <tut2.html#104>`_ + * `manual.html#207 <manual.html#207>`_ + * `tut2.html#104 <tut2.html#104>`_ `MULTIPLE_KEY_FLAG`:idx: `mysql.html#127 <mysql.html#127>`_ @@ -2989,7 +2984,7 @@ Index `mysql.html#110 <mysql.html#110>`_ `nan`:idx: - `system.html#430 <system.html#430>`_ + `system.html#431 <system.html#431>`_ `Natural`:idx: `system.html#134 <system.html#134>`_ @@ -2998,10 +2993,10 @@ Index `pegs.html#130 <pegs.html#130>`_ `neginf`:idx: - `system.html#429 <system.html#429>`_ + `system.html#430 <system.html#430>`_ `nestList`:idx: - `macros.html#150 <macros.html#150>`_ + `macros.html#151 <macros.html#151>`_ `NET`:idx: `mysql.html#199 <mysql.html#199>`_ @@ -3047,35 +3042,35 @@ Index * `system.html#125 <system.html#125>`_ `newCall`:idx: - * `macros.html#148 <macros.html#148>`_ * `macros.html#149 <macros.html#149>`_ + * `macros.html#150 <macros.html#150>`_ `newFileStream`:idx: * `streams.html#120 <streams.html#120>`_ * `streams.html#121 <streams.html#121>`_ `newFloatLitNode`:idx: - `macros.html#141 <macros.html#141>`_ + `macros.html#142 <macros.html#142>`_ `newIdentNode`:idx: - * `macros.html#142 <macros.html#142>`_ * `macros.html#143 <macros.html#143>`_ + * `macros.html#144 <macros.html#144>`_ `newIntLitNode`:idx: - `macros.html#140 <macros.html#140>`_ - - `newLine`:idx: - `pegs.html#120 <pegs.html#120>`_ + `macros.html#141 <macros.html#141>`_ `newline`:idx: * `manual.html#121 <manual.html#121>`_ * `pegs.html#119 <pegs.html#119>`_ + `newLine`:idx: + `pegs.html#120 <pegs.html#120>`_ + `NewLines`:idx: `lexbase.html#102 <lexbase.html#102>`_ `newNimNode`:idx: - `macros.html#133 <macros.html#133>`_ + `macros.html#134 <macros.html#134>`_ `newNonTerminal`:idx: `pegs.html#123 <pegs.html#123>`_ @@ -3094,7 +3089,7 @@ Index * `strtabs.html#105 <strtabs.html#105>`_ `newStrLitNode`:idx: - `macros.html#139 <macros.html#139>`_ + `macros.html#140 <macros.html#140>`_ `next`:idx: * `parseopt.html#105 <parseopt.html#105>`_ @@ -3138,7 +3133,7 @@ Index `xmlgen.html#152 <xmlgen.html#152>`_ `noSideEffect`:idx: - `manual.html#226 <manual.html#226>`_ + `manual.html#227 <manual.html#227>`_ `not`:idx: * `system.html#120 <system.html#120>`_ @@ -3261,16 +3256,16 @@ Index `os.html#127 <os.html#127>`_ `ParseBiggestInt`:idx: - `strutils.html#146 <strutils.html#146>`_ + `strutils.html#143 <strutils.html#143>`_ `parseCmdLine`:idx: - `os.html#157 <os.html#157>`_ + `os.html#160 <os.html#160>`_ `ParseFloat`:idx: - `strutils.html#147 <strutils.html#147>`_ + `strutils.html#144 <strutils.html#144>`_ `ParseInt`:idx: - `strutils.html#145 <strutils.html#145>`_ + `strutils.html#142 <strutils.html#142>`_ `parsePeg`:idx: `pegs.html#150 <pegs.html#150>`_ @@ -3284,12 +3279,18 @@ Index `PathSep`:idx: `os.html#105 <os.html#105>`_ + `pcDirectory`:idx: + `os.html#154 <os.html#154>`_ + `Pcharacter_set`:idx: `mysql.html#350 <mysql.html#350>`_ `Pcharset_info_st`:idx: `mysql.html#349 <mysql.html#349>`_ + `pcLinkToDirectory`:idx: + `os.html#155 <os.html#155>`_ + `PCURL`:idx: `libcurl.html#139 <libcurl.html#139>`_ @@ -3495,13 +3496,13 @@ Index `mysql.html#200 <mysql.html#200>`_ `PNimrodNode`:idx: - `macros.html#110 <macros.html#110>`_ + `macros.html#111 <macros.html#111>`_ `PNimrodSymbol`:idx: - `macros.html#109 <macros.html#109>`_ + `macros.html#110 <macros.html#110>`_ `PNimrodType`:idx: - `macros.html#108 <macros.html#108>`_ + `macros.html#109 <macros.html#109>`_ `PNonTerminal`:idx: `pegs.html#101 <pegs.html#101>`_ @@ -3571,6 +3572,12 @@ Index `processID`:idx: `osproc.html#112 <osproc.html#112>`_ + `procvar`:idx: + `manual.html#228 <manual.html#228>`_ + + `programming by contracts`:idx: + `system.html#414 <system.html#414>`_ + `Psockaddr`:idx: `mysql.html#250 <mysql.html#250>`_ @@ -3662,7 +3669,7 @@ Index `math.html#134 <math.html#134>`_ `push/pop`:idx: - `manual.html#232 <manual.html#232>`_ + `manual.html#234 <manual.html#234>`_ `putEnv`:idx: `os.html#149 <os.html#149>`_ @@ -3690,7 +3697,7 @@ Index `manual.html#128 <manual.html#128>`_ `quoteIfContainsWhite`:idx: - `strutils.html#154 <strutils.html#154>`_ + `strutils.html#151 <strutils.html#151>`_ `random`:idx: `math.html#113 <math.html#113>`_ @@ -3764,16 +3771,16 @@ Index `system.html#412 <system.html#412>`_ `reBinary`:idx: - `regexprs.html#116 <regexprs.html#116>`_ + `regexprs.html#113 <regexprs.html#113>`_ `Recursive module dependancies`:idx: - `manual.html#220 <manual.html#220>`_ + `manual.html#221 <manual.html#221>`_ `reEmail`:idx: - `regexprs.html#119 <regexprs.html#119>`_ + `regexprs.html#116 <regexprs.html#116>`_ `reFloat`:idx: - `regexprs.html#118 <regexprs.html#118>`_ + `regexprs.html#115 <regexprs.html#115>`_ `REFRESH_DES_KEY_FILE`:idx: `mysql.html#154 <mysql.html#154>`_ @@ -3821,40 +3828,40 @@ Index `nimrodc.html#112 <nimrodc.html#112>`_ `reHex`:idx: - `regexprs.html#115 <regexprs.html#115>`_ + `regexprs.html#112 <regexprs.html#112>`_ `reIdentifier`:idx: - `regexprs.html#112 <regexprs.html#112>`_ + `regexprs.html#109 <regexprs.html#109>`_ `reInteger`:idx: - `regexprs.html#114 <regexprs.html#114>`_ + `regexprs.html#111 <regexprs.html#111>`_ `removeDir`:idx: - `os.html#155 <os.html#155>`_ + `os.html#158 <os.html#158>`_ `removeFile`:idx: `os.html#144 <os.html#144>`_ `reNatural`:idx: - `regexprs.html#113 <regexprs.html#113>`_ + `regexprs.html#110 <regexprs.html#110>`_ `renderSQL`:idx: `parsesql.html#110 <parsesql.html#110>`_ `reOctal`:idx: - `regexprs.html#117 <regexprs.html#117>`_ + `regexprs.html#114 <regexprs.html#114>`_ `repeatChar`:idx: - `strutils.html#149 <strutils.html#149>`_ + `strutils.html#146 <strutils.html#146>`_ `replace`:idx: - * `strutils.html#157 <strutils.html#157>`_ - * `strutils.html#158 <strutils.html#158>`_ + * `strutils.html#154 <strutils.html#154>`_ + * `strutils.html#155 <strutils.html#155>`_ * `pegs.html#143 <pegs.html#143>`_ `replaceStr`:idx: - * `strutils.html#125 <strutils.html#125>`_ - * `strutils.html#126 <strutils.html#126>`_ + * `strutils.html#122 <strutils.html#122>`_ + * `strutils.html#123 <strutils.html#123>`_ `repr`:idx: `system.html#370 <system.html#370>`_ @@ -3873,7 +3880,7 @@ Index `manual.html#192 <manual.html#192>`_ `reURL`:idx: - `regexprs.html#120 <regexprs.html#120>`_ + `regexprs.html#117 <regexprs.html#117>`_ `round`:idx: `math.html#121 <math.html#121>`_ @@ -3910,7 +3917,7 @@ Index `scope`:idx: * `manual.html#106 <manual.html#106>`_ - * `manual.html#221 <manual.html#221>`_ + * `manual.html#222 <manual.html#222>`_ `scramble`:idx: `mysql.html#278 <mysql.html#278>`_ @@ -3940,7 +3947,7 @@ Index `xmlgen.html#163 <xmlgen.html#163>`_ `separate compilation`:idx: - * `manual.html#218 <manual.html#218>`_ + * `manual.html#219 <manual.html#219>`_ * `tut1.html#127 <tut1.html#127>`_ `seq`:idx: @@ -4009,7 +4016,7 @@ Index `terminal.html#103 <terminal.html#103>`_ `setFilePermissions`:idx: - `os.html#160 <os.html#160>`_ + `os.html#163 <os.html#163>`_ `setFilePos`:idx: `system.html#522 <system.html#522>`_ @@ -4041,9 +4048,6 @@ Index * `system.html#230 <system.html#230>`_ * `system.html#231 <system.html#231>`_ - `simple assertions`:idx: - `regexprs.html#103 <regexprs.html#103>`_ - `simple statements`:idx: `manual.html#177 <manual.html#177>`_ @@ -4053,6 +4057,9 @@ Index `sizeof`:idx: `system.html#162 <system.html#162>`_ + `sleep`:idx: + `os.html#170 <os.html#170>`_ + `small`:idx: `xmlgen.html#164 <xmlgen.html#164>`_ @@ -4063,10 +4070,10 @@ Index `xmlgen.html#165 <xmlgen.html#165>`_ `split`:idx: - * `strutils.html#129 <strutils.html#129>`_ - * `strutils.html#130 <strutils.html#130>`_ - * `strutils.html#136 <strutils.html#136>`_ - * `strutils.html#137 <strutils.html#137>`_ + * `strutils.html#126 <strutils.html#126>`_ + * `strutils.html#127 <strutils.html#127>`_ + * `strutils.html#133 <strutils.html#133>`_ + * `strutils.html#134 <strutils.html#134>`_ * `pegs.html#146 <pegs.html#146>`_ * `pegs.html#147 <pegs.html#147>`_ @@ -4077,19 +4084,19 @@ Index `os.html#133 <os.html#133>`_ `splitLines`:idx: - * `strutils.html#131 <strutils.html#131>`_ - * `strutils.html#135 <strutils.html#135>`_ + * `strutils.html#128 <strutils.html#128>`_ + * `strutils.html#132 <strutils.html#132>`_ `splitLinesSeq`:idx: - `strutils.html#132 <strutils.html#132>`_ + `strutils.html#129 <strutils.html#129>`_ `SplitPath`:idx: * `os.html#125 <os.html#125>`_ * `os.html#126 <os.html#126>`_ `splitSeq`:idx: - * `strutils.html#133 <strutils.html#133>`_ - * `strutils.html#134 <strutils.html#134>`_ + * `strutils.html#130 <strutils.html#130>`_ + * `strutils.html#131 <strutils.html#131>`_ `sqlite3_aggregate_context`:idx: `sqlite3.html#261 <sqlite3.html#261>`_ @@ -4600,11 +4607,10 @@ Index `math.html#136 <math.html#136>`_ `startProcess`:idx: - * `osproc.html#107 <osproc.html#107>`_ - * `osproc.html#117 <osproc.html#117>`_ + `osproc.html#107 <osproc.html#107>`_ `startsWith`:idx: - * `strutils.html#150 <strutils.html#150>`_ + * `strutils.html#147 <strutils.html#147>`_ * `pegs.html#141 <pegs.html#141>`_ `statement macros`:idx: @@ -4696,10 +4702,10 @@ Index `manual.html#152 <manual.html#152>`_ `strVal`:idx: - `macros.html#126 <macros.html#126>`_ + `macros.html#127 <macros.html#127>`_ `strVal=`:idx: - `macros.html#132 <macros.html#132>`_ + `macros.html#133 <macros.html#133>`_ `st_udf_args`:idx: `mysql.html#258 <mysql.html#258>`_ @@ -4739,22 +4745,22 @@ Index `osproc.html#108 <osproc.html#108>`_ `swap`:idx: - `system.html#415 <system.html#415>`_ + `system.html#416 <system.html#416>`_ `symAddr`:idx: `dynlib.html#104 <dynlib.html#104>`_ `symbol`:idx: - `macros.html#123 <macros.html#123>`_ + `macros.html#124 <macros.html#124>`_ `symbol=`:idx: - `macros.html#129 <macros.html#129>`_ + `macros.html#130 <macros.html#130>`_ `syscall`:idx: `manual.html#172 <manual.html#172>`_ `system`:idx: - `manual.html#222 <manual.html#222>`_ + `manual.html#223 <manual.html#223>`_ `table`:idx: `xmlgen.html#170 <xmlgen.html#170>`_ @@ -4973,7 +4979,7 @@ Index `xmlgen.html#172 <xmlgen.html#172>`_ `template`:idx: - `manual.html#213 <manual.html#213>`_ + `manual.html#214 <manual.html#214>`_ `TEndian`:idx: `system.html#384 <system.html#384>`_ @@ -5004,7 +5010,7 @@ Index `system.html#490 <system.html#490>`_ `TFilePermission`:idx: - `os.html#158 <os.html#158>`_ + `os.html#161 <os.html#161>`_ `TFileStream`:idx: `streams.html#119 <streams.html#119>`_ @@ -5051,6 +5057,9 @@ Index `TNimNodeKinds`:idx: `macros.html#103 <macros.html#103>`_ + `TNimrodIdent`:idx: + `macros.html#108 <macros.html#108>`_ + `TNimrodNodeKind`:idx: `macros.html#102 <macros.html#102>`_ @@ -5073,7 +5082,7 @@ Index `system.html#402 <system.html#402>`_ `toBin`:idx: - `strutils.html#162 <strutils.html#162>`_ + `strutils.html#159 <strutils.html#159>`_ `TObject`:idx: `system.html#136 <system.html#136>`_ @@ -5082,7 +5091,7 @@ Index `system.html#399 <system.html#399>`_ `toHex`:idx: - `strutils.html#143 <strutils.html#143>`_ + `strutils.html#140 <strutils.html#140>`_ `toInt`:idx: `system.html#401 <system.html#401>`_ @@ -5093,19 +5102,19 @@ Index * `unicode.html#111 <unicode.html#111>`_ `toOct`:idx: - `strutils.html#161 <strutils.html#161>`_ + `strutils.html#158 <strutils.html#158>`_ `toOctal`:idx: - `strutils.html#128 <strutils.html#128>`_ + `strutils.html#125 <strutils.html#125>`_ `TOptParser`:idx: `parseopt.html#102 <parseopt.html#102>`_ `toString`:idx: - `strutils.html#148 <strutils.html#148>`_ + `strutils.html#145 <strutils.html#145>`_ `toStrLit`:idx: - `macros.html#144 <macros.html#144>`_ + `macros.html#145 <macros.html#145>`_ `toTitle`:idx: `unicode.html#113 <unicode.html#113>`_ @@ -5235,15 +5244,15 @@ Index `parsexml.html#106 <parsexml.html#106>`_ `typ`:idx: - `macros.html#125 <macros.html#125>`_ + `macros.html#126 <macros.html#126>`_ `typ=`:idx: - `macros.html#131 <macros.html#131>`_ + `macros.html#132 <macros.html#132>`_ `type`:idx: * `manual.html#102 <manual.html#102>`_ * `manual.html#141 <manual.html#141>`_ - * `manual.html#210 <manual.html#210>`_ + * `manual.html#211 <manual.html#211>`_ `type casts`:idx: `tut2.html#101 <tut2.html#101>`_ @@ -5252,7 +5261,7 @@ Index `tut2.html#102 <tut2.html#102>`_ `type parameters`:idx: - * `manual.html#212 <manual.html#212>`_ + * `manual.html#213 <manual.html#213>`_ * `tut2.html#110 <tut2.html#110>`_ `type suffix`:idx: @@ -5331,17 +5340,17 @@ Index `cgi.html#109 <cgi.html#109>`_ `validEmailAddress`:idx: - `strutils.html#164 <strutils.html#164>`_ + `strutils.html#161 <strutils.html#161>`_ `validIdentifier`:idx: - `strutils.html#165 <strutils.html#165>`_ - - `var`:idx: - `xmlgen.html#181 <xmlgen.html#181>`_ + `strutils.html#162 <strutils.html#162>`_ `Var`:idx: `manual.html#181 <manual.html#181>`_ + `var`:idx: + `xmlgen.html#181 <xmlgen.html#181>`_ + `varargs`:idx: `nimrodc.html#106 <nimrodc.html#106>`_ @@ -5354,7 +5363,7 @@ Index * `tut2.html#103 <tut2.html#103>`_ `verbose`:idx: - `regexprs.html#121 <regexprs.html#121>`_ + `regexprs.html#118 <regexprs.html#118>`_ `vertical tabulator`:idx: `manual.html#126 <manual.html#126>`_ @@ -5366,16 +5375,19 @@ Index `osproc.html#113 <osproc.html#113>`_ `walkDir`:idx: - `os.html#154 <os.html#154>`_ + `os.html#156 <os.html#156>`_ + + `walkDirRec`:idx: + `os.html#157 <os.html#157>`_ `walkFiles`:idx: * `os.html#152 <os.html#152>`_ * `zipfiles.html#110 <zipfiles.html#110>`_ `warning`:idx: - * `manual.html#224 <manual.html#224>`_ - * `manual.html#230 <manual.html#230>`_ - * `macros.html#137 <macros.html#137>`_ + * `manual.html#225 <manual.html#225>`_ + * `manual.html#232 <manual.html#232>`_ + * `macros.html#138 <macros.html#138>`_ `when`:idx: * `manual.html#185 <manual.html#185>`_ @@ -5384,12 +5396,12 @@ Index `while`:idx: `manual.html#197 <manual.html#197>`_ - `whitespace`:idx: - `pegs.html#126 <pegs.html#126>`_ - `Whitespace`:idx: `strutils.html#102 <strutils.html#102>`_ + `whitespace`:idx: + `pegs.html#126 <pegs.html#126>`_ + `winTimeToUnixTime`:idx: `times.html#118 <times.html#118>`_ diff --git a/doc/tut1.txt b/doc/tut1.txt index 69f218a31..c3d7b0039 100755 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -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 diff --git a/doc/tut2.txt b/doc/tut2.txt index 4515ffbd5..c81f98e77 100755 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -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 diff --git a/examples/filterex.nim b/examples/filterex.nim new file mode 100755 index 000000000..3713f4b64 --- /dev/null +++ b/examples/filterex.nim @@ -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> diff --git a/examples/iupex1.nim b/examples/iupex1.nim new file mode 100755 index 000000000..89e85e776 --- /dev/null +++ b/examples/iupex1.nim @@ -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() + diff --git a/examples/keyval2.nim b/examples/keyval2.nim new file mode 100755 index 000000000..5e7d0ea4a --- /dev/null +++ b/examples/keyval2.nim @@ -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] diff --git a/examples/transff.nim b/examples/transff.nim new file mode 100755 index 000000000..32d17e52c --- /dev/null +++ b/examples/transff.nim @@ -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")]) + diff --git a/install.txt b/install.txt index b8b2fefb5..e22a6effd 100755 --- a/install.txt +++ b/install.txt @@ -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: diff --git a/koch.py b/koch.py index 6430e4a6e..9a9f38649 100755 --- a/koch.py +++ b/koch.py @@ -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": diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim new file mode 100755 index 000000000..197250d9c --- /dev/null +++ b/lib/impure/db_postgres.nim @@ -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 diff --git a/lib/nimbase.h b/lib/nimbase.h index e62ad7096..a0f08f4f3 100755 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -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 diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 5c8103b9a..ddeaec664 100755 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -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 diff --git a/lib/pure/hashtabs.nim b/lib/pure/hashtabs.nim new file mode 100755 index 000000000..68d19d63b --- /dev/null +++ b/lib/pure/hashtabs.nim @@ -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, "}") diff --git a/lib/pure/macros.nim b/lib/pure/macros.nim index 341a7cb61..5129cde1b 100755 --- a/lib/pure/macros.nim +++ b/lib/pure/macros.nim @@ -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`. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 83cdbc0dd..a2a1830f7 100755 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -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.} diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 7d017616e..d76825531 100755 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -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 diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim new file mode 100755 index 000000000..c029f92a2 --- /dev/null +++ b/lib/pure/pegs.nim @@ -0,0 +1,1320 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2009 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Simple PEG (Parsing expression grammar) matching. Uses no memorization, but +## uses superoperators and symbol inlining to improve performance. Note: +## Matching performance is hopefully competitive with optimized regular +## expression engines. +## +## .. include:: ../doc/pegdocs.txt +## + +const + useUnicode = true ## change this to deactivate proper UTF-8 support + +import + strutils + +when useUnicode: + import unicode + +const + InlineThreshold = 5 ## number of leaves; -1 to disable inlining + +type + TPegKind = enum + pkEmpty, + pkAny, ## any character (.) + pkAnyRune, ## any Unicode character (_) + pkNewLine, ## CR-LF, LF, CR + pkTerminal, + pkTerminalIgnoreCase, + pkTerminalIgnoreStyle, + pkChar, ## single character to match + pkCharChoice, + pkNonTerminal, + pkSequence, ## a b c ... --> Internal DSL: peg(a, b, c) + pkOrderedChoice, ## a / b / ... --> Internal DSL: a / b or /[a, b, c] + pkGreedyRep, ## a* --> Internal DSL: *a + ## a+ --> Internal DSL: +a; represented as (a a*) + pkGreedyRepChar, ## x* where x is a single character (superop) + pkGreedyRepSet, ## [set]* (superop) + pkGreedyAny, ## .* or _* (superop) + pkOption, ## a? --> Internal DSL: ?a + pkAndPredicate, ## &a --> Internal DSL: &a + pkNotPredicate, ## !a --> Internal DSL: !a + pkCapture, ## {a} --> Internal DSL: capture(a) + pkRule, ## a <- b + pkList ## a, b + TNonTerminalFlag = enum + ntDeclared, ntUsed + TNonTerminal {.final.} = object ## represents a non terminal symbol + name: string ## the name of the symbol + line: int ## the line the symbol has been declared/used in + col: int ## the column the symbol has been declared/used in + flags: set[TNonTerminalFlag] ## the nonterminal's flags + rule: TNode ## the rule that the symbol refers to + TNode {.final.} = object + case kind: TPegKind + of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine: nil + of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string + of pkChar, pkGreedyRepChar: ch: char + of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] + of pkNonTerminal: nt: PNonTerminal + else: sons: seq[TNode] + PNonTerminal* = ref TNonTerminal + + TPeg* = TNode ## type that represents a PEG + +proc term*(t: string): TPeg = + ## constructs a PEG from a terminal string + if t.len != 1: + result.kind = pkTerminal + result.term = t + else: + result.kind = pkChar + result.ch = t[0] + +proc termIgnoreCase*(t: string): TPeg = + ## constructs a PEG from a terminal string; ignore case for matching + result.kind = pkTerminalIgnoreCase + result.term = t + +proc termIgnoreStyle*(t: string): TPeg = + ## constructs a PEG from a terminal string; ignore style for matching + result.kind = pkTerminalIgnoreStyle + result.term = t + +proc term*(t: char): TPeg = + ## constructs a PEG from a terminal char + assert t != '\0' + result.kind = pkChar + result.ch = t + +proc charSet*(s: set[char]): TPeg = + ## constructs a PEG from a character set `s` + assert '\0' notin s + result.kind = pkCharChoice + new(result.charChoice) + result.charChoice^ = s + +proc len(a: TPeg): int {.inline.} = return a.sons.len +proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s) + +proc addChoice(dest: var TPeg, elem: TPeg) = + var L = dest.len-1 + if L >= 0 and dest.sons[L].kind == pkCharChoice: + case elem.kind + of pkCharChoice: + dest.sons[L].charChoice^ = dest.sons[L].charChoice^ + elem.charChoice^ + of pkChar: incl(dest.sons[L].charChoice^, elem.ch) + else: add(dest, elem) + else: add(dest, elem) + +template multipleOp(k: TPegKind, localOpt: expr) = + result.kind = k + result.sons = @[] + for x in items(a): + if x.kind == k: + for y in items(x.sons): + localOpt(result, y) + else: + localOpt(result, x) + if result.len == 1: + result = result.sons[0] + +proc `/`*(a: openArray[TPeg]): TPeg = + ## constructs an ordered choice with the PEGs in `a` + multipleOp(pkOrderedChoice, addChoice) + +proc addSequence(dest: var TPeg, elem: TPeg) = + var L = dest.len-1 + if L >= 0 and dest.sons[L].kind == pkTerminal: + case elem.kind + of pkTerminal: add(dest.sons[L].term, elem.term) + of pkChar: add(dest.sons[L].term, elem.ch) + else: add(dest, elem) + else: add(dest, elem) + +proc sequence*(a: openArray[TPeg]): TPeg = + ## constructs a sequence with all the PEGs from `a` + multipleOp(pkSequence, addSequence) + +proc `?`*(a: TPeg): TPeg = + ## constructs an optional piece with the PEG `a` + if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar, + pkGreedyRepSet}: + # a* ? --> a* + # a? ? --> a? + result = a + else: + result.kind = pkOption + result.sons = @[a] + +proc `*`*(a: TPeg): TPeg = + ## constructs a "greedy repetition" piece the PEG `a` + case a.kind + of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption: + assert false + # produces endless loop! + of pkChar: + result.kind = pkGreedyRepChar + result.ch = a.ch + of pkCharChoice: + result.kind = pkGreedyRepSet + result.charChoice = a.charChoice # copying a reference suffices! + of pkAny, pkAnyRune: + result.kind = pkGreedyAny + else: + result.kind = pkGreedyRep + result.sons = @[a] + +proc `+`*(a: TPeg): TPeg = + ## constructs a "greedy positive repetition" with the PEG `a` + return sequence(a, *a) + +proc `&`*(a: TPeg): TPeg = + ## constructs an "and predicate" with the PEG `a` + result.kind = pkAndPredicate + result.sons = @[a] + +proc `!`*(a: TPeg): TPeg = + ## constructs a "not predicate" with the PEG `a` + result.kind = pkNotPredicate + result.sons = @[a] + +proc any*: TPeg {.inline.} = + ## constructs the PEG `any character`:idx: (``.``) + result.kind = pkAny + +proc anyRune*: TPeg {.inline.} = + ## constructs the PEG `any rune`:idx: (``_``) + result.kind = pkAnyRune + +proc newLine*: TPeg {.inline.} = + ## constructs the PEG `newline`:idx: (``\n``) + result.kind = pkNewline + +proc capture*(a: TPeg): TPeg = + ## constructs a capture with the PEG `a` + result.kind = pkCapture + result.sons = @[a] + +proc spaceCost(n: TPeg): int = + case n.kind + of pkEmpty: nil + of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, + pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkAny, pkAnyRune, + pkNewLine, pkGreedyAny: + result = 1 + of pkNonTerminal: + # we cannot inline a rule with a non-terminal + result = InlineThreshold+1 + else: + for i in 0..n.len-1: + inc(result, spaceCost(n.sons[i])) + if result >= InlineThreshold: break + +proc nonterminal*(n: PNonTerminal): TPeg = + ## constructs a PEG that consists of the nonterminal symbol + assert n != nil + if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold: + when false: echo "inlining symbol: ", n.name + result = n.rule # inlining of rule enables better optimizations + else: + result.kind = pkNonTerminal + result.nt = n + +proc newNonTerminal*(name: string, line, column: int): PNonTerminal = + ## constructs a nonterminal symbol + new(result) + result.name = name + result.line = line + result.col = column + +template letters*: expr = + ## expands to ``charset({'A'..'Z', 'a'..'z'})`` + charset({'A'..'Z', 'a'..'z'}) + +template digits*: expr = + ## expands to ``charset({'0'..'9'})`` + charset({'0'..'9'}) + +template whitespace*: expr = + ## expands to ``charset({' ', '\9'..'\13'})`` + charset({' ', '\9'..'\13'}) + +template identChars*: expr = + ## expands to ``charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})`` + charset({'a'..'z', 'A'..'Z', '0'..'9', '_'}) + +template identStartChars*: expr = + ## expands to ``charset({'A'..'Z', 'a'..'z', '_'})`` + charset({'a'..'z', 'A'..'Z', '_'}) + +template ident*: expr = + ## same as ``[a-zA-Z_][a-zA-z_0-9]*``; standard identifier + sequence(charset({'a'..'z', 'A'..'Z', '_'}), + *charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})) + +template natural*: expr = + ## same as ``\d+`` + +digits + +const + MaxSubpatterns* = 10 ## defines the maximum number of subpatterns that + ## can be captured. More subpatterns cannot be captured! + +# --------------------- core engine ------------------------------------------- + +type + TMatchClosure {.final.} = object + matches: array[0..maxSubpatterns-1, tuple[first, last: int]] + ml: int + +when not useUnicode: + type + TRune = char + template fastRuneAt(s, i, ch: expr) = + ch = s[i] + inc(i) + template runeLenAt(s, i: expr): expr = 1 + +proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int = + ## this implements a simple PEG interpreter. Thanks to superoperators it + ## has competitive performance nevertheless. + ## Returns -1 if it does not match, else the length of the match + case p.kind + of pkEmpty: result = 0 # match of length 0 + of pkAny: + if s[start] != '\0': result = 1 + else: result = -1 + of pkAnyRune: + if s[start] != '\0': + result = runeLenAt(s, start) + else: + result = -1 + of pkGreedyAny: + result = len(s) - start + of pkNewLine: + if s[start] == '\L': result = 1 + elif s[start] == '\C': + if s[start+1] == '\L': result = 2 + else: result = 1 + else: result = -1 + of pkTerminal: + result = len(p.term) + for i in 0..result-1: + if p.term[i] != s[start+i]: + result = -1 + break + of pkTerminalIgnoreCase: + var + i = 0 + a, b: TRune + result = start + while i < len(p.term): + fastRuneAt(p.term, i, a) + fastRuneAt(s, result, b) + if toLower(a) != toLower(b): + result = -1 + break + dec(result, start) + of pkTerminalIgnoreStyle: + var + i = 0 + a, b: TRune + result = start + while i < len(p.term): + while true: + fastRuneAt(p.term, i, a) + if a != TRune('_'): break + while true: + fastRuneAt(s, result, b) + if b != TRune('_'): break + if toLower(a) != toLower(b): + result = -1 + break + dec(result, start) + of pkChar: + if p.ch == s[start]: result = 1 + else: result = -1 + of pkCharChoice: + if contains(p.charChoice^, s[start]): result = 1 + else: result = -1 + of pkNonTerminal: + var oldMl = c.ml + when false: echo "enter: ", p.nt.name + result = m(s, p.nt.rule, start, c) + when false: echo "leave: ", p.nt.name + if result < 0: c.ml = oldMl + of pkSequence: + var oldMl = c.ml + result = 0 + for i in 0..high(p.sons): + var x = m(s, p.sons[i], start+result, c) + if x < 0: + c.ml = oldMl + result = -1 + break + else: inc(result, x) + of pkOrderedChoice: + var oldMl = c.ml + for i in 0..high(p.sons): + result = m(s, p.sons[i], start, c) + if result >= 0: break + c.ml = oldMl + of pkGreedyRep: + result = 0 + while true: + var x = m(s, p.sons[0], start+result, c) + # if x == 0, we have an endless loop; so the correct behaviour would be + # not to break. But endless loops can be easily introduced: + # ``(comment / \w*)*`` is such an example. Breaking for x == 0 does the + # expected thing in this case. + if x <= 0: break + inc(result, x) + of pkGreedyRepChar: + result = 0 + var ch = p.ch + while ch == s[start+result]: inc(result) + of pkGreedyRepSet: + result = 0 + while contains(p.charChoice^, s[start+result]): inc(result) + of pkOption: + result = max(0, m(s, p.sons[0], start, c)) + of pkAndPredicate: + var oldMl = c.ml + result = m(s, p.sons[0], start, c) + if result >= 0: result = 0 # do not consume anything + else: c.ml = oldMl + of pkNotPredicate: + var oldMl = c.ml + result = m(s, p.sons[0], start, c) + if result < 0: result = 0 + else: + c.ml = oldMl + result = -1 + of pkCapture: + var idx = c.ml # reserve a slot for the subpattern + inc(c.ml) + result = m(s, p.sons[0], start, c) + if result >= 0: + if idx < maxSubpatterns: + c.matches[idx] = (start, start+result-1) + #else: silently ignore the capture + else: + c.ml = idx + of pkRule, pkList: assert false + +proc match*(s: string, pattern: TPeg, 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. + var c: TMatchClosure + result = m(s, pattern, start, c) == len(s) + if result: + for i in 0..c.ml-1: + matches[i] = copy(s, c.matches[i][0], c.matches[i][1]) + +proc match*(s: string, pattern: TPeg, start = 0): bool = + ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``. + var c: TMatchClosure + result = m(s, pattern, start, c) == len(s) + +proc matchLen*(s: string, pattern: TPeg, 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. It's possible that a suffix of `s` remains + ## that does not belong to the match. + var c: TMatchClosure + result = m(s, pattern, start, c) + if result >= 0: + for i in 0..c.ml-1: + matches[i] = copy(s, c.matches[i][0], c.matches[i][1]) + +proc matchLen*(s: string, pattern: TPeg, 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. It's possible that a suffix of `s` remains + ## that does not belong to the match. + var c: TMatchClosure + result = m(s, pattern, start, c) + +proc find*(s: string, pattern: TPeg, matches: var openarray[string], + start = 0): int = + ## returns the starting position of ``pattern`` in ``s`` and the captured + ## substrings in the array ``matches``. If it does not match, nothing + ## is written into ``matches`` and -1 is returned. + for i in 0 .. s.len-1: + if matchLen(s, pattern, matches, i) >= 0: return i + return -1 + # could also use the pattern here: (!P .)* P + +proc find*(s: string, pattern: TPeg, start = 0): int = + ## returns the starting position of ``pattern`` in ``s``. If it does not + ## match, -1 is returned. + for i in 0 .. s.len-1: + if matchLen(s, pattern, i) >= 0: return i + return -1 + +template `=~`*(s: string, pattern: TPeg): 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 =~ peg"\s* {\w+} \s* '=' \s* {\w+}": + ## # matches a key=value pair: + ## echo("Key: ", matches[0]) + ## echo("Value: ", matches[1]) + ## elif line =~ peg"\s*{'#'.*}": + ## # matches a comment + ## # note that the implicit ``matches`` array is different from the + ## # ``matches`` array of the first branch + ## echo("comment: ", matches[0]) + ## 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: TPeg, start = 0): bool = + ## same as ``find(s, pattern, start) >= 0`` + return find(s, pattern, start) >= 0 + +proc contains*(s: string, pattern: TPeg, 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: TPeg): bool = + ## returns true if `s` starts with the pattern `prefix` + result = matchLen(s, prefix) >= 0 + +proc endsWith*(s: string, suffix: TPeg): 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: TPeg, 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(peg"{\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: TPeg, 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: TPeg, 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: TPeg): string = + ## Splits the string `s` into substrings. + ## + ## Substrings are separated by the PEG `sep`. + ## Examples: + ## + ## .. code-block:: nimrod + ## for word in split("00232this02939is39an22example111", peg"\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: TPeg): seq[string] {.noSideEffect.} = + ## Splits the string `s` into substrings. + accumulateResult(split(s, sep)) + +# ------------------------- debugging ----------------------------------------- + +proc esc(c: char, reserved = {'\0'..'\255'}): string = + case c + of '\b': result = "\\b" + of '\t': result = "\\t" + of '\c': result = "\\c" + of '\L': result = "\\l" + of '\v': result = "\\v" + of '\f': result = "\\f" + of '\e': result = "\\e" + of '\a': result = "\\a" + of '\\': result = "\\\\" + of 'a'..'z', 'A'..'Z', '0'..'9', '_': result = $c + elif c < ' ' or c >= '\128': result = '\\' & $ord(c) + elif c in reserved: result = '\\' & c + else: result = $c + +proc singleQuoteEsc(c: Char): string = return "'" & esc(c, {'\''}) & "'" + +proc singleQuoteEsc(str: string): string = + result = "'" + for c in items(str): add result, esc(c, {'\''}) + add result, '\'' + +proc charSetEscAux(cc: set[char]): string = + const reserved = {'^', '-', ']'} + result = "" + var c1 = 0 + while c1 <= 0xff: + if chr(c1) in cc: + var c2 = c1 + while c2 < 0xff and chr(succ(c2)) in cc: inc(c2) + if c1 == c2: + add result, esc(chr(c1), reserved) + elif c2 == succ(c1): + add result, esc(chr(c1), reserved) & esc(chr(c2), reserved) + else: + add result, esc(chr(c1), reserved) & '-' & esc(chr(c2), reserved) + c1 = c2 + inc(c1) + +proc CharSetEsc(cc: set[char]): string = + if card(cc) >= 128+64: + result = "[^" & CharSetEscAux({'\1'..'\xFF'} - cc) & ']' + else: + result = '[' & CharSetEscAux(cc) & ']' + +proc toStrAux(r: TPeg, res: var string) = + case r.kind + of pkEmpty: add(res, "()") + of pkAny: add(res, '.') + of pkAnyRune: add(res, '_') + of pkNewline: add(res, "\\n") + of pkTerminal: add(res, singleQuoteEsc(r.term)) + of pkTerminalIgnoreCase: + add(res, 'i') + add(res, singleQuoteEsc(r.term)) + of pkTerminalIgnoreStyle: + add(res, 'y') + add(res, singleQuoteEsc(r.term)) + of pkChar: add(res, singleQuoteEsc(r.ch)) + of pkCharChoice: add(res, charSetEsc(r.charChoice^)) + of pkNonTerminal: add(res, r.nt.name) + of pkSequence: + add(res, '(') + toStrAux(r.sons[0], res) + for i in 1 .. high(r.sons): + add(res, ' ') + toStrAux(r.sons[i], res) + add(res, ')') + of pkOrderedChoice: + add(res, '(') + toStrAux(r.sons[0], res) + for i in 1 .. high(r.sons): + add(res, " / ") + toStrAux(r.sons[i], res) + add(res, ')') + of pkGreedyRep: + toStrAux(r.sons[0], res) + add(res, '*') + of pkGreedyRepChar: + add(res, singleQuoteEsc(r.ch)) + add(res, '*') + of pkGreedyRepSet: + add(res, charSetEsc(r.charChoice^)) + add(res, '*') + of pkGreedyAny: + add(res, ".*") + of pkOption: + toStrAux(r.sons[0], res) + add(res, '?') + of pkAndPredicate: + add(res, '&') + toStrAux(r.sons[0], res) + of pkNotPredicate: + add(res, '!') + toStrAux(r.sons[0], res) + of pkCapture: + add(res, '{') + toStrAux(r.sons[0], res) + add(res, '}') + of pkRule: + toStrAux(r.sons[0], res) + add(res, " <- ") + toStrAux(r.sons[1], res) + of pkList: + for i in 0 .. high(r.sons): + toStrAux(r.sons[i], res) + add(res, "\n") + +proc `$` *(r: TPeg): string = + ## converts a PEG to its string representation + result = "" + toStrAux(r, result) + + +# ------------------- scanner ------------------------------------------------- + +type + TModifier = enum + modNone, + modVerbatim, + modIgnoreCase, + modIgnoreStyle + TTokKind = enum ## enumeration of all tokens + tkInvalid, ## invalid token + tkEof, ## end of file reached + tkAny, ## . + tkAnyRune, ## _ + tkIdentifier, ## abc + tkStringLit, ## "abc" or 'abc' + tkCharSet, ## [^A-Z] + tkParLe, ## '(' + tkParRi, ## ')' + tkCurlyLe, ## '{' + tkCurlyRi, ## '}' + tkArrow, ## '<-' + tkBar, ## '/' + tkStar, ## '*' + tkPlus, ## '+' + tkAmp, ## '&' + tkNot, ## '!' + tkOption, ## '?' + tkBuiltin, ## \identifier + tkEscaped ## \\ + + TToken {.final.} = object ## a token + kind: TTokKind ## the type of the token + modifier: TModifier + literal: string ## the parsed (string) literal + charset: set[char] ## if kind == tkCharSet + + TPegLexer = object ## the lexer object. + bufpos: int ## the current position within the buffer + buf: cstring ## the buffer itself + LineNumber: int ## the current line number + lineStart: int ## index of last line start in buffer + colOffset: int ## column to add + filename: string + +const + tokKindToStr: array[TTokKind, string] = [ + "invalid", "[EOF]", ".", "_", "identifier", "string literal", + "character set", "(", ")", "{", "}", "<-", "/", "*", "+", "&", "!", "?", + "built-in", "escaped" + ] + +proc HandleCR(L: var TPegLexer, pos: int): int = + assert(L.buf[pos] == '\c') + inc(L.linenumber) + result = pos+1 + if L.buf[result] == '\L': inc(result) + L.lineStart = result + +proc HandleLF(L: var TPegLexer, pos: int): int = + assert(L.buf[pos] == '\L') + inc(L.linenumber) + result = pos+1 + L.lineStart = result + +proc init(L: var TPegLexer, input, filename: string, line = 1, col = 0) = + L.buf = input + L.bufpos = 0 + L.lineNumber = line + L.colOffset = col + L.lineStart = 0 + L.filename = filename + +proc getColumn(L: TPegLexer): int {.inline.} = + result = abs(L.bufpos - L.lineStart) + L.colOffset + +proc getLine(L: TPegLexer): int {.inline.} = + result = L.linenumber + +proc errorStr(L: TPegLexer, msg: string, line = -1, col = -1): string = + var line = if line < 0: getLine(L) else: line + var col = if col < 0: getColumn(L) else: col + result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg] + +proc handleHexChar(c: var TPegLexer, xi: var int) = + case c.buf[c.bufpos] + of '0'..'9': + xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) + inc(c.bufpos) + of 'a'..'f': + xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10) + inc(c.bufpos) + of 'A'..'F': + xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) + inc(c.bufpos) + else: nil + +proc getEscapedChar(c: var TPegLexer, tok: var TToken) = + inc(c.bufpos) + case c.buf[c.bufpos] + of 'r', 'R', 'c', 'C': + add(tok.literal, '\c') + Inc(c.bufpos) + of 'l', 'L': + add(tok.literal, '\L') + Inc(c.bufpos) + of 'f', 'F': + add(tok.literal, '\f') + inc(c.bufpos) + of 'e', 'E': + add(tok.literal, '\e') + Inc(c.bufpos) + of 'a', 'A': + add(tok.literal, '\a') + Inc(c.bufpos) + of 'b', 'B': + add(tok.literal, '\b') + Inc(c.bufpos) + of 'v', 'V': + add(tok.literal, '\v') + Inc(c.bufpos) + of 't', 'T': + add(tok.literal, '\t') + Inc(c.bufpos) + of 'x', 'X': + inc(c.bufpos) + var xi = 0 + handleHexChar(c, xi) + handleHexChar(c, xi) + if xi == 0: tok.kind = tkInvalid + else: add(tok.literal, Chr(xi)) + of '0'..'9': + var val = ord(c.buf[c.bufpos]) - ord('0') + Inc(c.bufpos) + var i = 1 + while (i <= 3) and (c.buf[c.bufpos] in {'0'..'9'}): + val = val * 10 + ord(c.buf[c.bufpos]) - ord('0') + inc(c.bufpos) + inc(i) + if val > 0 and val <= 255: add(tok.literal, chr(val)) + else: tok.kind = tkInvalid + of '\0'..'\31': + tok.kind = tkInvalid + elif c.buf[c.bufpos] in strutils.letters: + tok.kind = tkInvalid + else: + add(tok.literal, c.buf[c.bufpos]) + Inc(c.bufpos) + +proc skip(c: var TPegLexer) = + var pos = c.bufpos + var buf = c.buf + while true: + case buf[pos] + of ' ', '\t': + Inc(pos) + of '#': + while not (buf[pos] in {'\c', '\L', '\0'}): inc(pos) + of '\c': + pos = HandleCR(c, pos) + buf = c.buf + of '\L': + pos = HandleLF(c, pos) + buf = c.buf + else: + break # EndOfFile also leaves the loop + c.bufpos = pos + +proc getString(c: var TPegLexer, tok: var TToken) = + tok.kind = tkStringLit + var pos = c.bufPos + 1 + var buf = c.buf + var quote = buf[pos-1] + while true: + case buf[pos] + of '\\': + c.bufpos = pos + getEscapedChar(c, tok) + pos = c.bufpos + of '\c', '\L', '\0': + tok.kind = tkInvalid + break + elif buf[pos] == quote: + inc(pos) + break + else: + add(tok.literal, buf[pos]) + Inc(pos) + c.bufpos = pos + +proc getCharSet(c: var TPegLexer, tok: var TToken) = + tok.kind = tkCharSet + tok.charset = {} + var pos = c.bufPos + 1 + var buf = c.buf + var caret = false + if buf[pos] == '^': + inc(pos) + caret = true + while true: + var ch: char + case buf[pos] + of ']': + inc(pos) + break + of '\\': + c.bufpos = pos + getEscapedChar(c, tok) + pos = c.bufpos + ch = tok.literal[tok.literal.len-1] + of '\C', '\L', '\0': + tok.kind = tkInvalid + break + else: + ch = buf[pos] + Inc(pos) + incl(tok.charset, ch) + if buf[pos] == '-': + if buf[pos+1] == ']': + incl(tok.charset, '-') + inc(pos) + else: + inc(pos) + var ch2: char + case buf[pos] + of '\\': + c.bufpos = pos + getEscapedChar(c, tok) + pos = c.bufpos + ch2 = tok.literal[tok.literal.len-1] + of '\C', '\L', '\0': + tok.kind = tkInvalid + break + else: + ch2 = buf[pos] + Inc(pos) + for i in ord(ch)+1 .. ord(ch2): + incl(tok.charset, chr(i)) + c.bufpos = pos + if caret: tok.charset = {'\1'..'\xFF'} - tok.charset + +proc getSymbol(c: var TPegLexer, tok: var TToken) = + var pos = c.bufpos + var buf = c.buf + while true: + add(tok.literal, buf[pos]) + Inc(pos) + if buf[pos] notin strutils.IdentChars: break + c.bufpos = pos + tok.kind = tkIdentifier + +proc getBuiltin(c: var TPegLexer, tok: var TToken) = + if c.buf[c.bufpos+1] in strutils.Letters: + inc(c.bufpos) + getSymbol(c, tok) + tok.kind = tkBuiltin + else: + tok.kind = tkEscaped + getEscapedChar(c, tok) # may set tok.kind to tkInvalid + +proc getTok(c: var TPegLexer, tok: var TToken) = + tok.kind = tkInvalid + tok.modifier = modNone + setlen(tok.literal, 0) + skip(c) + case c.buf[c.bufpos] + of '{': + tok.kind = tkCurlyLe + inc(c.bufpos) + add(tok.literal, '{') + of '}': + tok.kind = tkCurlyRi + inc(c.bufpos) + add(tok.literal, '}') + of '[': + getCharset(c, tok) + of '(': + tok.kind = tkParLe + Inc(c.bufpos) + add(tok.literal, '(') + of ')': + tok.kind = tkParRi + Inc(c.bufpos) + add(tok.literal, ')') + of '.': + tok.kind = tkAny + inc(c.bufpos) + add(tok.literal, '.') + of '_': + tok.kind = tkAnyRune + inc(c.bufpos) + add(tok.literal, '_') + of '\\': + getBuiltin(c, tok) + of '\'', '"': getString(c, tok) + of '\0': + tok.kind = tkEof + tok.literal = "[EOF]" + of 'a'..'z', 'A'..'Z', '\128'..'\255': + getSymbol(c, tok) + if c.buf[c.bufpos] in {'\'', '"'}: + case tok.literal + of "i": tok.modifier = modIgnoreCase + of "y": tok.modifier = modIgnoreStyle + of "v": tok.modifier = modVerbatim + else: nil + setLen(tok.literal, 0) + getString(c, tok) + if tok.modifier == modNone: tok.kind = tkInvalid + of '+': + tok.kind = tkPlus + inc(c.bufpos) + add(tok.literal, '+') + of '*': + tok.kind = tkStar + inc(c.bufpos) + add(tok.literal, '+') + of '<': + if c.buf[c.bufpos+1] == '-': + inc(c.bufpos, 2) + tok.kind = tkArrow + add(tok.literal, "<-") + else: + add(tok.literal, '<') + of '/': + tok.kind = tkBar + inc(c.bufpos) + add(tok.literal, '/') + of '?': + tok.kind = tkOption + inc(c.bufpos) + add(tok.literal, '?') + of '!': + tok.kind = tkNot + inc(c.bufpos) + add(tok.literal, '!') + of '&': + tok.kind = tkAmp + inc(c.bufpos) + add(tok.literal, '!') + else: + add(tok.literal, c.buf[c.bufpos]) + inc(c.bufpos) + +proc arrowIsNextTok(c: TPegLexer): bool = + # the only look ahead we need + var pos = c.bufpos + while c.buf[pos] in {'\t', ' '}: inc(pos) + result = c.buf[pos] == '<' and c.buf[pos+1] == '-' + +# ----------------------------- parser ---------------------------------------- + +type + EInvalidPeg* = object of EBase ## raised if an invalid PEG has been detected + TPegParser = object of TPegLexer ## the PEG parser object + tok: TToken + nonterms: seq[PNonTerminal] + modifier: TModifier + +proc getTok(p: var TPegParser) = getTok(p, p.tok) + +proc pegError(p: TPegParser, msg: string, line = -1, col = -1) = + var e: ref EInvalidPeg + new(e) + e.msg = errorStr(p, msg, line, col) + raise e + +proc eat(p: var TPegParser, kind: TTokKind) = + if p.tok.kind == kind: getTok(p) + else: pegError(p, tokKindToStr[kind] & " expected") + +proc parseExpr(p: var TPegParser): TPeg + +proc getNonTerminal(p: TPegParser, name: string): PNonTerminal = + for i in 0..high(p.nonterms): + result = p.nonterms[i] + if cmpIgnoreStyle(result.name, name) == 0: return + # forward reference: + result = newNonTerminal(name, getLine(p), getColumn(p)) + add(p.nonterms, result) + +proc modifiedTerm(s: string, m: TModifier): TPeg = + case m + of modNone, modVerbatim: result = term(s) + of modIgnoreCase: result = termIgnoreCase(s) + of modIgnoreStyle: result = termIgnoreStyle(s) + +proc primary(p: var TPegParser): TPeg = + case p.tok.kind + of tkAmp: + getTok(p) + return &primary(p) + of tkNot: + getTok(p) + return !primary(p) + else: nil + case p.tok.kind + of tkIdentifier: + if not arrowIsNextTok(p): + var nt = getNonTerminal(p, p.tok.literal) + incl(nt.flags, ntUsed) + result = nonTerminal(nt) + getTok(p) + else: + pegError(p, "expression expected, but found: " & p.tok.literal) + of tkStringLit: + var m = p.tok.modifier + if m == modNone: m = p.modifier + result = modifiedTerm(p.tok.literal, m) + getTok(p) + of tkCharSet: + if '\0' in p.tok.charset: + pegError(p, "binary zero ('\\0') not allowed in character class") + result = charset(p.tok.charset) + getTok(p) + of tkParLe: + getTok(p) + result = parseExpr(p) + eat(p, tkParRi) + of tkCurlyLe: + getTok(p) + result = capture(parseExpr(p)) + eat(p, tkCurlyRi) + of tkAny: + result = any() + getTok(p) + of tkAnyRune: + result = anyRune() + getTok(p) + of tkBuiltin: + case p.tok.literal + of "n": result = newLine() + of "d": result = charset({'0'..'9'}) + of "D": result = charset({'\1'..'\xff'} - {'0'..'9'}) + of "s": result = charset({' ', '\9'..'\13'}) + of "S": result = charset({'\1'..'\xff'} - {' ', '\9'..'\13'}) + of "w": result = charset({'a'..'z', 'A'..'Z', '_'}) + of "W": result = charset({'\1'..'\xff'} - {'a'..'z', 'A'..'Z', '_'}) + of "ident": result = pegs.ident + else: pegError(p, "unknown built-in: " & p.tok.literal) + getTok(p) + of tkEscaped: + result = term(p.tok.literal[0]) + getTok(p) + else: + pegError(p, "expression expected, but found: " & p.tok.literal) + getTok(p) # we must consume a token here to prevent endless loops! + while true: + case p.tok.kind + of tkOption: + result = ?result + getTok(p) + of tkStar: + result = *result + getTok(p) + of tkPlus: + result = +result + getTok(p) + else: break + +proc seqExpr(p: var TPegParser): TPeg = + result = primary(p) + while true: + case p.tok.kind + of tkAmp, tkNot, tkStringLit, tkCharset, tkParLe, tkCurlyLe, + tkAny, tkAnyRune, tkBuiltin, tkEscaped: + result = sequence(result, primary(p)) + of tkIdentifier: + if not arrowIsNextTok(p): + result = sequence(result, primary(p)) + else: break + else: break + +proc parseExpr(p: var TPegParser): TPeg = + result = seqExpr(p) + while p.tok.kind == tkBar: + getTok(p) + result = result / seqExpr(p) + +proc parseRule(p: var TPegParser): PNonTerminal = + if p.tok.kind == tkIdentifier and arrowIsNextTok(p): + result = getNonTerminal(p, p.tok.literal) + if ntDeclared in result.flags: + pegError(p, "attempt to redefine: " & result.name) + result.line = getLine(p) + result.col = getColumn(p) + getTok(p) + eat(p, tkArrow) + result.rule = parseExpr(p) + incl(result.flags, ntDeclared) # NOW inlining may be attempted + else: + pegError(p, "rule expected, but found: " & p.tok.literal) + +proc rawParse(p: var TPegParser): TPeg = + ## parses a rule or a PEG expression + if p.tok.kind == tkBuiltin: + case p.tok.literal + of "i": + p.modifier = modIgnoreCase + getTok(p) + of "y": + p.modifier = modIgnoreStyle + getTok(p) + else: nil + if p.tok.kind == tkIdentifier and arrowIsNextTok(p): + result = parseRule(p).rule + while p.tok.kind != tkEof: + discard parseRule(p) + else: + result = parseExpr(p) + if p.tok.kind != tkEof: + pegError(p, "EOF expected, but found: " & p.tok.literal) + for i in 0..high(p.nonterms): + var nt = p.nonterms[i] + if ntDeclared notin nt.flags: + pegError(p, "undeclared identifier: " & nt.name, nt.line, nt.col) + elif ntUsed notin nt.flags and i > 0: + pegError(p, "unused rule: " & nt.name, nt.line, nt.col) + +proc parsePeg*(input: string, filename = "pattern", line = 1, col = 0): TPeg = + var p: TPegParser + init(TPegLexer(p), input, filename, line, col) + p.tok.kind = tkInvalid + p.tok.modifier = modNone + p.tok.literal = "" + p.tok.charset = {} + p.nonterms = @[] + getTok(p) + result = rawParse(p) + +proc peg*(pattern: string): TPeg = + ## constructs a TPeg object from the `pattern`. The short name has been + ## chosen to encourage its use as a raw string modifier:: + ## + ## peg"{\ident} \s* '=' \s* {.*}" + result = parsePeg(pattern, "pattern") + +when isMainModule: + assert match("W_HI_Le", peg"\y 'while'") + assert(not match("W_HI_L", peg"\y 'while'")) + assert(not match("W_HI_Le", peg"\y v'while'")) + assert match("W_HI_Le", peg"y'while'") + + assert($ +digits == $peg"\d+") + assert "0158787".match(peg"\d+") + assert "ABC 0232".match(peg"\w+\s+\d+") + assert "ABC".match(peg"\d+ / \w+") + + for word in split("00232this02939is39an22example111", peg"\d+"): + writeln(stdout, word) + + assert matchLen("key", ident) == 3 + + var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident) + assert matchLen("key1= cal9", pattern) == 11 + + var ws = newNonTerminal("ws", 1, 1) + ws.rule = *whitespace + + var expr = newNonTerminal("expr", 1, 1) + expr.rule = sequence(capture(ident), *sequence( + nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) + + var c: TMatchClosure + var s = "a+b + c +d+e+f" + assert m(s, expr.rule, 0, c) == len(s) + var a = "" + for i in 0..c.ml-1: + a.add(copy(s, c.matches[i][0], c.matches[i][1])) + assert a == "abcdef" + #echo expr.rule + + #const filename = "lib/devel/peg/grammar.txt" + #var grammar = parsePeg(newFileStream(filename, fmRead), filename) + #echo "a <- [abc]*?".match(grammar) + assert find("_____abc_______", term("abc")) == 5 + assert match("_______ana", peg"A <- 'ana' / . A") + assert match("abcs%%%", peg"A <- ..A / .A / '%'") + + if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}": + assert matches[0] == "abc" + else: + assert false + + var g2 = peg"""S <- A B / C D + A <- 'a'+ + B <- 'b'+ + C <- 'c'+ + D <- 'd'+ + """ + assert($g2 == "((A B) / (C D))") + assert match("cccccdddddd", g2) + echo "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") + assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}") + diff --git a/lib/pure/re.nim b/lib/pure/re.nim new file mode 100755 index 000000000..f854c07e5 --- /dev/null +++ b/lib/pure/re.nim @@ -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) diff --git a/lib/pure/regexprs.nim b/lib/pure/regexprs.nim index 54f9dc471..cff3152cf 100755 --- a/lib/pure/regexprs.nim +++ b/lib/pure/regexprs.nim @@ -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 diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 337be31d1..f4d2911fc 100755 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -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 diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index cfbfef810..25b495f10 100755 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -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 diff --git a/lib/pure/variants.nim b/lib/pure/variants.nim new file mode 100755 index 000000000..f661f81a6 --- /dev/null +++ b/lib/pure/variants.nim @@ -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 = + + diff --git a/lib/system.nim b/lib/system.nim index f52a233f8..9a64362c5 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -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.} = diff --git a/lib/system/mm.nim b/lib/system/mm.nim index 9e207fcf0..76b5d83bd 100755 --- a/lib/system/mm.nim +++ b/lib/system/mm.nim @@ -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 diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index c0fbe2bb8..40e6e7b11 100755 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -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".} + + diff --git a/lib/wrappers/tre/config.h b/lib/wrappers/tre/config.h index 1a3a0bc3a..1a3a0bc3a 100644..100755 --- a/lib/wrappers/tre/config.h +++ b/lib/wrappers/tre/config.h diff --git a/lib/wrappers/tre/tre_all.c b/lib/wrappers/tre/tre_all.c index 8272657a3..8272657a3 100644..100755 --- a/lib/wrappers/tre/tre_all.c +++ b/lib/wrappers/tre/tre_all.c diff --git a/lib/wrappers/tre/version.txt b/lib/wrappers/tre/version.txt index 4d01880a7..4d01880a7 100644..100755 --- a/lib/wrappers/tre/version.txt +++ b/lib/wrappers/tre/version.txt diff --git a/nim/ast.pas b/nim/ast.pas index cbe5b37fd..8927fc862 100755 --- a/nim/ast.pas +++ b/nim/ast.pas @@ -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 ); diff --git a/nim/ccgexprs.pas b/nim/ccgexprs.pas index 99d36da2d..a5789487a 100755 --- a/nim/ccgexprs.pas +++ b/nim/ccgexprs.pas @@ -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)); diff --git a/nim/ccgtypes.pas b/nim/ccgtypes.pas index db07a6864..1c07fe5c7 100755 --- a/nim/ccgtypes.pas +++ b/nim/ccgtypes.pas @@ -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 diff --git a/nim/cgen.pas b/nim/cgen.pas index 17ae8b461..0b74f25b7 100755 --- a/nim/cgen.pas +++ b/nim/cgen.pas @@ -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' + diff --git a/nim/cgmeth.pas b/nim/cgmeth.pas index 6b9335c4c..6b9335c4c 100644..100755 --- a/nim/cgmeth.pas +++ b/nim/cgmeth.pas diff --git a/nim/commands.pas b/nim/commands.pas index b281552c9..1663b6b93 100755 --- a/nim/commands.pas +++ b/nim/commands.pas @@ -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); diff --git a/nim/depends.pas b/nim/depends.pas index d8b978142..c5b844044 100755 --- a/nim/depends.pas +++ b/nim/depends.pas @@ -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 diff --git a/nim/docgen.pas b/nim/docgen.pas index 4d94a2a68..468dd1bc9 100755 --- a/nim/docgen.pas +++ b/nim/docgen.pas @@ -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); diff --git a/nim/ecmasgen.pas b/nim/ecmasgen.pas index 8edd2605a..59cb3c330 100755 --- a/nim/ecmasgen.pas +++ b/nim/ecmasgen.pas @@ -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 diff --git a/nim/evals.pas b/nim/evals.pas index 3787ff371..bb6c8a2b1 100755 --- a/nim/evals.pas +++ b/nim/evals.pas @@ -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; diff --git a/nim/extccomp.pas b/nim/extccomp.pas index 9e7c7e4cc..aab4febb7 100755 --- a/nim/extccomp.pas +++ b/nim/extccomp.pas @@ -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; diff --git a/nim/filters.pas b/nim/filters.pas new file mode 100755 index 000000000..95f628fe2 --- /dev/null +++ b/nim/filters.pas @@ -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. diff --git a/nim/importer.pas b/nim/importer.pas index d031ffe3a..a1ed57978 100755 --- a/nim/importer.pas +++ b/nim/importer.pas @@ -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; diff --git a/nim/llstream.pas b/nim/llstream.pas index df4c823a6..30d9c0287 100755 --- a/nim/llstream.pas +++ b/nim/llstream.pas @@ -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); diff --git a/nim/lookups.pas b/nim/lookups.pas index d68095635..e4c07224f 100755 --- a/nim/lookups.pas +++ b/nim/lookups.pas @@ -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 diff --git a/nim/main.pas b/nim/main.pas index 1bb2ce586..c3f2cbd85 100755 --- a/nim/main.pas +++ b/nim/main.pas @@ -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; diff --git a/nim/msgs.pas b/nim/msgs.pas index 9d421a5bf..f1a3a200a 100755 --- a/nim/msgs.pas +++ b/nim/msgs.pas @@ -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 diff --git a/nim/nimrod.pas b/nim/nimrod.pas index 728325ccc..b3247bff0 100755 --- a/nim/nimrod.pas +++ b/nim/nimrod.pas @@ -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); diff --git a/nim/nos.pas b/nim/nos.pas index 2edb0864e..7b9d2ee09 100755 --- a/nim/nos.pas +++ b/nim/nos.pas @@ -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 diff --git a/nim/nsystem.pas b/nim/nsystem.pas index 49d17b172..4cdfade93 100755 --- a/nim/nsystem.pas +++ b/nim/nsystem.pas @@ -56,6 +56,11 @@ const snil = ''; type + TStringSeq = array of string; + TCharSet = set of Char; + + +type Natural = 0..high(int); Positive = 1..high(int); NObject = object // base type for all objects, cannot use @@ -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; diff --git a/nim/nversion.pas b/nim/nversion.pas index b2ba371f6..20ab5cc77 100755 --- a/nim/nversion.pas +++ b/nim/nversion.pas @@ -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 diff --git a/nim/options.pas b/nim/options.pas index d210f3b44..d03a24f2a 100755 --- a/nim/options.pas +++ b/nim/options.pas @@ -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; diff --git a/nim/osproc.pas b/nim/osproc.pas index dd6bd10f0..485daaf67 100755 --- a/nim/osproc.pas +++ b/nim/osproc.pas @@ -17,14 +17,42 @@ interface uses nsystem, nos; - -function executeCommand(const cmd: string): int; + +type + TProcessOption = (poEchoCmd, poUseShell, poStdErrToStdOut, poParentStreams); + TProcessOptions = set of TProcessOption; + +function execCmd(const cmd: string): int; +function execProcesses(const cmds: array of string; + options: TProcessOptions; + n: int): int; + +function countProcessors(): int; implementation -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. diff --git a/nim/pasparse.pas b/nim/pasparse.pas index c44c735d6..dbfbf0437 100755 --- a/nim/pasparse.pas +++ b/nim/pasparse.pas @@ -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)); diff --git a/nim/passes.pas b/nim/passes.pas index 8d961e5d3..c280a75b1 100755 --- a/nim/passes.pas +++ b/nim/passes.pas @@ -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); diff --git a/nim/pbraces.pas b/nim/pbraces.pas new file mode 100755 index 000000000..d362fac65 --- /dev/null +++ b/nim/pbraces.pas @@ -0,0 +1,1499 @@ +// +// +// The Nimrod Compiler +// (c) Copyright 2009 Andreas Rumpf +// +// See the file "copying.txt", included in this +// distribution, for details about the copyright. +// +unit pbraces; + +{$include config.inc} + +interface + +uses + nsystem, llstream, scanner, idents, strutils, ast, msgs, pnimsyn; + +function ParseAll(var p: TParser): PNode; + +function parseTopLevelStmt(var p: TParser): PNode; +// implements an iterator. Returns the next top-level statement or nil if end +// of stream. + +implementation + +// ------------------- 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; + +function optExpr(var p: TParser): PNode; // [expr] +begin + if (p.tok.tokType <> tkComma) and (p.tok.tokType <> tkBracketRi) + and (p.tok.tokType <> tkDotDot) then + result := parseExpr(p) + else + result := nil; +end; + +function dotdotExpr(var p: TParser; first: PNode = nil): PNode; +begin + result := newNodeP(nkRange, p); + addSon(result, first); + getTok(p); + optInd(p, result); + addSon(result, optExpr(p)); +end; + +function indexExpr(var p: TParser): PNode; +// indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr] +var + a, b: PNode; +begin + 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 indexExprList(var p: TParser; first: PNode): PNode; +var + a: PNode; +begin + result := newNodeP(nkBracketExpr, p); + addSon(result, first); + getTok(p); + optInd(p, result); + while (p.tok.tokType <> tkBracketRi) and (p.tok.tokType <> tkEof) + and (p.tok.tokType <> tkSad) do begin + a := indexExpr(p); + addSon(result, a); + if p.tok.tokType <> tkComma then break; + getTok(p); + optInd(p, a) + end; + optSad(p); + eat(p, tkBracketRi); +end; + +function exprColonEqExpr(var p: TParser; kind: TNodeKind; + tok: TTokType): PNode; +var + a: PNode; +begin + a := parseExpr(p); + if p.tok.tokType = tok then begin + result := newNodeP(kind, p); + getTok(p); + //optInd(p, result); + addSon(result, a); + addSon(result, parseExpr(p)); + end + else + result := a +end; + +procedure exprListAux(var p: TParser; elemKind: TNodeKind; + endTok, sepTok: TTokType; result: PNode); +var + a: PNode; +begin + getTok(p); + optInd(p, result); + while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof) do begin + a := exprColonEqExpr(p, elemKind, sepTok); + addSon(result, a); + if p.tok.tokType <> tkComma then break; + getTok(p); + optInd(p, a) + end; + eat(p, endTok); +end; + +function qualifiedIdent(var p: TParser): PNode; +var + a: PNode; +begin + result := parseSymbol(p); + if p.tok.tokType = tkDot then begin + getTok(p); + optInd(p, result); + a := result; + result := newNodeI(nkDotExpr, a.info); + addSon(result, a); + addSon(result, parseSymbol(p)); + end; +end; + +procedure qualifiedIdentListAux(var p: TParser; endTok: TTokType; + result: PNode); +var + a: PNode; +begin + getTok(p); + optInd(p, result); + while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof) do begin + a := qualifiedIdent(p); + addSon(result, a); + if p.tok.tokType <> tkComma then break; + getTok(p); + optInd(p, a) + end; + eat(p, endTok); +end; + +procedure exprColonEqExprListAux(var p: TParser; elemKind: TNodeKind; + endTok, sepTok: TTokType; result: PNode); +var + a: PNode; +begin + getTok(p); + optInd(p, result); + while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof) + and (p.tok.tokType <> tkSad) do begin + a := exprColonEqExpr(p, elemKind, sepTok); + addSon(result, a); + if p.tok.tokType <> tkComma then break; + getTok(p); + optInd(p, a) + end; + optSad(p); + eat(p, endTok); +end; + +function exprColonEqExprList(var p: TParser; kind, elemKind: TNodeKind; + endTok, sepTok: TTokType): PNode; +begin + result := newNodeP(kind, p); + exprColonEqExprListAux(p, elemKind, endTok, sepTok, result); +end; + +function parseCast(var p: TParser): PNode; +begin + result := newNodeP(nkCast, p); + getTok(p); + eat(p, tkBracketLe); + optInd(p, result); + addSon(result, parseTypeDesc(p)); + optSad(p); + eat(p, tkBracketRi); + eat(p, tkParLe); + optInd(p, result); + addSon(result, parseExpr(p)); + optSad(p); + eat(p, tkParRi); +end; + +function parseAddr(var p: TParser): PNode; +begin + result := newNodeP(nkAddr, p); + getTok(p); + eat(p, tkParLe); + optInd(p, result); + addSon(result, parseExpr(p)); + optSad(p); + eat(p, tkParRi); +end; + +procedure setBaseFlags(n: PNode; base: TNumericalBase); +begin + case base of + base10: begin end; + base2: include(n.flags, nfBase2); + base8: include(n.flags, nfBase8); + base16: include(n.flags, nfBase16); + end +end; + +function identOrLiteral(var p: TParser): PNode; +begin + case p.tok.tokType of + tkSymbol: begin + result := newIdentNodeP(p.tok.ident, p); + getTok(p) + end; + tkAccent: result := accExpr(p); + // literals + tkIntLit: begin + result := newIntNodeP(nkIntLit, p.tok.iNumber, p); + setBaseFlags(result, p.tok.base); + getTok(p); + end; + tkInt8Lit: begin + result := newIntNodeP(nkInt8Lit, p.tok.iNumber, p); + setBaseFlags(result, p.tok.base); + getTok(p); + end; + tkInt16Lit: begin + result := newIntNodeP(nkInt16Lit, p.tok.iNumber, p); + setBaseFlags(result, p.tok.base); + getTok(p); + end; + tkInt32Lit: begin + result := newIntNodeP(nkInt32Lit, p.tok.iNumber, p); + setBaseFlags(result, p.tok.base); + getTok(p); + end; + tkInt64Lit: begin + result := newIntNodeP(nkInt64Lit, p.tok.iNumber, p); + setBaseFlags(result, p.tok.base); + getTok(p); + end; + tkFloatLit: begin + result := newFloatNodeP(nkFloatLit, p.tok.fNumber, p); + setBaseFlags(result, p.tok.base); + getTok(p); + end; + tkFloat32Lit: begin + result := newFloatNodeP(nkFloat32Lit, p.tok.fNumber, p); + setBaseFlags(result, p.tok.base); + getTok(p); + end; + tkFloat64Lit: begin + result := newFloatNodeP(nkFloat64Lit, p.tok.fNumber, p); + setBaseFlags(result, p.tok.base); + getTok(p); + end; + tkStrLit: begin + result := newStrNodeP(nkStrLit, p.tok.literal, p); + getTok(p); + end; + tkRStrLit: begin + result := newStrNodeP(nkRStrLit, p.tok.literal, p); + getTok(p); + end; + tkTripleStrLit: begin + result := newStrNodeP(nkTripleStrLit, p.tok.literal, p); + getTok(p); + end; + tkCallRStrLit: begin + result := newNodeP(nkCallStrLit, p); + addSon(result, newIdentNodeP(p.tok.ident, p)); + addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p)); + getTok(p); + end; + tkCallTripleStrLit: begin + result := newNodeP(nkCallStrLit, p); + addSon(result, newIdentNodeP(p.tok.ident, p)); + addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p)); + getTok(p); + end; + tkCharLit: begin + result := newIntNodeP(nkCharLit, ord(p.tok.literal[strStart]), p); + getTok(p); + end; + tkNil: begin + result := newNodeP(nkNilLit, p); + getTok(p); + end; + tkParLe: begin // () constructor + result := exprColonEqExprList(p, nkPar, nkExprColonExpr, tkParRi, + tkColon); + end; + tkCurlyLe: begin // {} constructor + result := exprColonEqExprList(p, nkCurly, nkRange, tkCurlyRi, tkDotDot); + end; + tkBracketLe: begin // [] constructor + result := exprColonEqExprList(p, nkBracket, nkExprColonExpr, tkBracketRi, + tkColon); + end; + tkCast: result := parseCast(p); + tkAddr: result := parseAddr(p); + else begin + parMessage(p, errExprExpected, tokToStr(p.tok)); + getTok(p); // we must consume a token here to prevend endless loops! + result := nil + end + end +end; + +function primary(var p: TParser): PNode; +var + a: PNode; +begin + // prefix operator? + if (p.tok.tokType = tkNot) or (p.tok.tokType = tkOpr) then begin + result := newNodeP(nkPrefix, p); + a := newIdentNodeP(p.tok.ident, p); + addSon(result, a); + getTok(p); + optInd(p, a); + addSon(result, primary(p)); + exit + end + else if p.tok.tokType = tkBind then begin + result := newNodeP(nkBind, p); + getTok(p); + optInd(p, result); + addSon(result, primary(p)); + exit + end; + result := identOrLiteral(p); + while true do begin + case p.tok.tokType of + tkParLe: begin + a := result; + result := newNodeP(nkCall, p); + addSon(result, a); + exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result); + end; + tkDot: begin + a := result; + result := newNodeP(nkDotExpr, p); + addSon(result, a); + getTok(p); // skip '.' + optInd(p, result); + addSon(result, parseSymbol(p)); + end; + tkHat: begin + a := result; + result := newNodeP(nkDerefExpr, p); + addSon(result, a); + getTok(p); + end; + tkBracketLe: result := indexExprList(p, result); + else break + end + end +end; + +function lowestExprAux(var p: TParser; out v: PNode; limit: int): PToken; +var + op, nextop: PToken; + opPred: int; + v2, node, opNode: PNode; +begin + v := primary(p); + // expand while operators have priorities higher than 'limit' + op := p.tok; + opPred := getPrecedence(p.tok); + while (opPred > limit) do begin + node := newNodeP(nkInfix, p); + opNode := newIdentNodeP(op.ident, p); + // skip operator: + getTok(p); + optInd(p, opNode); + + // read sub-expression with higher priority + nextop := lowestExprAux(p, v2, opPred); + addSon(node, opNode); + addSon(node, v); + addSon(node, v2); + v := node; + op := nextop; + opPred := getPrecedence(nextop); + end; + result := op; // return first untreated operator +end; + +function lowestExpr(var p: TParser): PNode; +begin +{@discard} lowestExprAux(p, result, -1); +end; + +function parseIfExpr(var p: TParser): PNode; +// if (expr) expr else expr +var + branch: PNode; +begin + result := newNodeP(nkIfExpr, p); + while true do begin + getTok(p); // skip `if`, `elif` + branch := newNodeP(nkElifExpr, p); + eat(p, tkParLe); + addSon(branch, parseExpr(p)); + eat(p, tkParRi); + addSon(branch, parseExpr(p)); + addSon(result, branch); + if p.tok.tokType <> tkElif then break + end; + branch := newNodeP(nkElseExpr, p); + eat(p, tkElse); + addSon(branch, parseExpr(p)); + addSon(result, branch); +end; + +function parsePragma(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkPragma, p); + getTok(p); + optInd(p, result); + while (p.tok.tokType <> tkCurlyDotRi) and (p.tok.tokType <> tkCurlyRi) + and (p.tok.tokType <> tkEof) and (p.tok.tokType <> tkSad) do begin + a := exprColonEqExpr(p, nkExprColonExpr, tkColon); + addSon(result, a); + if p.tok.tokType = tkComma then begin + getTok(p); + optInd(p, a) + end + end; + optSad(p); + if (p.tok.tokType = tkCurlyDotRi) or (p.tok.tokType = tkCurlyRi) then + getTok(p) + else + 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; + 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 = tkCurlyLe) 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, tkProc, tkBind, + tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, + tkVar, tkRef, tkPtr, tkTuple, tkType: result := true; + else result := false; + end; +end; + +function parseExprStmt(var p: TParser): PNode; +var + a, b, e: PNode; +begin + a := lowestExpr(p); + if p.tok.tokType = tkEquals then begin + getTok(p); + optInd(p, result); + b := parseExpr(p); + result := newNodeI(nkAsgn, a.info); + addSon(result, a); + addSon(result, b); + end + else begin + result := newNodeP(nkCommand, p); + result.info := a.info; + addSon(result, a); + while true do begin + if not isExprStart(p) then break; + e := parseExpr(p); + addSon(result, e); + if p.tok.tokType <> tkComma then break; + getTok(p); + optInd(p, a); + end; + if sonsLen(result) <= 1 then result := a + else a := result; + if p.tok.tokType = tkCurlyLe then begin // macro statement + result := newNodeP(nkMacroStmt, p); + result.info := a.info; + addSon(result, a); + getTok(p); + skipComment(p, result); + if (p.tok.tokType = tkInd) + or not (p.tok.TokType in [tkOf, tkElif, tkElse, tkExcept]) then + addSon(result, parseStmt(p)); + while true do begin + if p.tok.tokType = tkSad then getTok(p); + case p.tok.tokType of + tkOf: begin + b := newNodeP(nkOfBranch, p); + exprListAux(p, nkRange, tkCurlyLe, tkDotDot, b); + end; + tkElif: begin + b := newNodeP(nkElifBranch, p); + getTok(p); + optInd(p, b); + addSon(b, parseExpr(p)); + eat(p, tkCurlyLe); + end; + tkExcept: begin + b := newNodeP(nkExceptBranch, p); + qualifiedIdentListAux(p, tkCurlyLe, b); + skipComment(p, b); + end; + tkElse: begin + b := newNodeP(nkElse, p); + getTok(p); + eat(p, tkCurlyLe); + end; + else break; + end; + addSon(b, parseStmt(p)); + eat(p, tkCurlyRi); + addSon(result, b); + if b.kind = nkElse then break; + end; + eat(p, tkCurlyRi); + end + end +end; + +function parseImportStmt(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkImportStmt, p); + getTok(p); // skip `import` + optInd(p, result); + while true do begin + case p.tok.tokType of + tkEof, tkSad, tkDed: break; + tkSymbol, tkAccent: a := parseSymbol(p); + tkRStrLit: begin + a := newStrNodeP(nkRStrLit, p.tok.literal, p); + getTok(p) + end; + tkStrLit: begin + a := newStrNodeP(nkStrLit, p.tok.literal, p); + getTok(p); + end; + tkTripleStrLit: begin + a := newStrNodeP(nkTripleStrLit, p.tok.literal, p); + getTok(p) + end; + else begin + parMessage(p, errIdentifierExpected, tokToStr(p.tok)); + break + end + end; + addSon(result, a); + if p.tok.tokType <> tkComma then break; + getTok(p); + optInd(p, a) + end; +end; + +function parseIncludeStmt(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkIncludeStmt, p); + getTok(p); // skip `include` + optInd(p, result); + while true do begin + case p.tok.tokType of + tkEof, tkSad, tkDed: break; + tkSymbol, tkAccent: a := parseSymbol(p); + tkRStrLit: begin + a := newStrNodeP(nkRStrLit, p.tok.literal, p); + getTok(p) + end; + tkStrLit: begin + a := newStrNodeP(nkStrLit, p.tok.literal, p); + getTok(p); + end; + tkTripleStrLit: begin + a := newStrNodeP(nkTripleStrLit, p.tok.literal, p); + getTok(p) + end; + else begin + parMessage(p, errIdentifierExpected, tokToStr(p.tok)); + break + end; + end; + addSon(result, a); + if p.tok.tokType <> tkComma then break; + getTok(p); + optInd(p, a) + end; +end; + +function parseFromStmt(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkFromStmt, p); + getTok(p); // skip `from` + optInd(p, result); + case p.tok.tokType of + tkSymbol, tkAccent: a := parseSymbol(p); + tkRStrLit: begin + a := newStrNodeP(nkRStrLit, p.tok.literal, p); + getTok(p) + end; + tkStrLit: begin + a := newStrNodeP(nkStrLit, p.tok.literal, p); + getTok(p); + end; + tkTripleStrLit: begin + a := newStrNodeP(nkTripleStrLit, p.tok.literal, p); + getTok(p) + end; + else begin + parMessage(p, errIdentifierExpected, tokToStr(p.tok)); exit + end + end; + addSon(result, a); + //optInd(p, a); + eat(p, tkImport); + optInd(p, result); + while true do begin + case p.tok.tokType of + tkEof, tkSad, tkDed: break; + tkSymbol, tkAccent: a := parseSymbol(p); + else begin + parMessage(p, errIdentifierExpected, tokToStr(p.tok)); + break + end; + end; + //optInd(p, a); + addSon(result, a); + if p.tok.tokType <> tkComma then break; + getTok(p); + optInd(p, a) + end; +end; + +function parseReturnOrRaise(var p: TParser; kind: TNodeKind): PNode; +begin + result := newNodeP(kind, p); + getTok(p); + optInd(p, result); + case p.tok.tokType of + tkEof, tkSad, tkDed: addSon(result, nil); + else addSon(result, parseExpr(p)); + end; +end; + +function parseYieldOrDiscard(var p: TParser; kind: TNodeKind): PNode; +begin + result := newNodeP(kind, p); + getTok(p); + optInd(p, result); + addSon(result, parseExpr(p)); +end; + +function parseBreakOrContinue(var p: TParser; kind: TNodeKind): PNode; +begin + result := newNodeP(kind, p); + getTok(p); + optInd(p, result); + case p.tok.tokType of + tkEof, tkSad, tkDed: addSon(result, nil); + else addSon(result, parseSymbol(p)); + end; +end; + +function parseIfOrWhen(var p: TParser; kind: TNodeKind): PNode; +var + branch: PNode; +begin + result := newNodeP(kind, p); + while true do begin + getTok(p); // skip `if`, `when`, `elif` + branch := newNodeP(nkElifBranch, p); + optInd(p, branch); + eat(p, tkParLe); + addSon(branch, parseExpr(p)); + eat(p, tkParRi); + skipComment(p, branch); + addSon(branch, parseStmt(p)); + skipComment(p, branch); + addSon(result, branch); + if p.tok.tokType <> tkElif then break + end; + if p.tok.tokType = tkElse then begin + branch := newNodeP(nkElse, p); + eat(p, tkElse); + skipComment(p, branch); + addSon(branch, parseStmt(p)); + addSon(result, branch); + end +end; + +function parseWhile(var p: TParser): PNode; +begin + result := newNodeP(nkWhileStmt, p); + getTok(p); + optInd(p, result); + eat(p, tkParLe); + addSon(result, parseExpr(p)); + eat(p, tkParRi); + skipComment(p, result); + addSon(result, parseStmt(p)); +end; + +function parseCase(var p: TParser): PNode; +var + b: PNode; + inElif: bool; +begin + result := newNodeP(nkCaseStmt, p); + getTok(p); + eat(p, tkParLe); + addSon(result, parseExpr(p)); + eat(p, tkParRi); + skipComment(p, result); + inElif := false; + while true do begin + if p.tok.tokType = tkSad then getTok(p); + case p.tok.tokType of + tkOf: begin + if inElif then break; + b := newNodeP(nkOfBranch, p); + exprListAux(p, nkRange, tkColon, tkDotDot, b); + end; + tkElif: begin + inElif := true; + b := newNodeP(nkElifBranch, p); + getTok(p); + optInd(p, b); + addSon(b, parseExpr(p)); + eat(p, tkColon); + end; + tkElse: begin + b := newNodeP(nkElse, p); + getTok(p); + eat(p, tkColon); + end; + else break; + end; + skipComment(p, b); + addSon(b, parseStmt(p)); + addSon(result, b); + if b.kind = nkElse then break; + end +end; + +function parseTry(var p: TParser): PNode; +var + b: PNode; +begin + result := newNodeP(nkTryStmt, p); + getTok(p); + eat(p, tkColon); + skipComment(p, result); + addSon(result, parseStmt(p)); + b := nil; + while true do begin + if p.tok.tokType = tkSad then getTok(p); + case p.tok.tokType of + tkExcept: begin + b := newNodeP(nkExceptBranch, p); + qualifiedIdentListAux(p, tkColon, b); + end; + tkFinally: begin + b := newNodeP(nkFinally, p); + getTok(p); + eat(p, tkColon); + end; + else break; + end; + skipComment(p, b); + addSon(b, parseStmt(p)); + addSon(result, b); + if b.kind = nkFinally then break; + end; + if b = nil then parMessage(p, errTokenExpected, 'except'); +end; + +function parseFor(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkForStmt, p); + getTok(p); + optInd(p, result); + a := parseSymbol(p); + addSon(result, a); + while p.tok.tokType = tkComma do begin + getTok(p); + optInd(p, a); + a := parseSymbol(p); + addSon(result, a); + end; + eat(p, tkIn); + addSon(result, exprColonEqExpr(p, nkRange, tkDotDot)); + eat(p, tkColon); + skipComment(p, result); + addSon(result, parseStmt(p)) +end; + +function parseBlock(var p: TParser): PNode; +begin + result := newNodeP(nkBlockStmt, p); + getTok(p); + optInd(p, result); + case p.tok.tokType of + tkEof, tkSad, tkDed, tkColon: addSon(result, nil); + else addSon(result, parseSymbol(p)); + end; + eat(p, tkColon); + skipComment(p, result); + addSon(result, parseStmt(p)); +end; + +function parseAsm(var p: TParser): PNode; +begin + result := newNodeP(nkAsmStmt, p); + getTok(p); + optInd(p, result); + if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p)) + else addSon(result, nil); + case p.tok.tokType of + tkStrLit: addSon(result, newStrNodeP(nkStrLit, p.tok.literal, p)); + tkRStrLit: addSon(result, newStrNodeP(nkRStrLit, p.tok.literal, p)); + tkTripleStrLit: + addSon(result, newStrNodeP(nkTripleStrLit, p.tok.literal, p)); + else begin + parMessage(p, errStringLiteralExpected); + addSon(result, nil); exit + end; + end; + getTok(p); +end; + +function parseGenericParamList(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkGenericParams, p); + getTok(p); + optInd(p, result); + while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin + a := parseIdentColonEquals(p, {@set}[withBothOptional]); + addSon(result, a); + if p.tok.tokType <> tkComma then break; + getTok(p); + optInd(p, a) + end; + optSad(p); + eat(p, tkBracketRi); +end; + +function parseRoutine(var p: TParser; kind: TNodeKind): PNode; +begin + result := newNodeP(kind, p); + getTok(p); + optInd(p, result); + addSon(result, identVis(p)); + if p.tok.tokType = tkBracketLe then addSon(result, parseGenericParamList(p)) + else addSon(result, nil); + 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 + addSon(result, nil); + indAndComment(p, result); // XXX: document this in the grammar! +end; + +function newCommentStmt(var p: TParser): PNode; +begin + result := newNodeP(nkCommentStmt, p); + result.info.line := result.info.line - int16(1); +end; + +type + TDefParser = function (var p: TParser): PNode; + +function parseSection(var p: TParser; kind: TNodeKind; + defparser: TDefParser): PNode; +var + a: PNode; +begin + result := newNodeP(kind, p); + getTok(p); + skipComment(p, result); + case p.tok.tokType of + tkInd: begin + pushInd(p.lex^, p.tok.indent); + getTok(p); skipComment(p, result); + while true do begin + case p.tok.tokType of + tkSad: getTok(p); + tkSymbol, tkAccent: begin + a := defparser(p); + skipComment(p, a); + addSon(result, a); + end; + tkDed: begin getTok(p); break end; + tkEof: break; // BUGFIX + tkComment: begin + a := newCommentStmt(p); + skipComment(p, a); + addSon(result, a); + end; + else begin + parMessage(p, errIdentifierExpected, tokToStr(p.tok)); + break + end + end + end; + popInd(p.lex^); + end; + tkSymbol, tkAccent, tkParLe: begin + // tkParLe is allowed for ``var (x, y) = ...`` tuple parsing + addSon(result, defparser(p)); + end + else parMessage(p, errIdentifierExpected, tokToStr(p.tok)); + end +end; + +function parseConstant(var p: TParser): PNode; +begin + result := newNodeP(nkConstDef, p); + addSon(result, identWithPragma(p)); + if p.tok.tokType = tkColon then begin + getTok(p); optInd(p, result); + addSon(result, parseTypeDesc(p)); + end + else + addSon(result, nil); + eat(p, tkEquals); + optInd(p, result); + addSon(result, parseExpr(p)); + indAndComment(p, result); // XXX: special extension! +end; + +function parseEnum(var p: TParser): PNode; +var + a, b: PNode; +begin + result := newNodeP(nkEnumTy, p); + a := nil; + getTok(p); + optInd(p, result); + if p.tok.tokType = tkOf then begin + a := newNodeP(nkOfInherit, p); + getTok(p); optInd(p, a); + addSon(a, parseTypeDesc(p)); + addSon(result, a) + end + else addSon(result, nil); + + while true do begin + case p.tok.tokType of + tkEof, tkSad, tkDed: break; + else a := parseSymbol(p); + end; + optInd(p, a); + if p.tok.tokType = tkEquals then begin + getTok(p); + optInd(p, a); + b := a; + a := newNodeP(nkEnumFieldDef, p); + addSon(a, b); + addSon(a, parseExpr(p)); + skipComment(p, a); + end; + if p.tok.tokType = tkComma then begin + getTok(p); + optInd(p, a) + end; + addSon(result, a); + end +end; + +function parseObjectPart(var p: TParser): PNode; forward; + +function parseObjectWhen(var p: TParser): PNode; +var + branch: PNode; +begin + result := newNodeP(nkRecWhen, p); + while true do begin + getTok(p); // skip `when`, `elif` + branch := newNodeP(nkElifBranch, p); + optInd(p, branch); + addSon(branch, parseExpr(p)); + eat(p, tkColon); + skipComment(p, branch); + addSon(branch, parseObjectPart(p)); + skipComment(p, branch); + addSon(result, branch); + if p.tok.tokType <> tkElif then break + end; + if p.tok.tokType = tkElse then begin + branch := newNodeP(nkElse, p); + eat(p, tkElse); eat(p, tkColon); + skipComment(p, branch); + addSon(branch, parseObjectPart(p)); + addSon(result, branch); + end +end; + +function parseObjectCase(var p: TParser): PNode; +var + a, b: PNode; +begin + result := newNodeP(nkRecCase, p); + getTok(p); + a := newNodeP(nkIdentDefs, p); + addSon(a, identWithPragma(p)); + eat(p, tkColon); + addSon(a, parseTypeDesc(p)); + addSon(a, nil); + addSon(result, a); + skipComment(p, result); + while true do begin + if p.tok.tokType = tkSad then getTok(p); + case p.tok.tokType of + tkOf: begin + b := newNodeP(nkOfBranch, p); + exprListAux(p, nkRange, tkColon, tkDotDot, b); + end; + tkElse: begin + b := newNodeP(nkElse, p); + getTok(p); + eat(p, tkColon); + end; + else break; + end; + skipComment(p, b); + addSon(b, parseObjectPart(p)); + addSon(result, b); + if b.kind = nkElse then break; + end +end; + +function parseObjectPart(var p: TParser): PNode; +begin + case p.tok.tokType of + tkInd: begin + result := newNodeP(nkRecList, p); + pushInd(p.lex^, p.tok.indent); + getTok(p); skipComment(p, result); + while true do begin + case p.tok.tokType of + tkSad: getTok(p); + tkCase, tkWhen, tkSymbol, tkAccent, tkNil: begin + addSon(result, parseObjectPart(p)); + end; + tkDed: begin getTok(p); break end; + tkEof: break; + else begin + parMessage(p, errIdentifierExpected, tokToStr(p.tok)); + break + end + end + end; + popInd(p.lex^); + end; + tkWhen: result := parseObjectWhen(p); + tkCase: result := parseObjectCase(p); + tkSymbol, tkAccent: begin + result := parseIdentColonEquals(p, {@set}[withPragma]); + skipComment(p, result); + end; + tkNil: begin + result := newNodeP(nkNilLit, p); + getTok(p); + end; + else result := nil + end +end; + +function parseObject(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkObjectTy, p); + getTok(p); + if p.tok.tokType = tkCurlyDotLe then addSon(result, parsePragma(p)) + else addSon(result, nil); + if p.tok.tokType = tkOf then begin + a := newNodeP(nkOfInherit, p); + getTok(p); + addSon(a, parseTypeDesc(p)); + addSon(result, a); + end + else addSon(result, nil); + skipComment(p, result); + addSon(result, parseObjectPart(p)); +end; + +function parseDistinct(var p: TParser): PNode; +begin + result := newNodeP(nkDistinctTy, p); + getTok(p); + optInd(p, result); + addSon(result, parseTypeDesc(p)); +end; + +function parseTypeDef(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkTypeDef, p); + addSon(result, identWithPragma(p)); + if p.tok.tokType = tkBracketLe then addSon(result, parseGenericParamList(p)) + else addSon(result, nil); + if p.tok.tokType = tkEquals then begin + getTok(p); optInd(p, result); + case p.tok.tokType of + tkObject: a := parseObject(p); + tkEnum: a := parseEnum(p); + tkDistinct: a := parseDistinct(p); + else a := parseTypeDesc(p); + end; + addSon(result, a); + end + else + addSon(result, nil); + indAndComment(p, result); // special extension! +end; + +function parseVarTuple(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkVarTuple, p); + getTok(p); // skip '(' + optInd(p, result); + while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin + a := identWithPragma(p); + addSon(result, a); + if p.tok.tokType <> tkComma then break; + getTok(p); + optInd(p, a) + end; + addSon(result, nil); // no type desc + optSad(p); + eat(p, tkParRi); + eat(p, tkEquals); + optInd(p, result); + addSon(result, parseExpr(p)); +end; + +function parseVariable(var p: TParser): PNode; +begin + if p.tok.tokType = tkParLe then + result := parseVarTuple(p) + else + result := parseIdentColonEquals(p, {@set}[withPragma]); + indAndComment(p, result); // special extension! +end; + +function simpleStmt(var p: TParser): PNode; +begin + case p.tok.tokType of + tkReturn: result := parseReturnOrRaise(p, nkReturnStmt); + tkRaise: result := parseReturnOrRaise(p, nkRaiseStmt); + tkYield: result := parseYieldOrDiscard(p, nkYieldStmt); + tkDiscard: result := parseYieldOrDiscard(p, nkDiscardStmt); + tkBreak: result := parseBreakOrContinue(p, nkBreakStmt); + tkContinue: result := parseBreakOrContinue(p, nkContinueStmt); + tkCurlyDotLe: result := parsePragma(p); + tkImport: result := parseImportStmt(p); + tkFrom: result := parseFromStmt(p); + tkInclude: result := parseIncludeStmt(p); + tkComment: result := newCommentStmt(p); + else begin + if isExprStart(p) then + result := parseExprStmt(p) + else + result := nil; + end + end; + if result <> nil then + skipComment(p, result); +end; + +function complexOrSimpleStmt(var p: TParser): PNode; +begin + case p.tok.tokType of + tkIf: result := parseIfOrWhen(p, nkIfStmt); + tkWhile: result := parseWhile(p); + tkCase: result := parseCase(p); + tkTry: result := parseTry(p); + tkFor: result := parseFor(p); + tkBlock: result := parseBlock(p); + tkAsm: result := parseAsm(p); + tkProc: result := parseRoutine(p, nkProcDef); + tkMethod: result := parseRoutine(p, nkMethodDef); + tkIterator: result := parseRoutine(p, nkIteratorDef); + tkMacro: result := parseRoutine(p, nkMacroDef); + tkTemplate: result := parseRoutine(p, nkTemplateDef); + tkConverter: result := parseRoutine(p, nkConverterDef); + tkType: result := parseSection(p, nkTypeSection, parseTypeDef); + tkConst: result := parseSection(p, nkConstSection, parseConstant); + tkWhen: result := parseIfOrWhen(p, nkWhenStmt); + tkVar: result := parseSection(p, nkVarSection, parseVariable); + else result := simpleStmt(p); + end +end; + +function parseStmt(var p: TParser): PNode; +var + a: PNode; +begin + if p.tok.tokType = tkInd then begin + result := newNodeP(nkStmtList, p); + pushInd(p.lex^, p.tok.indent); + getTok(p); + while true do begin + case p.tok.tokType of + tkSad: getTok(p); + tkEof: break; + tkDed: begin getTok(p); break end; + else begin + a := complexOrSimpleStmt(p); + if a = nil then break; + addSon(result, a); + end + end + end; + popInd(p.lex^); + end + else begin + // the case statement is only needed for better error messages: + case p.tok.tokType of + tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, + tkProc, tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar: begin + parMessage(p, errComplexStmtRequiresInd); + result := nil + end + else begin + result := simpleStmt(p); + if result = nil then parMessage(p, errExprExpected, tokToStr(p.tok)); + if p.tok.tokType = tkSad then getTok(p); + end + end + end +end; + +function parseAll(var p: TParser): PNode; +var + a: PNode; +begin + result := newNodeP(nkStmtList, p); + while true do begin + case p.tok.tokType of + tkSad: getTok(p); + tkDed, tkInd: parMessage(p, errInvalidIndentation); + tkEof: break; + else begin + a := complexOrSimpleStmt(p); + if a = nil then parMessage(p, errExprExpected, tokToStr(p.tok)); + addSon(result, a); + end + end + end +end; + +function parseTopLevelStmt(var p: TParser): PNode; +begin + result := nil; + while true do begin + case p.tok.tokType of + tkSad: getTok(p); + tkDed, tkInd: begin + parMessage(p, errInvalidIndentation); + break; + end; + tkEof: break; + else begin + result := complexOrSimpleStmt(p); + if result = nil then parMessage(p, errExprExpected, tokToStr(p.tok)); + break + end + end + end +end; + +end. diff --git a/nim/platform.pas b/nim/platform.pas index 9f8d30f60..c2fa711b9 100755 --- a/nim/platform.pas +++ b/nim/platform.pas @@ -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: ':'+''; diff --git a/nim/pnimsyn.pas b/nim/pnimsyn.pas index 724a85a38..bf964fda1 100755 --- a/nim/pnimsyn.pas +++ b/nim/pnimsyn.pas @@ -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 diff --git a/nim/pragmas.pas b/nim/pragmas.pas index fc354cd2c..e8d07d52d 100755 --- a/nim/pragmas.pas +++ b/nim/pragmas.pas @@ -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 diff --git a/nim/ptmplsyn.pas b/nim/ptmplsyn.pas index 2368b22c7..717da6ee0 100755 --- a/nim/ptmplsyn.pas +++ b/nim/ptmplsyn.pas @@ -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; + 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; - p.state := psDirective; - 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; + p: TTmplParser; begin +{@ignore} + FillChar(p, sizeof(p), 0); +{@emit} + p.info := newLineInfo(filename, 0, 0); + p.outp := LLStreamOpen(''); + 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); - if gVerbosity >= 2 then begin - rawMessage(hintCodeBegin); - messageOut(p.outp.s); - rawMessage(hintCodeEnd); - end; - openParser(q, toFilename(p.info), p.outp); - result := ParseModule(q); - closeParser(q); -end; - -procedure openTmplParser(var p: TTmplParser; const filename: string; - inputStream: PLLStream); -begin -{@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 + 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. diff --git a/nim/rnimsyn.pas b/nim/rnimsyn.pas index 1ad1c9408..ec1e9571e 100755 --- a/nim/rnimsyn.pas +++ b/nim/rnimsyn.pas @@ -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]); diff --git a/nim/rodread.pas b/nim/rodread.pas index dc3630a45..457ad6cc2 100755 --- a/nim/rodread.pas +++ b/nim/rodread.pas @@ -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]); diff --git a/nim/rst.pas b/nim/rst.pas index d5cde5c7e..fb29b7d9f 100755 --- a/nim/rst.pas +++ b/nim/rst.pas @@ -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); diff --git a/nim/scanner.pas b/nim/scanner.pas index 05f903622..c03ae9224 100755 --- a/nim/scanner.pas +++ b/nim/scanner.pas @@ -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) diff --git a/nim/semexprs.pas b/nim/semexprs.pas index 43eb9ac6f..91c909dfd 100755 --- a/nim/semexprs.pas +++ b/nim/semexprs.pas @@ -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 := 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 := true + 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 diff --git a/nim/semstmts.pas b/nim/semstmts.pas index b4b72a25b..1ece72023 100755 --- a/nim/semstmts.pas +++ b/nim/semstmts.pas @@ -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; diff --git a/nim/semtempl.pas b/nim/semtempl.pas index 8e37a1ea3..e639960d3 100755 --- a/nim/semtempl.pas +++ b/nim/semtempl.pas @@ -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 diff --git a/nim/semtypes.pas b/nim/semtypes.pas index c0b86be86..e2a0d2185 100755 --- a/nim/semtypes.pas +++ b/nim/semtypes.pas @@ -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); diff --git a/nim/strutils.pas b/nim/strutils.pas index be20b52b2..afd8b4766 100755 --- a/nim/strutils.pas +++ b/nim/strutils.pas @@ -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 diff --git a/nim/syntaxes.pas b/nim/syntaxes.pas index 1068064ce..19035028e 100755 --- a/nim/syntaxes.pas +++ b/nim/syntaxes.pas @@ -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 +function ParseFile(const filename: string): PNode; +var + p: TParsers; + f: TBinaryFile; +begin + 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 - syntaxes: array [TSyntaxes] of TSyntax; + 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; -procedure addSyntax(const s: TSyntax); +function parsePipe(const filename: string; inputStream: PLLStream): PNode; var - len: int; + line: string; + s: PLLStream; + i: int; + q: TParser; begin - len := length(syntaxes); - setLength(syntaxes, len+1); - syntaxes[len] := s; + 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. diff --git a/nim/transf.pas b/nim/transf.pas index d5b0bb6f8..192d76a84 100755 --- a/nim/transf.pas +++ b/nim/transf.pas @@ -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]); diff --git a/nim/wordrecg.pas b/nim/wordrecg.pas index 9bf3de256..c18969877 100755 --- a/nim/wordrecg.pas +++ b/nim/wordrecg.pas @@ -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', diff --git a/rod/nimrod.ini b/rod/nimrod.ini index 6ae5f0156..78b736721 100755 --- a/rod/nimrod.ini +++ b/rod/nimrod.ini @@ -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] diff --git a/tests/sunset.tmpl b/tests/sunset.tmpl index 08fa50f56..6475bac4e 100755 --- a/tests/sunset.tmpl +++ b/tests/sunset.tmpl @@ -1,3 +1,4 @@ +#! stdtmpl #proc sunsetTemplate*(current, ticker, content: string, # tabs: openarray[array[0..1, string]]): string = # result = "" diff --git a/tests/tadrdisc.nim b/tests/tadrdisc.nim new file mode 100755 index 000000000..a7118455f --- /dev/null +++ b/tests/tadrdisc.nim @@ -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 + diff --git a/tests/tbind1.nim b/tests/tbind1.nim new file mode 100755 index 000000000..e7eed3e4f --- /dev/null +++ b/tests/tbind1.nim @@ -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 + diff --git a/tests/tbind2.nim b/tests/tbind2.nim new file mode 100755 index 000000000..41711083f --- /dev/null +++ b/tests/tbind2.nim @@ -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) + diff --git a/tests/tbintre2.nim b/tests/tbintre2.nim new file mode 100755 index 000000000..dedc87705 --- /dev/null +++ b/tests/tbintre2.nim @@ -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 + diff --git a/tests/tcasestm.nim b/tests/tcasestm.nim index 16051ceb8..277b0bab1 100755 --- a/tests/tcasestm.nim +++ b/tests/tcasestm.nim @@ -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 + diff --git a/tests/tcnstseq.nim b/tests/tcnstseq.nim new file mode 100755 index 000000000..4f389bb3b --- /dev/null +++ b/tests/tcnstseq.nim @@ -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 + diff --git a/tests/tcopy.nim b/tests/tcopy.nim index 81d72c7f2..6cb2ec14c 100755 --- a/tests/tcopy.nim +++ b/tests/tcopy.nim @@ -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) diff --git a/tests/tester.nim b/tests/tester.nim index b09200aea..25a6dd428 100755 --- a/tests/tester.nim +++ b/tests/tester.nim @@ -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) diff --git a/tests/titer4.nim b/tests/titer4.nim new file mode 100755 index 000000000..376522482 --- /dev/null +++ b/tests/titer4.nim @@ -0,0 +1,3 @@ + +for x in {'a'..'z'}: #ERROR_MSG iterator within for loop context expected + nil diff --git a/tests/tmultim1.nim b/tests/tmultim1.nim new file mode 100755 index 000000000..5d807e4c9 --- /dev/null +++ b/tests/tmultim1.nim @@ -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 diff --git a/tests/tmultim2.nim b/tests/tmultim2.nim new file mode 100755 index 000000000..bf3b5fd6e --- /dev/null +++ b/tests/tmultim2.nim @@ -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 + + diff --git a/tests/tobjcov.nim b/tests/tobjcov.nim new file mode 100755 index 000000000..da34fcb60 --- /dev/null +++ b/tests/tobjcov.nim @@ -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 + diff --git a/tests/toverl2.nim b/tests/toverl2.nim new file mode 100755 index 000000000..2d1225c6f --- /dev/null +++ b/tests/toverl2.nim @@ -0,0 +1,21 @@ +# Test new overloading resolution rules + +import strutils + +proc toverl2(x: int): string = return $x +proc toverl2(x: bool): string = return $x + +iterator toverl2(x: int): int = + var res = 0 + while res < x: + yield res + inc(res) + +var + pp: proc (x: bool): string = toverl2 +stdout.write(pp(true)) +for x in toverl2(3): + stdout.write(toverl2(x)) +stdout.write("\n") +#OUT true012 + diff --git a/tests/toverprc.nim b/tests/toverprc.nim new file mode 100755 index 000000000..f35528ace --- /dev/null +++ b/tests/toverprc.nim @@ -0,0 +1,25 @@ +# Test overloading of procs when used as function pointers + +import strutils + +proc parseInt(x: float): int = nil +proc parseInt(x: bool): int = nil +proc parseInt(x: float32): int = nil +proc parseInt(x: int8): int = nil +proc parseInt(x: TFile): int = nil +proc parseInt(x: char): int = nil +proc parseInt(x: int16): int = nil + +type + TParseInt = proc (x: string): int + +var + q = TParseInt(parseInt) + p: TParseInt = parseInt + +proc takeParseInt(x: proc (y: string): int): int = + result = x("123") + +echo "Give a list of numbers (separated by spaces): " +var x = stdin.readline.split.each(parseInt).max echo x, " is the maximum!" echo "another number: ", takeParseInt(parseInt) + diff --git a/tests/tposix.nim b/tests/tposix.nim index 87ea3acaf..bf0b49586 100755 --- a/tests/tposix.nim +++ b/tests/tposix.nim @@ -1,13 +1,16 @@ # Test Posix interface -import posix +when not defined(windows): -var - u: Tutsname + import posix -discard uname(u) + var + u: Tutsname + + discard uname(u) + + writeln(stdout, u.sysname) + writeln(stdout, u.nodename) + writeln(stdout, u.release) + writeln(stdout, u.machine) -writeln(stdout, u.sysname) -writeln(stdout, u.nodename) -writeln(stdout, u.release) -writeln(stdout, u.machine) diff --git a/tests/treadx.nim b/tests/treadx.nim new file mode 100755 index 000000000..13acb0514 --- /dev/null +++ b/tests/treadx.nim @@ -0,0 +1,12 @@ +import posix + +var inp = "" +var buf: array[0..10, char] +while true: + var r = read(0, addr(buf), sizeof(buf)-1) + add inp, $buf + if r != sizeof(buf)-1: break + +echo inp +#dafkladskölklödsaf ölksdakölfölksfklwe4iojr389wr 89uweokf sdlkf jweklr jweflksdj fioewjfsdlfsd + diff --git a/tests/trecinca.nim b/tests/trecinca.nim new file mode 100755 index 000000000..d78fee233 --- /dev/null +++ b/tests/trecinca.nim @@ -0,0 +1,5 @@ +# Test recursive includes + +include trecincb #ERROR_MSG recursive dependency: 'tests/trecincb.nim' + +echo "trecina" diff --git a/tests/trecincb.nim b/tests/trecincb.nim new file mode 100755 index 000000000..6191671a1 --- /dev/null +++ b/tests/trecincb.nim @@ -0,0 +1,6 @@ +# Test recursive includes + + +include trecincb #ERROR_MSG recursive dependency: 'tests/trecincb.nim' + +echo "trecinb" diff --git a/tests/treciter.nim b/tests/treciter.nim new file mode 100755 index 000000000..662239285 --- /dev/null +++ b/tests/treciter.nim @@ -0,0 +1,7 @@ +# Test that an error message occurs for a recursive iterator + +iterator myrec(n: int): int = + for x in myrec(n-1): #ERROR_MSG recursive dependency: 'myrec' + yield x + +for x in myrec(10): echo x diff --git a/tests/tseqcon2.nim b/tests/tseqcon2.nim new file mode 100755 index 000000000..6225c3bb1 --- /dev/null +++ b/tests/tseqcon2.nim @@ -0,0 +1,9 @@ +import os + +proc rec_dir(dir: string): seq[string] = + result = @[] + for kind, path in walk_dir(dir): + if kind == pcDirectory: + add(result, rec_dir(path)) + else: + add(result, path) diff --git a/tests/tsidee1.nim b/tests/tsidee1.nim new file mode 100755 index 000000000..3bd520680 --- /dev/null +++ b/tests/tsidee1.nim @@ -0,0 +1,11 @@ + +var + global: int + +proc dontcare(x: int): int = return x + global + +proc SideEffectLyer(x, y: int): int {.noSideEffect.} = #ERROR_MSG 'SideEffectLyer' can have side effects + return x + y + dontcare(x) + +echo SideEffectLyer(1, 3) + diff --git a/tests/tsidee2.nim b/tests/tsidee2.nim new file mode 100755 index 000000000..2eaec01d7 --- /dev/null +++ b/tests/tsidee2.nim @@ -0,0 +1,11 @@ + +var + global: int + +proc dontcare(x: int): int = return x + +proc SideEffectLyer(x, y: int): int {.noSideEffect.} = + return x + y + dontcare(x) + +echo SideEffectLyer(1, 3) #OUT 5 + diff --git a/tests/tsidee3.nim b/tests/tsidee3.nim new file mode 100755 index 000000000..be94192e7 --- /dev/null +++ b/tests/tsidee3.nim @@ -0,0 +1,11 @@ + +var + global: int + +proc dontcare(x: int): int {.noSideEffect.} = return x + +proc noSideEffect(x, y: int, p: proc (a: int): int {.noSideEffect.}): int {.noSideEffect.} = + return x + y + dontcare(x) + +echo noSideEffect(1, 3, dontcare) #OUT 5 + diff --git a/tests/tsidee4.nim b/tests/tsidee4.nim new file mode 100755 index 000000000..55c474d10 --- /dev/null +++ b/tests/tsidee4.nim @@ -0,0 +1,10 @@ + +var + global: int + +proc dontcare(x: int): int = return x + +proc noSideEffect(x, y: int, p: proc (a: int): int {.noSideEffect.}): int {.noSideEffect.} = + return x + y + dontcare(x) + +echo noSideEffect(1, 3, dontcare) #ERROR_MSG type mismatch diff --git a/tests/tvartup.nim b/tests/tvartup.nim new file mode 100755 index 000000000..05b00b207 --- /dev/null +++ b/tests/tvartup.nim @@ -0,0 +1,11 @@ +# Test the new tuple unpacking + +proc divmod(a, b: int): tuple[di, mo: int] = + return (a div b, a mod b) + +var (x, y) = divmod(15, 6) +stdout.write(x) +stdout.write(" ") +stdout.write(y) + +#OUT 2 3 diff --git a/tools/build.tmpl b/tools/build.tmpl index 3ac9252a3..d995ca8c9 100755 --- a/tools/build.tmpl +++ b/tools/build.tmpl @@ -1,4 +1,4 @@ -#subsChar ? +#! stdtmpl(subsChar='?') | standard #proc GenerateBuildScript(c: TConfigData): string = # result = "#! /bin/sh\n# Generated by niminst\n" CC="gcc" diff --git a/tools/deinstall.tmpl b/tools/deinstall.tmpl index b671ae80f..19b31e5b4 100755 --- a/tools/deinstall.tmpl +++ b/tools/deinstall.tmpl @@ -1,4 +1,4 @@ -#subsChar ? +#! stdtmpl(subsChar='?') | standard #proc GenerateDeinstallScript(c: TConfigData): string = # result = "#! /bin/sh\n# Generated by niminst\n" # var proj = c.name.toLower diff --git a/tools/inno.tmpl b/tools/inno.tmpl index 368846d66..cc8583779 100755 --- a/tools/inno.tmpl +++ b/tools/inno.tmpl @@ -1,3 +1,4 @@ +#! stdtmpl | standard #proc GenerateInnoSetup(c: TConfigData): string = # result = "" ; Default Template for NimInst diff --git a/tools/install.tmpl b/tools/install.tmpl index acca8a7fb..bf3bfabc4 100755 --- a/tools/install.tmpl +++ b/tools/install.tmpl @@ -1,4 +1,4 @@ -#subsChar ? +#! stdtmpl(subsChar = '?') | standard #proc GenerateInstallScript(c: TConfigData): string = # result = "#! /bin/sh\n# Generated by niminst\n" # var proj = c.name.toLower diff --git a/tools/niminst.nim b/tools/niminst.nim index b1de0964c..078aba6df 100755 --- a/tools/niminst.nim +++ b/tools/niminst.nim @@ -152,8 +152,8 @@ proc walkDirRecursively(s: var seq[string], root: string) = for k, f in walkDir(root): case k of pcFile, pcLinkToFile: add(s, UnixToNativePath(f)) - of pcDirectory: walkDirRecursively(s, f) - of pcLinkToDirectory: nil + of pcDir: walkDirRecursively(s, f) + of pcLinkToDir: nil proc addFiles(s: var seq[string], patterns: seq[string]) = for p in items(patterns): @@ -339,9 +339,10 @@ proc setupDist(c: var TConfigData) = if c.innoSetup.path.len == 0: c.innoSetup.path = "iscc.exe" var outcmd = if c.outdir.len == 0: "build" else: c.outdir - var cmd = "$# $# /O$# $#" % [c.innoSetup.path, c.innoSetup.flags, outcmd, n] + var cmd = "$# $# /O$# $#" % [quoteIfContainsWhite(c.innoSetup.path), + c.innoSetup.flags, outcmd, n] Echo(cmd) - if executeShellCommand(cmd) == 0: + if execShellCmd(cmd) == 0: removeFile(n) else: quit("External program failed") diff --git a/tools/nimweb.nim b/tools/nimweb.nim index ef59c2fb9..bc074af74 100755 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -14,7 +14,7 @@ type TKeyValPair = tuple[key, val: string] TConfigData = object of TObject tabs, links: seq[TKeyValPair] - doc, srcdoc, webdoc: seq[string] + doc, srcdoc, webdoc, pdf: seq[string] authors, projectName, projectTitle, logo, infile, outdir, ticker: string vars: PStringTable nimrodArgs: string @@ -25,9 +25,10 @@ proc initConfigData(c: var TConfigData) = c.doc = @[] c.srcdoc = @[] c.webdoc = @[] + c.pdf = @[] c.infile = "" c.outdir = "" - c.nimrodArgs = "" + c.nimrodArgs = "--hint[Conf]:off " c.authors = "" c.projectTitle = "" c.projectName = "" @@ -65,7 +66,7 @@ proc parseCmdLine(c: var TConfigData) = case kind of cmdArgument: c.infile = addFileExt(key, "ini") - c.nimrodArgs = cmdLineRest(p) + c.nimrodArgs.add(cmdLineRest(p)) break of cmdLongOption, cmdShortOption: case normalize(key) @@ -86,8 +87,8 @@ proc walkDirRecursively(s: var seq[string], root, ext: string) = of pcFile, pcLinkToFile: if cmpIgnoreCase(ext, splitFile(f).ext) == 0: add(s, f) - of pcDirectory: walkDirRecursively(s, f, ext) - of pcLinkToDirectory: nil + of pcDir: walkDirRecursively(s, f, ext) + of pcLinkToDir: nil proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) = for p in items(patterns): @@ -132,6 +133,7 @@ proc parseIniFile(c: var TConfigData) = of "documentation": case normalize(k.key) of "doc": addFiles(c.doc, "doc", ".txt", split(v, {';'})) + of "pdf": addFiles(c.pdf, "doc", ".txt", split(v, {';'})) of "srcdoc": addFiles(c.srcdoc, "lib", ".nim", split(v, {';'})) of "webdoc": addFiles(c.webdoc, "lib", ".nim", split(v, {';'})) else: quit(errorStr(p, "unknown variable: " & k.key)) @@ -167,11 +169,24 @@ proc buildDoc(c: var TConfigData, destPath: string) = [c.nimrodArgs, destPath]) proc buildPdfDoc(c: var TConfigData, destPath: string) = - for d in items(c.doc): - Exec("nimrod rst2tex $# $#" % [c.nimrodArgs, d]) - - - + if os.execShellCmd("pdflatex -version") != 0: + echo "pdflatex not found; not PDF documentation generated" + else: + for d in items(c.pdf): + Exec("nimrod rst2tex $# $#" % [c.nimrodArgs, d]) + # call LaTeX twice to get cross references right: + Exec("pdflatex " & changeFileExt(d, "tex")) + Exec("pdflatex " & changeFileExt(d, "tex")) + # delete all the crappy temporary files: + var pdf = extractFileTrunk(d) & ".pdf" + moveFile(destPath / pdf, pdf) + removeFile(changeFileExt(pdf, "aux")) + if existsFile(changeFileExt(pdf, "toc")): + removeFile(changeFileExt(pdf, "toc")) + removeFile(changeFileExt(pdf, "log")) + removeFile(changeFileExt(pdf, "out")) + removeFile(changeFileExt(d, "tex")) + proc buildAddDoc(c: var TConfigData, destPath: string) = # build additional documentation (without the index): for d in items(c.webdoc): @@ -205,6 +220,7 @@ proc main(c: var TConfigData) = buildAddDoc(c, "web/upload") buildDoc(c, "web/upload") buildDoc(c, "doc") + #buildPdfDoc(c, "doc") var c: TConfigData initConfigData(c) diff --git a/tools/sunset.tmpl b/tools/sunset.tmpl index 44f7ac486..18f8b74d5 100755 --- a/tools/sunset.tmpl +++ b/tools/sunset.tmpl @@ -1,3 +1,4 @@ +#! stdtmpl | standard #proc generateHTMLPage(c: var TConfigData, currentTab, content: string): string = # result = "" <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> diff --git a/web/download.txt b/web/download.txt index c1a70bd63..2bb131227 100755 --- a/web/download.txt +++ b/web/download.txt @@ -3,8 +3,8 @@ Here you can download the latest version of the Nimrod Compiler. Please choose your platform: -* source-based installation: `<download/nimrod_0.8.0.zip>`_ -* installer for Windows XP/Vista (i386): `<download/nimrod_0.8.0.exe>`_ +* source-based installation: `<download/nimrod_0.8.2.zip>`_ +* installer for Windows XP/Vista (i386): `<download/nimrod_0.8.2.exe>`_ (includes GCC and everything else you need) The source-based installation has been tested on these systems: diff --git a/web/index.txt b/web/index.txt index e0ca80eb3..dc9f9133a 100755 --- a/web/index.txt +++ b/web/index.txt @@ -75,9 +75,10 @@ Nimrod is expressive Nimrod is elegant ================= +* Macros can use the imperative paradigm to construct parse trees. Nimrod + does not require a different coding style for meta programming. * Macros cannot change Nimrod's syntax because there is no need for it. Nimrod's syntax is flexible enough. -* Yet Nimrod can be parsed with an LL(1) parser. * Statements are grouped by indentation but can span multiple lines. Indentation must not contain tabulators so the compiler always sees the code the same way as you do. @@ -103,9 +104,8 @@ Nimrod plays nice with others Roadmap to 1.0 ============== -Version 0.8.4 +Version 0.8.x * general expressions as generic parameters - * multimethods Version 0.9.0 * closures and anonymous procs @@ -120,6 +120,6 @@ Planned features beyond 1.0 =========================== * Threading with a transactional memory modell (the type system may be - enhanced to support extensive compile-time checks for this) -* Recursive iterators/coroutines -* Other code generators: LLVM, EcmaScript + enhanced to support extensive compile-time checks for this). +* Recursive iterators/coroutines. +* Other code generators: LLVM, EcmaScript. diff --git a/web/news.txt b/web/news.txt index 65b3a5b3c..ea7c23de1 100755 --- a/web/news.txt +++ b/web/news.txt @@ -2,9 +2,70 @@ News ==== +2009-10-21 Version 0.8.2 released +================================= + +Bugfixes +-------- +- Passing ``--hint[X]:off`` or ``--warning[X]:off`` as command line arguments + to the compiler now works. +- Default parameters for templates now work. +- Fixed security issue in ``system.copy``. +- String case without an ``else`` part generated wrong code. +- Various fixes for the ``osproc`` module; it now works on Windows. + + +Additions +--------- +- Implemented multi-methods. +- The compiler now detects the number of available processors and + executes the C compiler concurrently. This can be adjusted by the + ``--parallel_build: number_of_processors`` command line switch. +- PEG-based pattern matching via the `pegs <pegs.html>`_ module. +- Added ``system.definedInScope``. +- Added ``system.accumulateResult``. +- Added ``os.walkDirRec``. +- Added ``osproc.countProcessors``. +- Added ``osproc.execProcesses``. +- The configuration system now supports ``$cc.options.linker``. +- Merged ``expr`` and ``typeDesc`` in the grammar/parser: this will allow for + anonymous procs without ``lambda``. +- Many scripts have no need for a GC: The new ``--gc:none`` command line switch + can be used to exclude the GC from the executable. + -2009-09-12 Date Version 0.8.0 released -====================================== +Changes affecting backwards compability +--------------------------------------- +- Taking the address of an object discriminant is now invalid. +- Passing a proc to a proc var is only allowed for procs marked with + ``procvar`` or a calling convention != ``nimcall``. For now this only causes + a warning, but this will turn into an error soon. +- ``system.&`` and ``system.add`` now receive ``openArray`` instead of ``seq`` + parameters. +- Removed ``strutils.findSubStr`` and ``strutils.findChars``. They have been + deprecated for 3 versions. +- The unicode module uses a distinct type for ``TRune``. +- Deprecated ``strutils.toString``: use ``$`` instead. +- Deprecated ``os.splitPath``: use ``os.splitPath`` that returns a tuple + instead. +- Deprecated ``os.extractDir``: use ``splitFile(path).dir`` instead. +- Deprecated ``os.splitFilename``: use ``splitFile`` instead. +- Deprecated ``os.extractFileExt``: use ``splitFile`` instead. +- Deprecated ``os.extractFileTrunk``: use ``splitFile`` instead. +- Deprecated ``os.appendFileExt``: use ``os.addFileExt`` instead. +- Deprecated ``os.executeShellCommand``: use ``os.execShellCmd`` instead. +- Deprecated ``os.iterOverEnvironment``: use ``os.envPairs`` instead. +- Deprecated ``parseopt.init``: use ``parseopt.initOptParser`` instead. +- Deprecated ``parseopt.getRestOfCommandLine``: use ``parseopt.cmdLineRest`` + instead. +- Deprecated ``os.pcDirectory``: use ``os.pcDir`` instead. +- Deprecated ``os.pcLinkToDirectory``: use ``os.pcLinkToDir`` instead. +- Deprecated ``osproc.executeProcess``: use ``osproc.execProcess`` instead. +- Deprecated ``osproc.executeCommand``: use ``osproc.execCmd`` instead. + + +2009-09-12 Version 0.8.0 released +================================= Bugfixes -------- diff --git a/web/nimrod.ini b/web/nimrod.ini index 1f58a0915..4920eda06 100755 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -22,6 +22,7 @@ file: ticker [Documentation] doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview" +pdf: "manual;lib;tut1;tut2;nimrodc" srcdoc: "system.nim;pure/os;pure/strutils;pure/regexprs;pure/math" srcdoc: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib" srcdoc: "pure/parseopt;pure/hashes;pure/strtabs;pure/lexbase" diff --git a/web/ticker.txt b/web/ticker.txt index 61e5e40fd..4f33e4ce7 100755 --- a/web/ticker.txt +++ b/web/ticker.txt @@ -1,6 +1,9 @@ +| `2009-10-21`:newsdate: +| Nimrod version 0.8.2 has been released! + Get it `here <./download.html>`_. + | `2009-09-12`:newsdate: | Nimrod version 0.8.0 has been released! - Get it `here <./download.html>`_. | `2009-06-08`:newsdate: | Nimrod version 0.7.10 has been released! |