mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 05:50:30 +00:00
version 0.8.2
This commit is contained in:
12
boot.nim
12
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):
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
{'errCannotEvalXBecauseIncompletelyDefined':
|
||||
"cannot evalutate '$1' because type is not defined completely"},
|
||||
{'errChrExpectsRange0_255': "'chr' expects an int in the range 0..255"},
|
||||
{'errDotRequiresRecordOrObjectType': "'.' requires a record or object type"},
|
||||
{'errDynlibRequiresExportc': "'dynlib' requires 'exportc'"},
|
||||
{'errUndeclaredFieldX': "undeclared field: '$1'"},
|
||||
{'errNilAccess': 'attempt to access a nil address'},
|
||||
{'errIndexOutOfBounds': 'index out of bounds'},
|
||||
@@ -152,7 +152,7 @@
|
||||
{'errButExpectedX': "but expected '$1'"},
|
||||
{'errAmbiguousCallXYZ': 'ambiguous call; both $1 and $2 match for: $3'},
|
||||
{'errWrongNumberOfArguments': 'wrong number of arguments'},
|
||||
{'errInlineProcHasNoAddress': 'an inline proc has no address'},
|
||||
{'errXCannotBePassedToProcVar': "'$1' cannot be passed to a procvar"},
|
||||
{'errXCannotBeInParamDecl': '$1 cannot be declared in parameter declaration'},
|
||||
{'errPragmaOnlyInHeaderOfProc':
|
||||
'pragmas are only in the header of a proc allowed'},
|
||||
@@ -186,7 +186,6 @@
|
||||
{'errATypeHasNoValue': 'a type has no value'},
|
||||
{'errXisNoType': "invalid type: '$1'"},
|
||||
{'errCircumNeedsPointer': "'^' needs a pointer or reference type"},
|
||||
{'errInvalidContextForBuiltinX': "invalid context for builtin '$1'"},
|
||||
{'errInvalidExpression': 'invalid expression'},
|
||||
{'errInvalidExpressionX': "invalid expression: '$1'"},
|
||||
{'errEnumHasNoValueX': "enum has no value '$1'"},
|
||||
@@ -250,6 +249,8 @@
|
||||
{'warnUnknownSubstitutionX': "unknown substitution '$1'"},
|
||||
{'warnLanguageXNotSupported': "language '$1' not supported"},
|
||||
{'warnCommentXIgnored': "comment '$1' ignored"},
|
||||
{'warnXisPassedToProcVar': "'$1' is passed to a procvar; deprecated"},
|
||||
|
||||
# user warning message:
|
||||
{'warnUser': '$1'},
|
||||
|
||||
|
||||
3
doc/apis.txt
Normal file → Executable file
3
doc/apis.txt
Normal file → Executable file
@@ -66,4 +66,7 @@ function func
|
||||
coordinate coord
|
||||
rectangle rect
|
||||
point point
|
||||
symbol sym
|
||||
identifier ident
|
||||
indentation indent
|
||||
------------------- ------------ --------------------------------------
|
||||
|
||||
41
doc/effects.txt
Executable file
41
doc/effects.txt
Executable file
@@ -0,0 +1,41 @@
|
||||
=====================================================================
|
||||
Side effects in Nimrod
|
||||
=====================================================================
|
||||
|
||||
Note: Side effects are implicit produced values! Maybe they should be
|
||||
explicit like in Haskell?
|
||||
|
||||
|
||||
The idea is that side effects and partial evaluation belong together:
|
||||
Iff a proc is side effect free and all its argument are evaluable at
|
||||
compile time, it can be evaluated by the compiler. However, really
|
||||
difficult is the ``newString`` proc: If it is simply wrapped, it
|
||||
should not be evaluated at compile time! On other occasions it can
|
||||
and should be evaluted:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc toUpper(s: string): string =
|
||||
result = newString(len(s))
|
||||
for i in 0..len(s) - 1:
|
||||
result[i] = toUpper(s[i])
|
||||
|
||||
No, it really can always be evaluated. The code generator should transform
|
||||
``s = "\0\0\0..."`` back into ``s = newString(...)``.
|
||||
|
||||
|
||||
``new`` cannot be evaluated at compile time either.
|
||||
|
||||
|
||||
Raise statement
|
||||
===============
|
||||
|
||||
It is impractical to consider ``raise`` a statement with side effects.
|
||||
|
||||
|
||||
Solution
|
||||
========
|
||||
|
||||
Being side effect free does not suffice for compile time evaluation. However,
|
||||
the evaluator can attempt to evaluate at compile time.
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ nimrod main module: parses the command line and calls
|
||||
``main.MainCommand``
|
||||
main implements the top-level command dispatching
|
||||
nimconf implements the config file reader
|
||||
|
||||
syntaxes dispatcher for the different parsers and filters
|
||||
ptmplsyn standard template filter (``#! stdtempl``)
|
||||
lexbase buffer handling of the lexical analyser
|
||||
scanner lexical analyser
|
||||
pnimsyn Nimrod's parser
|
||||
|
||||
232
doc/filters.txt
Executable file
232
doc/filters.txt
Executable file
@@ -0,0 +1,232 @@
|
||||
===================
|
||||
Parsers and Filters
|
||||
===================
|
||||
|
||||
.. contents::
|
||||
|
||||
The Nimrod compiler contains multiple parsers. (The standard is
|
||||
indentation-based.) Two others are available: The `braces`:idx: parser and the
|
||||
`endX`:idx: parser. Both parsers use the same lexer as the standard parser.
|
||||
|
||||
To use a different parser for a source file the *shebang* notation is used:
|
||||
|
||||
.. code-block:: nimrod
|
||||
#! braces
|
||||
if (x == 10) {
|
||||
echo "x is ten"
|
||||
} else {
|
||||
echo "x isn't ten"
|
||||
}
|
||||
|
||||
The special ``#!`` comment for specifying a parser needs to be in the first
|
||||
line with no leading whitespace, unless an UNIX shebang line is used. Then the
|
||||
parser shebang can occur in the second line:
|
||||
|
||||
.. code-block:: nimrod
|
||||
#! /usr/bin/env nimrod c -r
|
||||
#! braces
|
||||
if (x == 10) {
|
||||
echo "x is ten"
|
||||
} else {
|
||||
echo "x isn't ten"
|
||||
}
|
||||
|
||||
An UNIX shebang line is defined by the pattern ``'#!' \s* '/' .*``
|
||||
(``#!`` followed by optional whitespace followed by ``/``).
|
||||
|
||||
|
||||
Filters
|
||||
=======
|
||||
|
||||
Nimrod's shebang also supports the invokation of `source filters`:idx: before
|
||||
the source code file is passed to the parser::
|
||||
|
||||
#! stdtmpl(subsChar = '$', metaChar = '#')
|
||||
#proc generateXML(name, age: string): string =
|
||||
# result = ""
|
||||
<xml>
|
||||
<name>$name</name>
|
||||
<age>$age</age>
|
||||
</xml>
|
||||
|
||||
Filters transform the input character stream to an in-memory output stream.
|
||||
They are used to provide templating systems or preprocessors.
|
||||
|
||||
As the example shows, passing arguments to a filter (or parser) can be done
|
||||
just like an ordinary procedure call with named or positional arguments. The
|
||||
available parameters depend on the invoked filter/parser.
|
||||
|
||||
|
||||
Pipe operator
|
||||
-------------
|
||||
|
||||
Filters and parsers can be combined with the ``|`` `pipe operator`:idx:. Only
|
||||
the last operand can be a parser because a parser returns an abstract syntax
|
||||
tree which a filter cannot process::
|
||||
|
||||
#! strip(startswith="<") | stdtmpl | standard
|
||||
#proc generateXML(name, age: string): string =
|
||||
# result = ""
|
||||
<xml>
|
||||
<name>$name</name>
|
||||
<age>$age</age>
|
||||
</xml>
|
||||
|
||||
|
||||
Available filters
|
||||
=================
|
||||
|
||||
**Hint:** With ``--verbosity:2`` (or higher) Nimrod lists the processed code
|
||||
after each filter application.
|
||||
|
||||
Replace filter
|
||||
--------------
|
||||
|
||||
The `replace`:idx: filter replaces substrings in each line.
|
||||
|
||||
Parameters and their defaults:
|
||||
|
||||
``sub: string = ""``
|
||||
the substring that is searched for
|
||||
|
||||
``by: string = ""``
|
||||
the string the substring is replaced with
|
||||
|
||||
|
||||
Strip filter
|
||||
------------
|
||||
|
||||
The `strip`:idx: filter simply removes leading and trailing whitespace from
|
||||
each line.
|
||||
|
||||
Parameters and their defaults:
|
||||
|
||||
``startswith: string = ""``
|
||||
strip only the lines that start with *startswith* (ignoring leading
|
||||
whitespace). If empty every line is stripped.
|
||||
|
||||
``leading: bool = true``
|
||||
strip leading whitespace
|
||||
|
||||
``trailing: bool = true``
|
||||
strip trailing whitespace
|
||||
|
||||
|
||||
StdTmpl filter
|
||||
--------------
|
||||
|
||||
The `stdtmpl`:idx: filter provides a simple templating engine for Nimrod. The
|
||||
filter uses a line based parser: Lines prefixed with a *meta character*
|
||||
(default: ``#``) contain Nimrod code, other lines are verbatim. Because
|
||||
indentation-based parsing is not suited for a templating engine, control flow
|
||||
statements need ``end X`` delimiters.
|
||||
|
||||
Parameters and their defaults:
|
||||
|
||||
``metaChar: char = '#'``
|
||||
prefix for a line that contains Nimrod code
|
||||
|
||||
``subsChar: char = '$'``
|
||||
prefix for a Nimrod expression within a template line
|
||||
|
||||
``conc: string = " & "``
|
||||
the operation for concatenation
|
||||
|
||||
``emit: string = "result.add"``
|
||||
the operation to emit a string literal
|
||||
|
||||
``toString: string = "$"``
|
||||
the operation that is applied to each expression
|
||||
|
||||
Example::
|
||||
|
||||
#! stdtmpl | standard
|
||||
#proc generateHTMLPage(title, currentTab, content: string,
|
||||
# tabs: openArray[string]): string =
|
||||
# result = ""
|
||||
<head><title>$title</title></head>
|
||||
<body>
|
||||
<div id="menu">
|
||||
<ul>
|
||||
#for tab in items(tabs):
|
||||
#if currentTab == tab:
|
||||
<li><a id="selected"
|
||||
#else:
|
||||
<li><a
|
||||
#end if
|
||||
href="${tab}.html">$tab</a></li>
|
||||
#end for
|
||||
</ul>
|
||||
</div>
|
||||
<div id="content">
|
||||
$content
|
||||
A dollar: $$.
|
||||
</div>
|
||||
</body>
|
||||
|
||||
The filter transforms this into:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc generateHTMLPage(title, currentTab, content: string,
|
||||
tabs: openArray[string]): string =
|
||||
result = ""
|
||||
result.add("<head><title>" & $(title) & "</title></head>\n" &
|
||||
"<body>\n" &
|
||||
" <div id=\"menu\">\n" &
|
||||
" <ul>\n")
|
||||
for tab in items(tabs):
|
||||
if currentTab == tab:
|
||||
result.add(" <li><a id=\"selected\" \n")
|
||||
else:
|
||||
result.add(" <li><a\n")
|
||||
#end
|
||||
result.add(" href=\"" & $(tab) & ".html\">" & $(tab) & "</a></li>\n")
|
||||
#end
|
||||
result.add(" </ul>\n" &
|
||||
" </div>\n" &
|
||||
" <div id=\"content\">\n" &
|
||||
" " & $(content) & "\n" &
|
||||
" A dollar: $.\n" &
|
||||
" </div>\n" &
|
||||
"</body>\n")
|
||||
|
||||
|
||||
Each line that does not start with the meta character (ignoring leading
|
||||
whitespace) is converted to a string literal that is added to ``result``.
|
||||
|
||||
The substitution character introduces a Nimrod expression *e* within the
|
||||
string literal. *e* is converted to a string with the *toString* operation
|
||||
which defaults to ``$``. For strong type checking, set ``toString`` to the
|
||||
empty string. *e* must match this PEG pattern::
|
||||
|
||||
e <- [a-zA-Z\128-\255][a-zA-Z0-9\128-\255_.]* / '{' x '}'
|
||||
x <- '{' x+ '}' / [^}]*
|
||||
|
||||
To produce a single substitution character it has to be doubled: ``$$``
|
||||
produces ``$``.
|
||||
|
||||
The template engine is quite flexible. It is easy to produce a procedure that
|
||||
writes the template code directly to a file::
|
||||
|
||||
#! stdtmpl(emit="f.write") | standard
|
||||
#proc writeHTMLPage(f: TFile, title, currentTab, content: string,
|
||||
# tabs: openArray[string]) =
|
||||
<head><title>$title</title></head>
|
||||
<body>
|
||||
<div id="menu">
|
||||
<ul>
|
||||
#for tab in items(tabs):
|
||||
#if currentTab == tab:
|
||||
<li><a id="selected"
|
||||
#else:
|
||||
<li><a
|
||||
#end if
|
||||
href="${tab}.html" title = "$title - $tab">$tab</a></li>
|
||||
#end for
|
||||
</ul>
|
||||
</div>
|
||||
<div id="content">
|
||||
$content
|
||||
A dollar: $$.
|
||||
</div>
|
||||
</body>
|
||||
179
doc/gramcurl.txt
Executable file
179
doc/gramcurl.txt
Executable file
@@ -0,0 +1,179 @@
|
||||
module ::= stmt*
|
||||
|
||||
comma ::= ',' [COMMENT] [IND]
|
||||
operator ::= OP0 | OR | XOR | AND | OP3 | OP4 | OP5 | OP6 | OP7
|
||||
| 'is' | 'isnot' | 'in' | 'notin'
|
||||
| 'div' | 'mod' | 'shl' | 'shr' | 'not'
|
||||
|
||||
prefixOperator ::= OP0 | OP3 | OP4 | OP5 | OP6 | OP7 | 'not'
|
||||
|
||||
optInd ::= [COMMENT] [IND]
|
||||
|
||||
|
||||
lowestExpr ::= orExpr (OP0 optInd orExpr)*
|
||||
orExpr ::= andExpr (OR | 'xor' optInd andExpr)*
|
||||
andExpr ::= cmpExpr ('and' optInd cmpExpr)*
|
||||
cmpExpr ::= ampExpr (OP3 | 'is' | 'isnot' | 'in' | 'notin' optInd ampExpr)*
|
||||
ampExpr ::= plusExpr (OP4 optInd plusExpr)*
|
||||
plusExpr ::= mulExpr (OP5 optInd mulExpr)*
|
||||
mulExpr ::= dollarExpr (OP6 | 'div' | 'mod' | 'shl' | 'shr' optInd dollarExpr)*
|
||||
dollarExpr ::= primary (OP7 optInd primary)*
|
||||
|
||||
indexExpr ::= '..' [expr] | expr ['=' expr | '..' expr]
|
||||
|
||||
castExpr ::= 'cast' '[' optInd typeDesc [SAD] ']' '(' optInd expr [SAD] ')'
|
||||
addrExpr ::= 'addr' '(' optInd expr ')'
|
||||
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')'
|
||||
| '[' ']' | '=' | literal)+ '`'
|
||||
| IDENT
|
||||
|
||||
primaryPrefix ::= (prefixOperator | 'bind') optInd
|
||||
primarySuffix ::= '.' optInd symbol
|
||||
| '(' optInd namedExprList [SAD] ')'
|
||||
| '[' optInd [indexExpr (comma indexExpr)* [comma]] [SAD] ']'
|
||||
| '^'
|
||||
| pragma
|
||||
|
||||
primary ::= primaryPrefix* (symbol | constructor | castExpr | addrExpr)
|
||||
primarySuffix*
|
||||
|
||||
literal ::= INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
|
||||
| FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
|
||||
| STR_LIT | RSTR_LIT | TRIPLESTR_LIT
|
||||
| CHAR_LIT
|
||||
| NIL
|
||||
|
||||
constructor ::= literal
|
||||
| '[' optInd colonExprList [SAD] ']'
|
||||
| '{' optInd sliceExprList [SAD] '}'
|
||||
| '(' optInd colonExprList [SAD] ')'
|
||||
|
||||
colonExpr ::= expr [':' expr]
|
||||
colonExprList ::= [colonExpr (comma colonExpr)* [comma]]
|
||||
|
||||
namedExpr ::= expr ['=' expr]
|
||||
namedExprList ::= [namedExpr (comma namedExpr)* [comma]]
|
||||
|
||||
sliceExpr ::= expr ['..' expr]
|
||||
sliceExprList ::= [sliceExpr (comma sliceExpr)* [comma]]
|
||||
|
||||
exprOrType ::= lowestExpr
|
||||
| 'if' '(' expr ')' expr ('elif' '(' expr ')' expr)* 'else' expr
|
||||
| 'var' exprOrType
|
||||
| 'ref' exprOrType
|
||||
| 'ptr' exprOrType
|
||||
| 'type' exprOrType
|
||||
| 'tuple' tupleDesc
|
||||
|
||||
expr ::= exprOrType
|
||||
| 'proc' paramList [pragma] ['=' stmt]
|
||||
|
||||
qualifiedIdent ::= symbol ['.' symbol]
|
||||
|
||||
typeDesc ::= exprOrType
|
||||
| 'proc' paramList [pragma]
|
||||
|
||||
macroStmt ::= '{' [stmt] '}' ('of' [sliceExprList] stmt
|
||||
|'elif' '(' expr ')' stmt
|
||||
|'except' '(' exceptList ')' stmt )*
|
||||
['else' stmt]
|
||||
|
||||
simpleStmt ::= returnStmt
|
||||
| yieldStmt
|
||||
| discardStmt
|
||||
| raiseStmt
|
||||
| breakStmt
|
||||
| continueStmt
|
||||
| pragma
|
||||
| importStmt
|
||||
| fromStmt
|
||||
| includeStmt
|
||||
| exprStmt
|
||||
complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
|
||||
| blockStmt | asmStmt
|
||||
| procDecl | iteratorDecl | macroDecl | templateDecl | methodDecl
|
||||
| constSection | typeSection | whenStmt | varSection
|
||||
|
||||
stmt ::= simpleStmt
|
||||
| indPush (complexStmt | simpleStmt) (';' (complexStmt | simpleStmt))*
|
||||
DED indPop
|
||||
|
||||
exprStmt ::= lowestExpr ['=' expr | [expr (comma expr)*] [macroStmt]]
|
||||
returnStmt ::= 'return' [expr]
|
||||
yieldStmt ::= 'yield' expr
|
||||
discardStmt ::= 'discard' expr
|
||||
raiseStmt ::= 'raise' [expr]
|
||||
breakStmt ::= 'break' [symbol]
|
||||
continueStmt ::= 'continue'
|
||||
ifStmt ::= 'if' '(' expr ')' stmt ('elif' '(' expr ')' stmt)* ['else' stmt]
|
||||
whenStmt ::= 'when' '(' expr ')' stmt ('elif' '(' expr ')' stmt)* ['else' stmt]
|
||||
caseStmt ::= 'case' '(' expr ')' ('of' sliceExprList ':' stmt)*
|
||||
('elif' '(' expr ')' stmt)*
|
||||
['else' stmt]
|
||||
whileStmt ::= 'while' '(' expr ')' stmt
|
||||
forStmt ::= 'for' '(' symbol (comma symbol)* 'in' expr ['..' expr] ')' stmt
|
||||
exceptList ::= [qualifiedIdent (comma qualifiedIdent)*]
|
||||
|
||||
tryStmt ::= 'try' stmt
|
||||
('except' '(' exceptList ')' stmt)*
|
||||
['finally' stmt]
|
||||
asmStmt ::= 'asm' [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
|
||||
blockStmt ::= 'block' [symbol] stmt
|
||||
filename ::= symbol | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
|
||||
importStmt ::= 'import' filename (comma filename)*
|
||||
includeStmt ::= 'include' filename (comma filename)*
|
||||
fromStmt ::= 'from' filename 'import' symbol (comma symbol)*
|
||||
|
||||
pragma ::= '{.' optInd (colonExpr [comma])* [SAD] ('.}' | '}')
|
||||
|
||||
param ::= symbol (comma symbol)* (':' typeDesc ['=' expr] | '=' expr)
|
||||
paramList ::= ['(' [param (comma param)*] [SAD] ')'] [':' typeDesc]
|
||||
|
||||
genericParam ::= symbol [':' typeDesc] ['=' expr]
|
||||
genericParams ::= '[' genericParam (comma genericParam)* [SAD] ']'
|
||||
|
||||
|
||||
routineDecl := symbol ['*'] [genericParams] paramList [pragma] ['=' stmt]
|
||||
procDecl ::= 'proc' routineDecl
|
||||
macroDecl ::= 'macro' routineDecl
|
||||
iteratorDecl ::= 'iterator' routineDecl
|
||||
templateDecl ::= 'template' routineDecl
|
||||
methodDecl ::= 'method' routineDecl
|
||||
|
||||
colonAndEquals ::= [':' typeDesc] '=' expr
|
||||
|
||||
constDecl ::= symbol ['*'] [pragma] colonAndEquals ';' [COMMENT]
|
||||
constSection ::= 'const' [COMMENT] (constDecl | '{' constDecl+ '}')
|
||||
|
||||
typeDef ::= typeDesc | objectDef | enumDef | 'distinct' typeDesc
|
||||
|
||||
objectField ::= symbol ['*'] [pragma]
|
||||
objectIdentPart ::= objectField (comma objectField)* ':' typeDesc
|
||||
[COMMENT|IND COMMENT]
|
||||
|
||||
objectWhen ::= 'when' expr ':' [COMMENT] objectPart
|
||||
('elif' expr ':' [COMMENT] objectPart)*
|
||||
['else' ':' [COMMENT] objectPart]
|
||||
objectCase ::= 'case' expr ':' typeDesc [COMMENT]
|
||||
('of' sliceExprList ':' [COMMENT] objectPart)*
|
||||
['else' ':' [COMMENT] objectPart]
|
||||
|
||||
objectPart ::= objectWhen | objectCase | objectIdentPart | 'nil'
|
||||
| indPush objectPart (SAD objectPart)* DED indPop
|
||||
tupleDesc ::= '[' optInd [param (comma param)*] [SAD] ']'
|
||||
|
||||
objectDef ::= 'object' [pragma] ['of' typeDesc] objectPart
|
||||
enumField ::= symbol ['=' expr]
|
||||
enumDef ::= 'enum' ['of' typeDesc] (enumField [comma] [COMMENT | IND COMMENT])+
|
||||
|
||||
typeDecl ::= COMMENT
|
||||
| symbol ['*'] [genericParams] ['=' typeDef] [COMMENT | IND COMMENT]
|
||||
|
||||
typeSection ::= 'type' indPush typeDecl (SAD typeDecl)* DED indPop
|
||||
|
||||
colonOrEquals ::= ':' typeDesc ['=' expr] | '=' expr
|
||||
varField ::= symbol ['*'] [pragma]
|
||||
varPart ::= symbol (comma symbol)* colonOrEquals [COMMENT | IND COMMENT]
|
||||
varSection ::= 'var' (varPart
|
||||
| indPush (COMMENT|varPart)
|
||||
(SAD (COMMENT|varPart))* DED indPop)
|
||||
@@ -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)*
|
||||
|
||||
@@ -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``.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -72,8 +72,7 @@ New Pragmas and Options
|
||||
-----------------------
|
||||
|
||||
Because Nimrod generates C code it needs some "red tape" to work properly.
|
||||
Thus lots of options and pragmas for tweaking the generated C code are
|
||||
available.
|
||||
Lots of options and pragmas for tweaking the generated C code are available.
|
||||
|
||||
Importc Pragma
|
||||
~~~~~~~~~~~~~~
|
||||
@@ -137,7 +136,7 @@ and instead the generated code should contain an ``#include``:
|
||||
PFile {.importc: "FILE*", header: "<stdio.h>".} = distinct pointer
|
||||
# import C's FILE* type; Nimrod will treat it as a new pointer type
|
||||
|
||||
The ``header`` pragma expects always a string constant. The string contant
|
||||
The ``header`` pragma always expects a string constant. The string contant
|
||||
contains the header file: As usual for C, a system header file is enclosed
|
||||
in angle brackets: ``<>``. If no angle brackets are given, Nimrod
|
||||
encloses the header file in ``""`` in the generated C code.
|
||||
@@ -145,9 +144,9 @@ encloses the header file in ``""`` in the generated C code.
|
||||
|
||||
Varargs Pragma
|
||||
~~~~~~~~~~~~~~
|
||||
The `varargs`:idx: pragma can be applied to procedures only. It tells Nimrod
|
||||
that the proc can take a variable number of parameters after the last
|
||||
specified parameter. Nimrod string values will be converted to C
|
||||
The `varargs`:idx: pragma can be applied to procedures only (and procedure
|
||||
types). It tells Nimrod that the proc can take a variable number of parameters
|
||||
after the last specified parameter. Nimrod string values will be converted to C
|
||||
strings automatically:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
@@ -218,7 +217,7 @@ collector to not consider objects of this type as part of a cycle:
|
||||
data: string
|
||||
|
||||
In the example a tree structure is declared with the ``TNode`` type. Note that
|
||||
the type definition is recursive thus the GC has to assume that objects of
|
||||
the type definition is recursive and the GC has to assume that objects of
|
||||
this type may form a cyclic graph. The ``acyclic`` pragma passes the
|
||||
information that this cannot happen to the GC. If the programmer uses the
|
||||
``acyclic`` pragma for data types that are in reality cyclic, the GC may leak
|
||||
|
||||
180
doc/pegdocs.txt
Executable file
180
doc/pegdocs.txt
Executable file
@@ -0,0 +1,180 @@
|
||||
PEG syntax and semantics
|
||||
========================
|
||||
|
||||
A PEG (Parsing expression grammar) is a simple deterministic grammar, that can
|
||||
be directly used for parsing. The current implementation has been designed as
|
||||
a more powerful replacement for regular expressions. UTF-8 is supported.
|
||||
|
||||
The notation used for a PEG is similar to that of EBNF:
|
||||
|
||||
=============== ============================================================
|
||||
notation meaning
|
||||
=============== ============================================================
|
||||
``A / ... / Z`` Ordered choice: Apply expressions `A`, ..., `Z`, in this
|
||||
order, to the text ahead, until one of them succeeds and
|
||||
possibly consumes some text. Indicate success if one of
|
||||
expressions succeeded. Otherwise do not consume any text
|
||||
and indicate failure.
|
||||
``A ... Z`` Sequence: Apply expressions `A`, ..., `Z`, in this order,
|
||||
to consume consecutive portions of the text ahead, as long
|
||||
as they succeed. Indicate success if all succeeded.
|
||||
Otherwise do not consume any text and indicate failure.
|
||||
The sequence's precedence is higher than that of ordered
|
||||
choice: ``A B / C`` means ``(A B) / Z`` and
|
||||
not ``A (B / Z)``.
|
||||
``(E)`` Grouping: Parenthesis can be used to change
|
||||
operator priority.
|
||||
``{E}`` Capture: Apply expression `E` and store the substring
|
||||
that matched `E` into a *capture* that can be accessed
|
||||
after the matching process.
|
||||
``&E`` And predicate: Indicate success if expression `E` matches
|
||||
the text ahead; otherwise indicate failure. Do not consume
|
||||
any text.
|
||||
``!E`` Not predicate: Indicate failure if expression E matches the
|
||||
text ahead; otherwise indicate success. Do not consume any
|
||||
text.
|
||||
``E+`` One or more: Apply expression `E` repeatedly to match
|
||||
the text ahead, as long as it succeeds. Consume the matched
|
||||
text (if any) and indicate success if there was at least
|
||||
one match. Otherwise indicate failure.
|
||||
``E*`` Zero or more: Apply expression `E` repeatedly to match
|
||||
the text ahead, as long as it succeeds. Consume the matched
|
||||
text (if any). Always indicate success.
|
||||
``E?`` Zero or one: If expression `E` matches the text ahead,
|
||||
consume it. Always indicate success.
|
||||
``[s]`` Character class: If the character ahead appears in the
|
||||
string `s`, consume it and indicate success. Otherwise
|
||||
indicate failure.
|
||||
``[a-b]`` Character range: If the character ahead is one from the
|
||||
range `a` through `b`, consume it and indicate success.
|
||||
Otherwise indicate failure.
|
||||
``'s'`` String: If the text ahead is the string `s`, consume it
|
||||
and indicate success. Otherwise indicate failure.
|
||||
``i's'`` String match ignoring case.
|
||||
``y's'`` String match ignoring style.
|
||||
``v's'`` Verbatim string match: Use this to override a global
|
||||
``\i`` or ``\y`` modifier.
|
||||
``.`` Any character: If there is a character ahead, consume it
|
||||
and indicate success. Otherwise (that is, at the end of
|
||||
input) indicate failure.
|
||||
``_`` Any Unicode character: If there is an UTF-8 character
|
||||
ahead, consume it and indicate success. Otherwise indicate
|
||||
failure.
|
||||
``A <- E`` Rule: Bind the expression `E` to the *nonterminal symbol*
|
||||
`A`. **Left recursive rules are not possible and crash the
|
||||
matching engine.**
|
||||
``\identifier`` Built-in macro for a longer expression.
|
||||
``\ddd`` Character with decimal code *ddd*.
|
||||
``\"``, etc Literal ``"``, etc.
|
||||
=============== ============================================================
|
||||
|
||||
|
||||
Built-in macros
|
||||
---------------
|
||||
|
||||
============== ============================================================
|
||||
macro meaning
|
||||
============== ============================================================
|
||||
``\d`` any decimal digit: ``[0-9]``
|
||||
``\D`` any character that is not a decimal digit: ``[^0-9]``
|
||||
``\s`` any whitespace character: ``[ \9-\13]``
|
||||
``\S`` any character that is not a whitespace character:
|
||||
``[^ \9-\13]``
|
||||
``\w`` any "word" character: ``[a-zA-Z_]``
|
||||
``\W`` any "non-word" character: ``[^a-zA-Z_]``
|
||||
``\n`` any newline combination: ``\10 / \13\10 / \13``
|
||||
``\i`` ignore case for matching; use this at the start of the PEG
|
||||
``\y`` ignore style for matching; use this at the start of the PEG
|
||||
``\ident`` a standard ASCII identifier: ``[a-zA-Z_][a-zA-Z_0-9]*``
|
||||
============== ============================================================
|
||||
|
||||
A backslash followed by a letter is a built-in macro, otherwise it
|
||||
is used for ordinary escaping:
|
||||
|
||||
============== ============================================================
|
||||
notation meaning
|
||||
============== ============================================================
|
||||
``\\`` a single backslash
|
||||
``\*`` same as ``'*'``
|
||||
``\t`` not a tabulator, but an (unknown) built-in
|
||||
============== ============================================================
|
||||
|
||||
|
||||
Supported PEG grammar
|
||||
---------------------
|
||||
|
||||
The PEG parser implements this grammar (written in PEG syntax)::
|
||||
|
||||
# Example grammar of PEG in PEG syntax.
|
||||
# Comments start with '#'.
|
||||
# First symbol is the start symbol.
|
||||
|
||||
grammar <- rule* / expr
|
||||
|
||||
identifier <- [A-Za-z][A-Za-z0-9_]*
|
||||
charsetchar <- "\\" . / [^\]]
|
||||
charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
|
||||
stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
|
||||
"'" ("\\" . / [^'])* "'")
|
||||
builtin <- "\\" identifier / [^\13\10]
|
||||
|
||||
comment <- '#' !\n* \n
|
||||
ig <- (\s / comment)* # things to ignore
|
||||
|
||||
rule <- identifier \s* "<-" expr ig
|
||||
identNoArrow <- identifier !(\s* "<-")
|
||||
primary <- (ig '&' / ig '!')* ((ig identNoArrow / ig charset / ig stringlit
|
||||
/ ig builtin / ig '.' / ig '_'
|
||||
/ (ig "(" expr ig ")"))
|
||||
(ig '?' / ig '*' / ig '+')*)
|
||||
|
||||
# Concatenation has higher priority than choice:
|
||||
# ``a b / c`` means ``(a b) / c``
|
||||
|
||||
seqExpr <- primary+
|
||||
expr <- seqExpr (ig "/" expr)*
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Check if `s` matches Nimrod's "while" keyword:
|
||||
|
||||
.. code-block:: nimrod
|
||||
s =~ peg" y'while'"
|
||||
|
||||
Exchange (key, val)-pairs:
|
||||
|
||||
.. code-block:: nimrod
|
||||
"key: val; key2: val2".replace(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1")
|
||||
|
||||
Determine the ``#include``'ed files of a C file:
|
||||
|
||||
.. code-block:: nimrod
|
||||
for line in lines("myfile.c"):
|
||||
if line =~ peg"""s <- ws '#include' ws '"' {[^"]+} '"' ws
|
||||
comment <- '/*' (!'*/' . )* '*/' / '//' .*
|
||||
ws <- (comment / \s+)* """:
|
||||
echo matches[0]
|
||||
|
||||
PEG vs regular expression
|
||||
-------------------------
|
||||
As a regular expression ``\[.*\]`` maches longest possible text between ``'['``
|
||||
and ``']'``. As a PEG it never matches anything, because a PEG is
|
||||
deterministic: ``.*`` consumes the rest of the input, so ``\]`` never matches.
|
||||
As a PEG this needs to be written as: ``\[ ( !\] . )* \]``
|
||||
|
||||
Note that the regular expression does not behave as intended either:
|
||||
``*`` should not be greedy, so ``\[.*?\]`` should be used.
|
||||
|
||||
|
||||
PEG construction
|
||||
----------------
|
||||
There are two ways to construct a PEG in Nimrod code:
|
||||
(1) Parsing a string into an AST which consists of `TPeg` nodes with the
|
||||
`peg` proc.
|
||||
(2) Constructing the AST directly with proc calls. This method does not
|
||||
support constructing rules, only simple expressions and is not as
|
||||
convenient. It's only advantage is that it does not pull in the whole PEG
|
||||
parser into your executable.
|
||||
|
||||
420
doc/theindex.txt
420
doc/theindex.txt
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -177,8 +177,7 @@ bound to a class. This has disadvantages:
|
||||
* Adding a method to a class the programmer has no control over is
|
||||
impossible or needs ugly workarounds.
|
||||
* Often it is unclear where the method should belong to: Is
|
||||
``join`` a string method or an array method? Should the complex
|
||||
``vertexCover`` algorithm really be a method of the ``graph`` class?
|
||||
``join`` a string method or an array method?
|
||||
|
||||
Nimrod avoids these problems by not assigning methods to a class. All methods
|
||||
in Nimrod are `multi-methods`:idx:. As we will see later, multi-methods are
|
||||
@@ -206,7 +205,7 @@ for any type:
|
||||
(Another way to look at the method call syntax is that it provides the missing
|
||||
postfix notation.)
|
||||
|
||||
So code that looks "pure object oriented" is easy to write:
|
||||
So "pure object oriented" code is easy to write:
|
||||
|
||||
.. code-block:: nimrod
|
||||
import strutils
|
||||
@@ -277,7 +276,7 @@ already provides ``v[]`` access.
|
||||
Dynamic dispatch
|
||||
----------------
|
||||
|
||||
Procedures always use static dispatch. To get dynamic dispatch, replace the
|
||||
Procedures always use static dispatch. For dynamic dispatch replace the
|
||||
``proc`` keyword by ``method``:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
23
examples/filterex.nim
Executable file
23
examples/filterex.nim
Executable file
@@ -0,0 +1,23 @@
|
||||
#! stdtmpl | standard
|
||||
#proc generateHTMLPage(title, currentTab, content: string,
|
||||
# tabs: openArray[string]): string =
|
||||
# result = ""
|
||||
<head><title>$title</title></head>
|
||||
<body>
|
||||
<div id="menu">
|
||||
<ul>
|
||||
#for tab in items(tabs):
|
||||
#if currentTab == tab:
|
||||
<li><a id="selected"
|
||||
#else:
|
||||
<li><a
|
||||
#end if
|
||||
href="${tab}.html" title = "$title - $tab">$tab</a></li>
|
||||
#end for
|
||||
</ul>
|
||||
</div>
|
||||
<div id="content">
|
||||
$content
|
||||
A dollar: $$.
|
||||
</div>
|
||||
</body>
|
||||
37
examples/iupex1.nim
Executable file
37
examples/iupex1.nim
Executable file
@@ -0,0 +1,37 @@
|
||||
# Example IUP program
|
||||
|
||||
# IupTabs: Creates a IupTabs control.
|
||||
|
||||
import iup
|
||||
|
||||
discard iup.Open(nil, nil)
|
||||
|
||||
var vbox1 = Iup.Vbox(Iup.Label("Inside Tab A"), Iup.Button("Button A", ""), nil)
|
||||
var vbox2 = Iup.Vbox(Iup.Label("Inside Tab B"), Iup.Button("Button B", ""), nil)
|
||||
|
||||
Iup.SetAttribute(vbox1, "TABTITLE", "Tab A")
|
||||
Iup.SetAttribute(vbox2, "TABTITLE", "Tab B")
|
||||
|
||||
var tabs1 = Iup.Tabs(vbox1, vbox2, nil)
|
||||
|
||||
vbox1 = Iup.Vbox(Iup.Label("Inside Tab C"), Iup.Button("Button C", ""), nil)
|
||||
vbox2 = Iup.Vbox(Iup.Label("Inside Tab D"), Iup.Button("Button D", ""), nil)
|
||||
|
||||
Iup.SetAttribute(vbox1, "TABTITLE", "Tab C")
|
||||
Iup.SetAttribute(vbox2, "TABTITLE", "Tab D")
|
||||
|
||||
var tabs2 = Iup.Tabs(vbox1, vbox2, nil)
|
||||
Iup.SetAttribute(tabs2, "TABTYPE", "LEFT")
|
||||
|
||||
var box = Iup.Hbox(tabs1, tabs2, nil)
|
||||
Iup.SetAttribute(box, "MARGIN", "10x10")
|
||||
Iup.SetAttribute(box, "GAP", "10")
|
||||
|
||||
var dlg = Iup.Dialog(box)
|
||||
Iup.SetAttribute(dlg, "TITLE", "IupTabs")
|
||||
Iup.SetAttribute(dlg, "SIZE", "200x80")
|
||||
|
||||
discard ShowXY(dlg, IUP_CENTER, IUP_CENTER)
|
||||
discard MainLoop()
|
||||
Close()
|
||||
|
||||
7
examples/keyval2.nim
Executable file
7
examples/keyval2.nim
Executable file
@@ -0,0 +1,7 @@
|
||||
# Filter key=value pairs from "myfile.txt"
|
||||
import pegs
|
||||
|
||||
for x in lines("myfile.txt"):
|
||||
if x =~ peg"{\ident} \s* '=' \s* {.*}":
|
||||
echo "Key: ", matches[1],
|
||||
" Value: ", matches[2]
|
||||
8
examples/transff.nim
Executable file
8
examples/transff.nim
Executable file
@@ -0,0 +1,8 @@
|
||||
# Shows how to transform a file
|
||||
|
||||
import pegs
|
||||
|
||||
transformFile("infile.txt", "outfile.txt",
|
||||
[(peg"""S <- {typedesc} \s* {\ident} \s* ','
|
||||
typedesc <- \ident '*'* """, r"$2: $1")])
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
7
koch.py
7
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":
|
||||
|
||||
129
lib/impure/db_postgres.nim
Executable file
129
lib/impure/db_postgres.nim
Executable file
@@ -0,0 +1,129 @@
|
||||
# Nimrod PostgreSQL database wrapper
|
||||
# (c) 2009 Andreas Rumpf
|
||||
|
||||
import strutils, postgres
|
||||
|
||||
type
|
||||
TDbHandle* = PGconn
|
||||
TRow* = seq[string]
|
||||
EDb* = object of EIO
|
||||
|
||||
proc dbError(db: TDbHandle) {.noreturn.} =
|
||||
## raises an EDb exception.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
e.msg = PQerrorMessage(db)
|
||||
raise e
|
||||
|
||||
proc dbError*(msg: string) {.noreturn.} =
|
||||
## raises an EDb exception with message `msg`.
|
||||
var e: ref EDb
|
||||
new(e)
|
||||
e.msg = msg
|
||||
raise e
|
||||
|
||||
when false:
|
||||
proc dbQueryOpt*(db: TDbHandle, query: string, args: openarray[string]) =
|
||||
var stmt = mysql_stmt_init(db)
|
||||
if stmt == nil: dbError(db)
|
||||
if mysql_stmt_prepare(stmt, query, len(query)) != 0:
|
||||
dbError(db)
|
||||
var
|
||||
bind: seq[MYSQL_BIND]
|
||||
discard mysql_stmt_close(stmt)
|
||||
|
||||
proc dbQuote(s: string): string =
|
||||
result = "'"
|
||||
for c in items(s):
|
||||
if c == '\'': add(result, "''")
|
||||
else: add(result, c)
|
||||
add(result, '\'')
|
||||
|
||||
proc dbFormat(formatstr: string, args: openarray[string]): string =
|
||||
result = ""
|
||||
var a = 0
|
||||
for c in items(formatstr):
|
||||
if c == '?':
|
||||
add(result, dbQuote(args[a]))
|
||||
inc(a)
|
||||
else:
|
||||
add(result, c)
|
||||
|
||||
proc dbTryQuery*(db: TDbHandle, query: string, args: openarray[string]): bool =
|
||||
var q = dbFormat(query, args)
|
||||
var res = PQExec(db, q)
|
||||
result = PQresultStatus(res) == PGRES_COMMAND_OK
|
||||
PQclear(res)
|
||||
|
||||
proc dbQuery*(db: TDbHandle, query: string, args: openarray[string]) =
|
||||
var q = dbFormat(query, args)
|
||||
var res = PQExec(db, q)
|
||||
if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
|
||||
PQclear(res)
|
||||
|
||||
proc dbTryInsertID*(db: TDbHandle, query: string,
|
||||
args: openarray[string]): int64 =
|
||||
var q = dbFormat(query, args)
|
||||
|
||||
|
||||
if mysqlRealQuery(db, q, q.len) != 0'i32:
|
||||
result = -1'i64
|
||||
else:
|
||||
result = mysql_insert_id(db)
|
||||
LAST_INSERT_ID()
|
||||
|
||||
proc dbInsertID*(db: TDbHandle, query: string, args: openArray[string]): int64 =
|
||||
result = dbTryInsertID(db, query, args)
|
||||
if result < 0: dbError(db)
|
||||
|
||||
proc dbQueryAffectedRows*(db: TDbHandle, query: string,
|
||||
args: openArray[string]): int64 =
|
||||
## runs the query (typically "UPDATE") and returns the
|
||||
## number of affected rows
|
||||
var q = dbFormat(query, args)
|
||||
var res = PQExec(db, q)
|
||||
if PQresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
|
||||
result = parseBiggestInt($PQcmdTuples(res))
|
||||
PQclear(res)
|
||||
|
||||
proc newRow(L: int): TRow =
|
||||
newSeq(result, L)
|
||||
for i in 0..L-1: result[i] = ""
|
||||
|
||||
iterator dbFastRows*(db: TDbHandle, query: string,
|
||||
args: openarray[string]): TRow =
|
||||
var q = dbFormat(query, args)
|
||||
var res = PQExec(db, q)
|
||||
if PQresultStatus(res) != PGRES_TUPLES_OK: dbError(db)
|
||||
var L = int(PQnfields(res))
|
||||
var result = newRow(L)
|
||||
for i in 0..PQntuples(res)-1:
|
||||
for j in 0..L-1:
|
||||
setLen(result[j], 0)
|
||||
add(result[j], PQgetvalue(res, i, j))
|
||||
yield result
|
||||
PQclear(res)
|
||||
|
||||
proc dbGetAllRows*(db: TDbHandle, query: string,
|
||||
args: openarray[string]): seq[TRow] =
|
||||
result = @[]
|
||||
for r in dbFastRows(db, query, args):
|
||||
result.add(r)
|
||||
|
||||
iterator dbRows*(db: TDbHandle, query: string,
|
||||
args: openarray[string]): TRow =
|
||||
for r in items(dbGetAllRows(db, query, args)): yield r
|
||||
|
||||
proc dbGetValue*(db: TDbHandle, query: string,
|
||||
args: openarray[string]): string =
|
||||
result = ""
|
||||
for row in dbFastRows(db, query, args):
|
||||
result = row[0]
|
||||
break
|
||||
|
||||
proc dbClose*(db: TDbHandle) =
|
||||
if db != nil: PQfinish(db)
|
||||
|
||||
proc dbOpen*(connection, user, password, database: string): TDbHandle =
|
||||
result = PQsetdbLogin(nil, nil, nil, nil, database, user, password)
|
||||
if PQStatus(result) != CONNECTION_OK: result = nil
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1222,6 +1222,8 @@ var
|
||||
SC_XOPEN_STREAMS*{.importc: "_SC_XOPEN_STREAMS", header: "<unistd.h>".}: cint
|
||||
SC_XOPEN_UNIX*{.importc: "_SC_XOPEN_UNIX", header: "<unistd.h>".}: cint
|
||||
SC_XOPEN_VERSION*{.importc: "_SC_XOPEN_VERSION", header: "<unistd.h>".}: cint
|
||||
SC_NPROCESSORS_ONLN*{.importc: "_SC_NPROCESSORS_ONLN",
|
||||
header: "<unistd.h>".}: cint
|
||||
|
||||
SEM_FAILED* {.importc, header: "<semaphore.h>".}: pointer
|
||||
IPC_CREAT* {.importc, header: "<sys/ipc.h>".}: cint
|
||||
|
||||
163
lib/pure/hashtabs.nim
Executable file
163
lib/pure/hashtabs.nim
Executable file
@@ -0,0 +1,163 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## The ``hashtabs`` module implements an efficient generic hash
|
||||
## table/dictionary data type.
|
||||
|
||||
import
|
||||
hashes
|
||||
|
||||
const
|
||||
growthFactor = 2
|
||||
startSize = 8
|
||||
sham = sizeof(THash)*8-2 # shift amount
|
||||
mask = 0b11 shl sham
|
||||
usedSlot = 0b10 shl sham
|
||||
delSlot = 0b01 shl sham
|
||||
emptySlot = 0
|
||||
|
||||
type
|
||||
TTable*[TKey, TValue] = object
|
||||
counter: int
|
||||
data: seq[tuple[key: TKey, val: TValue, h: THash]]
|
||||
|
||||
proc init*(t: var TTable, size = startSize) =
|
||||
t.counter = 0
|
||||
newSeq(t.data, size)
|
||||
|
||||
proc markUsed(h: THash): THash {.inline.} =
|
||||
return h and not mask or usedSlot
|
||||
|
||||
proc len*(t: TTable): int {.inline.} =
|
||||
## returns the number of keys in `t`.
|
||||
result = t.counter
|
||||
|
||||
proc mustRehash(length, counter: int): bool =
|
||||
assert(length > counter)
|
||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
||||
|
||||
proc nextTry(h, maxHash: THash): THash {.inline.} =
|
||||
result = ((5 * h) + 1) and maxHash
|
||||
|
||||
template eq(a, b: expr): expr = a == b
|
||||
|
||||
proc rawGet(t: TTable, key: TKey, fullhash: THash): int =
|
||||
var h = fullhash and high(t.data)
|
||||
while (t.data[h].h and mask) != 0:
|
||||
# If it is a deleted entry, the comparison with ``markUsed(fullhash)``
|
||||
# fails, so there is no need to check for this explicitely.
|
||||
if t.data[h].h == markUsed(fullhash) and eq(t.data[h].key, key): return h
|
||||
h = nextTry(h, high(t.data))
|
||||
result = - 1
|
||||
|
||||
proc `[]`*(t: TTable, key: TKey): TValue =
|
||||
## retrieves the value at ``t[key]``. If `key` is not in `t`,
|
||||
## `EInvalidValue` is raised.
|
||||
var index = rawGet(t, key, hash(key))
|
||||
if index >= 0: result = t.data[index].val
|
||||
else:
|
||||
var e: ref EInvalidValue
|
||||
new(e)
|
||||
e.msg = "invalid key: " & $key
|
||||
raise e
|
||||
|
||||
proc hasKey*(t: TTable, key: TKey): bool =
|
||||
## returns true iff `key` is in the table `t`.
|
||||
result = rawGet(t, key) >= 0
|
||||
|
||||
proc rawInsert[TKey, TValue](
|
||||
data: var seq[tuple[key: TKey, val: TValue, h: THash]],
|
||||
tup: tuple[key: TKey, val: TValue, h: THash]) =
|
||||
var h = tup.h and high(data)
|
||||
while (data[h].h and mask) == usedSlot: h = nextTry(h, high(data))
|
||||
data[h] = tup
|
||||
|
||||
proc enlarge(t: var TTable) =
|
||||
var n: seq[tuple[key: TKey, val: TValue, h: THash]]
|
||||
newSeq(n, len(t.data) * growthFactor)
|
||||
for i in 0..high(t.data):
|
||||
if (t.data[i].h and mask) == usedSlot: rawInsert(n, t.data[i])
|
||||
swap(t.data, n)
|
||||
|
||||
proc `[]=`*(t: var TTable, key: TKey, val: TValue) =
|
||||
## puts a (key, value)-pair into `t`.
|
||||
var fullhash = hash(key)
|
||||
var index = rawGet(t, key, fullhash)
|
||||
if index >= 0:
|
||||
t.data[index].val = val
|
||||
else:
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
rawInsert(t.data, (key, val, markUsed(fullhash)))
|
||||
inc(t.counter)
|
||||
|
||||
proc add*(t: var TTable, key: TKey, val: TValue) =
|
||||
## puts a (key, value)-pair into `t`, but does not check if key already
|
||||
## exists.
|
||||
if mustRehash(len(t.data), t.counter): enlarge(t)
|
||||
rawInsert(t.data, (key, val, markUsed(hash(key))))
|
||||
inc(t.counter)
|
||||
|
||||
proc del*(t: var TTable, key: TKey) =
|
||||
## deletes a (key, val)-pair in `t`.
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0:
|
||||
t.data[index].h = delSlot
|
||||
|
||||
proc delAll*(t: var TTable, key: TKey) =
|
||||
## deletes all (key, val)-pairs in `t`.
|
||||
while true:
|
||||
var index = rawGet(t, key)
|
||||
if index < 0: break
|
||||
t.data[index].h = delSlot
|
||||
|
||||
iterator pairs*(t: TTable): tuple[key: TKey, value: TValue] =
|
||||
## iterates over any (key, value) pair in the table `t`.
|
||||
for h in 0..high(t.data):
|
||||
if (t.data[h].h and mask) == usedSlot:
|
||||
yield (t.data[h].key, t.data[h].val)
|
||||
|
||||
iterator keys*(t: TTable): TKey =
|
||||
## iterate over any key in the table `t`. If key occurs multiple times, it
|
||||
## is yielded multiple times.
|
||||
for h in 0..high(t.data):
|
||||
if (t.data[h].h and mask) == usedSlot:
|
||||
yield t.data[h].key
|
||||
|
||||
iterator values*(t: TTable): TValue =
|
||||
## iterate over any value in the table `t`.
|
||||
for h in 0..high(t.data):
|
||||
if (t.data[h].h and mask) == usedSlot:
|
||||
yield t.data[h].val
|
||||
|
||||
iterator values*(t: TTable, key: TKey): TValue =
|
||||
## iterate over any value associated with `key` in `t`.
|
||||
var fullhash = hash(key)
|
||||
var h = fullhash and high(t.data)
|
||||
while (t.data[h].h and mask) != 0:
|
||||
# If it is a deleted entry, the comparison with ``markUsed(fullhash)``
|
||||
# fails, so there is no need to check for this explicitely.
|
||||
if t.data[h].h == markUsed(fullhash) and eq(t.data[h].key, key):
|
||||
yield t.data[h].val
|
||||
h = nextTry(h, high(t.data))
|
||||
|
||||
proc `$`*[KeyToStr=`$`, ValueToStr=`$`](t: TTable): string =
|
||||
## turns the table into its string representation. `$` must be available
|
||||
## for TKey and TValue for this to work.
|
||||
if t.len == 0:
|
||||
result = "{:}"
|
||||
else:
|
||||
result = "{"
|
||||
var i = 0
|
||||
for k, v in pairs(t):
|
||||
if i > 0: add(result, ", ")
|
||||
add(result, KeyToStr(k))
|
||||
add(result, ": ")
|
||||
add(result, ValueToStr(v))
|
||||
inc(i)
|
||||
add(result, "}")
|
||||
@@ -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`.
|
||||
|
||||
@@ -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.}
|
||||
|
||||
@@ -9,19 +9,19 @@
|
||||
|
||||
## This module implements an advanced facility for executing OS processes
|
||||
## and process communication.
|
||||
## **On Windows this module does not work properly. Please help!**
|
||||
|
||||
import
|
||||
os, strtabs, streams
|
||||
strutils, os, strtabs, streams
|
||||
|
||||
when defined(windows):
|
||||
import winlean
|
||||
else:
|
||||
import posix
|
||||
|
||||
type
|
||||
TProcess = object of TObject
|
||||
when defined(windows):
|
||||
FProcessHandle: Thandle
|
||||
FThreadHandle: Thandle
|
||||
inputHandle, outputHandle, errorHandle: TFileHandle
|
||||
else:
|
||||
inputHandle, outputHandle, errorHandle: TFileHandle
|
||||
@@ -31,10 +31,11 @@ type
|
||||
PProcess* = ref TProcess ## represents an operating system process
|
||||
|
||||
TProcessOption* = enum ## options that can be passed `startProcess`
|
||||
poNone, ## none option
|
||||
poEchoCmd, ## echo the command before execution
|
||||
poUseShell, ## use the shell to execute the command; NOTE: This
|
||||
## often creates a security whole!
|
||||
poStdErrToStdOut ## merge stdout and stderr to the stdout stream
|
||||
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
|
||||
poParentStreams ## use the parent's streams
|
||||
|
||||
proc execProcess*(command: string,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||
@@ -77,18 +78,6 @@ proc startProcess*(command: string,
|
||||
## Return value: The newly created process object. Nil is never returned,
|
||||
## but ``EOS`` is raised in case of an error.
|
||||
|
||||
when true:
|
||||
nil
|
||||
else:
|
||||
proc startGUIProcess*(command: string,
|
||||
workingDir: string = "",
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
x = -1,
|
||||
y = -1,
|
||||
width = -1,
|
||||
height = -1): PProcess
|
||||
|
||||
proc suspend*(p: PProcess)
|
||||
## Suspends the process `p`.
|
||||
|
||||
@@ -117,13 +106,108 @@ proc outputStream*(p: PProcess): PStream
|
||||
proc errorStream*(p: PProcess): PStream
|
||||
## returns ``p``'s output stream for reading from
|
||||
|
||||
when defined(macosx) or defined(bsd):
|
||||
const
|
||||
CTL_HW = 6
|
||||
HW_AVAILCPU = 25
|
||||
HW_NCPU = 3
|
||||
proc sysctl(x: ptr array[0..3, cint], y: cint, z: pointer,
|
||||
a: var int, b: pointer, c: int): cint {.
|
||||
importc: "sysctl", header: "<sys/sysctl.h>".}
|
||||
|
||||
proc countProcessors*(): int =
|
||||
## returns the numer of the processors/cores the machine has.
|
||||
## Returns 0 if it cannot be determined.
|
||||
when defined(windows):
|
||||
var x = getenv("NUMBER_OF_PROCESSORS")
|
||||
if x.len > 0: result = parseInt(x)
|
||||
elif defined(macosx) or defined(bsd):
|
||||
var
|
||||
mib: array[0..3, cint]
|
||||
len, numCPU: int
|
||||
mib[0] = CTL_HW
|
||||
mib[1] = HW_AVAILCPU
|
||||
len = sizeof(numCPU)
|
||||
discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
|
||||
if numCPU < 1:
|
||||
mib[1] = HW_NCPU
|
||||
discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
|
||||
result = numCPU
|
||||
elif defined(hpux):
|
||||
result = mpctl(MPC_GETNUMSPUS, nil, nil)
|
||||
elif defined(irix):
|
||||
var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
|
||||
result = sysconf(SC_NPROC_ONLN)
|
||||
else:
|
||||
result = sysconf(SC_NPROCESSORS_ONLN)
|
||||
if result <= 0: result = 1
|
||||
|
||||
proc startProcessAux(cmd: string, options: set[TProcessOption]): PProcess =
|
||||
var c = parseCmdLine(cmd)
|
||||
var a: seq[string] = @[] # slicing is not yet implemented :-(
|
||||
for i in 1 .. c.len-1: add(a, c[i])
|
||||
result = startProcess(command=c[0], args=a, options=options)
|
||||
|
||||
proc execProcesses*(cmds: openArray[string],
|
||||
options = {poStdErrToStdOut, poParentStreams},
|
||||
n = countProcessors()): int =
|
||||
## executes the commands `cmds` in parallel. Creates `n` processes
|
||||
## that execute in parallel. The highest return value of all processes
|
||||
## is returned.
|
||||
assert n > 0
|
||||
if n > 1:
|
||||
var q: seq[PProcess]
|
||||
newSeq(q, n)
|
||||
var m = min(n, cmds.len)
|
||||
for i in 0..m-1:
|
||||
q[i] = startProcessAux(cmds[i], options=options)
|
||||
when defined(noBusyWaiting):
|
||||
var r = 0
|
||||
for i in m..high(cmds):
|
||||
when defined(debugExecProcesses):
|
||||
var err = ""
|
||||
var outp = outputStream(q[r])
|
||||
while running(q[r]) or not outp.atEnd(outp):
|
||||
err.add(outp.readLine())
|
||||
err.add("\n")
|
||||
echo(err)
|
||||
result = max(waitForExit(q[r]), result)
|
||||
q[r] = startProcessAux(cmds[i], options=options)
|
||||
r = (r + 1) mod n
|
||||
else:
|
||||
var i = m
|
||||
while i <= high(cmds):
|
||||
sleep(50)
|
||||
for r in 0..n-1:
|
||||
if not running(q[r]):
|
||||
#echo(outputStream(q[r]).readLine())
|
||||
result = max(waitForExit(q[r]), result)
|
||||
q[r] = startProcessAux(cmds[i], options=options)
|
||||
inc(i)
|
||||
if i > high(cmds): break
|
||||
for i in 0..m-1:
|
||||
result = max(waitForExit(q[i]), result)
|
||||
else:
|
||||
for i in 0..high(cmds):
|
||||
var p = startProcessAux(cmds[i], options=options)
|
||||
result = max(waitForExit(p), result)
|
||||
|
||||
when true:
|
||||
nil
|
||||
else:
|
||||
proc startGUIProcess*(command: string,
|
||||
workingDir: string = "",
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
x = -1,
|
||||
y = -1,
|
||||
width = -1,
|
||||
height = -1): PProcess
|
||||
|
||||
proc execProcess(command: string,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||
poUseShell}): string =
|
||||
var c = parseCmdLine(command)
|
||||
var a: seq[string] = @[] # slicing is not yet implemented :-(
|
||||
for i in 1 .. c.len-1: add(a, c[i])
|
||||
var p = startProcess(command=c[0], args=a, options=options)
|
||||
var p = startProcessAux(command, options=options)
|
||||
var outp = outputStream(p)
|
||||
result = ""
|
||||
while running(p) or not outp.atEnd(outp):
|
||||
@@ -147,15 +231,19 @@ when defined(Windows):
|
||||
atTheEnd: bool
|
||||
|
||||
proc hsClose(s: PFileHandleStream) = nil # nothing to do here
|
||||
proc hsAtEnd(s: PFileHandleStream): bool = return true
|
||||
proc hsAtEnd(s: PFileHandleStream): bool = return s.atTheEnd
|
||||
|
||||
proc hsReadData(s: PFileHandleStream, buffer: pointer, bufLen: int): int =
|
||||
if s.atTheEnd: return 0
|
||||
var br: int32
|
||||
var a = winlean.ReadFile(s.handle, buffer, bufLen, br, nil)
|
||||
if a == 0: OSError()
|
||||
# TRUE and zero bytes returned (EOF).
|
||||
# TRUE and n (>0) bytes returned (good data).
|
||||
# FALSE and bytes returned undefined (system error).
|
||||
if a == 0 and br != 0: OSError()
|
||||
s.atTheEnd = br < bufLen
|
||||
result = br
|
||||
#atEnd = bytesRead < bufLen
|
||||
|
||||
|
||||
proc hsWriteData(s: PFileHandleStream, buffer: pointer, bufLen: int) =
|
||||
var bytesWritten: int32
|
||||
var a = winlean.writeFile(s.handle, buffer, bufLen, bytesWritten, nil)
|
||||
@@ -168,18 +256,14 @@ when defined(Windows):
|
||||
result.atEnd = hsAtEnd
|
||||
result.readData = hsReadData
|
||||
result.writeData = hsWriteData
|
||||
|
||||
|
||||
proc buildCommandLine(a: string, args: openarray[string]): cstring =
|
||||
var L = a.len
|
||||
for i in 0..high(args): inc(L, args[i].len+1)
|
||||
result = cast[cstring](alloc0(L+1))
|
||||
copyMem(result, cstring(a), a.len)
|
||||
L = a.len
|
||||
for i in 0..high(args):
|
||||
result[L] = ' '
|
||||
inc(L)
|
||||
copyMem(addr(result[L]), cstring(args[i]), args[i].len)
|
||||
inc(L, args[i].len)
|
||||
var res = quoteIfContainsWhite(a)
|
||||
for i in 0..high(args):
|
||||
res.add(' ')
|
||||
res.add(quoteIfContainsWhite(args[i]))
|
||||
result = cast[cstring](alloc0(res.len+1))
|
||||
copyMem(result, cstring(res), res.len)
|
||||
|
||||
proc buildEnv(env: PStringTable): cstring =
|
||||
var L = 0
|
||||
@@ -198,72 +282,81 @@ when defined(Windows):
|
||||
# O_WRONLY {.importc: "_O_WRONLY", header: "<fcntl.h>".}: int
|
||||
# O_RDONLY {.importc: "_O_RDONLY", header: "<fcntl.h>".}: int
|
||||
|
||||
proc CreatePipeHandles(Inhandle, OutHandle: var THandle) =
|
||||
proc CreatePipeHandles(Rdhandle, WrHandle: var THandle) =
|
||||
var piInheritablePipe: TSecurityAttributes
|
||||
piInheritablePipe.nlength = SizeOF(TSecurityAttributes)
|
||||
piInheritablePipe.lpSecurityDescriptor = nil
|
||||
piInheritablePipe.Binherithandle = 1
|
||||
if CreatePipe(Inhandle, Outhandle, piInheritablePipe, 0) == 0'i32:
|
||||
if CreatePipe(Rdhandle, Wrhandle, piInheritablePipe, 1024) == 0'i32:
|
||||
OSError()
|
||||
|
||||
proc startProcess*(command: string,
|
||||
proc fileClose(h: THandle) {.inline.} =
|
||||
if h > 4: discard CloseHandle(h)
|
||||
|
||||
proc startProcess(command: string,
|
||||
workingDir: string = "",
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}): PProcess =
|
||||
new(result)
|
||||
var
|
||||
SI: TStartupInfo
|
||||
ProcInfo: TProcessInformation
|
||||
success: int
|
||||
hi, ho, he: THandle
|
||||
new(result)
|
||||
SI.cb = SizeOf(SI)
|
||||
SI.dwFlags = STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES
|
||||
CreatePipeHandles(SI.hStdInput, HI)
|
||||
CreatePipeHandles(HO, Si.hStdOutput)
|
||||
if poStdErrToStdOut in options:
|
||||
SI.hStdError = SI.hStdOutput
|
||||
HE = HO
|
||||
if poParentStreams notin options:
|
||||
SI.dwFlags = STARTF_USESTDHANDLES # STARTF_USESHOWWINDOW or
|
||||
CreatePipeHandles(SI.hStdInput, HI)
|
||||
CreatePipeHandles(HO, Si.hStdOutput)
|
||||
if poStdErrToStdOut in options:
|
||||
SI.hStdError = SI.hStdOutput
|
||||
HE = HO
|
||||
else:
|
||||
CreatePipeHandles(HE, Si.hStdError)
|
||||
result.inputHandle = hi
|
||||
result.outputHandle = ho
|
||||
result.errorHandle = he
|
||||
else:
|
||||
CreatePipeHandles(HE, Si.hStdError)
|
||||
result.inputHandle = hi
|
||||
result.outputHandle = ho
|
||||
result.errorHandle = he
|
||||
SI.hStdError = GetStdHandle(STD_ERROR_HANDLE)
|
||||
SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE)
|
||||
SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||
result.inputHandle = si.hStdInput
|
||||
result.outputHandle = si.hStdOutput
|
||||
result.errorHandle = si.hStdError
|
||||
|
||||
var cmdl: cstring
|
||||
if poUseShell in options:
|
||||
var comspec = getEnv("COMSPEC")
|
||||
var a: seq[string] = @[]
|
||||
add(a, "/c")
|
||||
add(a, command)
|
||||
add(a, args)
|
||||
cmdl = buildCommandLine(comspec, a)
|
||||
if false: # poUseShell in options:
|
||||
cmdl = buildCommandLine(getEnv("COMSPEC"), @["/c", command] & args)
|
||||
else:
|
||||
cmdl = buildCommandLine(command, args)
|
||||
var wd: cstring = nil
|
||||
var e: cstring = nil
|
||||
if len(workingDir) > 0: wd = workingDir
|
||||
if env == nil:
|
||||
success = winlean.CreateProcess(nil,
|
||||
cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, nil, wd, SI, ProcInfo)
|
||||
else:
|
||||
var e = buildEnv(env)
|
||||
success = winlean.CreateProcess(nil,
|
||||
cmdl, nil, nil, 0, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
|
||||
dealloc(e)
|
||||
if env != nil: e = buildEnv(env)
|
||||
if poEchoCmd in options: echo($cmdl)
|
||||
success = winlean.CreateProcess(nil,
|
||||
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
|
||||
|
||||
if poParentStreams notin options:
|
||||
FileClose(si.hStdInput)
|
||||
FileClose(si.hStdOutput)
|
||||
if poStdErrToStdOut notin options:
|
||||
FileClose(si.hStdError)
|
||||
|
||||
if e != nil: dealloc(e)
|
||||
dealloc(cmdl)
|
||||
if success == 0:
|
||||
OSError()
|
||||
# NEW:
|
||||
# Close the handles now so anyone waiting is woken.
|
||||
if success == 0: OSError()
|
||||
# Close the handle now so anyone waiting is woken:
|
||||
discard closeHandle(procInfo.hThread)
|
||||
result.FProcessHandle = procInfo.hProcess
|
||||
result.FThreadHandle = procInfo.hThread
|
||||
result.id = procInfo.dwProcessID
|
||||
|
||||
proc suspend(p: PProcess) =
|
||||
discard SuspendThread(p.FThreadHandle)
|
||||
discard SuspendThread(p.FProcessHandle)
|
||||
|
||||
proc resume(p: PProcess) =
|
||||
discard ResumeThread(p.FThreadHandle)
|
||||
discard ResumeThread(p.FProcessHandle)
|
||||
|
||||
proc running(p: PProcess): bool =
|
||||
var x = waitForSingleObject(p.FProcessHandle, 50)
|
||||
@@ -274,7 +367,6 @@ when defined(Windows):
|
||||
discard TerminateProcess(p.FProcessHandle, 0)
|
||||
|
||||
proc waitForExit(p: PProcess): int =
|
||||
#CloseHandle(p.FThreadHandle)
|
||||
discard WaitForSingleObject(p.FProcessHandle, Infinite)
|
||||
var res: int32
|
||||
discard GetExitCodeProcess(p.FProcessHandle, res)
|
||||
@@ -314,17 +406,15 @@ when defined(Windows):
|
||||
discard CloseHandle(Process)
|
||||
|
||||
else:
|
||||
import posix
|
||||
|
||||
const
|
||||
readIdx = 0
|
||||
writeIdx = 1
|
||||
|
||||
proc addCmdArgs(command: string, args: openarray[string]): string =
|
||||
result = command
|
||||
result = quoteIfContainsWhite(command)
|
||||
for i in 0 .. high(args):
|
||||
add(result, " ")
|
||||
add(result, args[i])
|
||||
add(result, quoteIfContainsWhite(args[i]))
|
||||
|
||||
proc toCStringArray(b, a: openarray[string]): cstringArray =
|
||||
result = cast[cstringArray](alloc0((a.len + b.len + 1) * sizeof(cstring)))
|
||||
@@ -344,14 +434,15 @@ else:
|
||||
copyMem(result[i], addr(x[0]), x.len+1)
|
||||
inc(i)
|
||||
|
||||
proc startProcess*(command: string,
|
||||
proc startProcess(command: string,
|
||||
workingDir: string = "",
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}): PProcess =
|
||||
new(result)
|
||||
var
|
||||
p_stdin, p_stdout, p_stderr: array [0..1, cint]
|
||||
new(result)
|
||||
result.exitCode = 3 # for ``waitForExit``
|
||||
if pipe(p_stdin) != 0'i32 or pipe(p_stdout) != 0'i32:
|
||||
OSError("failed to create a pipe")
|
||||
var Pid = fork()
|
||||
@@ -389,6 +480,8 @@ else:
|
||||
# too risky to raise an exception here:
|
||||
quit("execve call failed: " & $strerror(errno))
|
||||
# Parent process. Copy process information.
|
||||
if poEchoCmd in options:
|
||||
echo(command & " " & join(args, " "))
|
||||
result.id = pid
|
||||
|
||||
result.inputHandle = p_stdin[writeIdx]
|
||||
@@ -415,9 +508,15 @@ else:
|
||||
if running(p): discard kill(p.id, SIGKILL)
|
||||
|
||||
proc waitForExit(p: PProcess): int =
|
||||
result = 1
|
||||
if waitPid(p.id, p.exitCode, 0) == int(p.id):
|
||||
result = p.exitCode
|
||||
#if waitPid(p.id, p.exitCode, 0) == int(p.id):
|
||||
# ``waitPid`` fails if the process is not running anymore. But then
|
||||
# ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is
|
||||
# initialized with 3, wrong success exit codes are prevented.
|
||||
var oldExitCode = p.exitCode
|
||||
if waitPid(p.id, p.exitCode, 0) < 0:
|
||||
# failed, so restore old exitCode
|
||||
p.exitCode = oldExitCode
|
||||
result = int(p.exitCode)
|
||||
|
||||
proc inputStream(p: PProcess): PStream =
|
||||
var f: TFile
|
||||
@@ -440,4 +539,5 @@ else:
|
||||
result = csystem(command)
|
||||
|
||||
when isMainModule:
|
||||
echo execCmd("gcc -v")
|
||||
var x = execProcess("gcc -v")
|
||||
echo "ECHO ", x
|
||||
|
||||
1320
lib/pure/pegs.nim
Executable file
1320
lib/pure/pegs.nim
Executable file
File diff suppressed because it is too large
Load Diff
353
lib/pure/re.nim
Executable file
353
lib/pure/re.nim
Executable file
@@ -0,0 +1,353 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Regular expression support for Nimrod. Consider using the pegs module
|
||||
## instead.
|
||||
|
||||
{.compile: "tre/tre_all.c".}
|
||||
|
||||
from strutils import addf
|
||||
|
||||
type
|
||||
TRegExDesc {.pure, final.} = object
|
||||
re_nsub: int # Number of parenthesized subexpressions.
|
||||
value: pointer # For internal use only.
|
||||
|
||||
TRegEx* = ref TRegExDesc ## a compiled regular expression
|
||||
EInvalidRegEx* = object of EInvalidValue
|
||||
## is raised if the pattern is no valid regular expression.
|
||||
|
||||
TRegMatch {.pure.} = object
|
||||
so, eo: cint
|
||||
|
||||
const
|
||||
MaxSubpatterns* = 10
|
||||
## defines the maximum number of subpatterns that can be captured.
|
||||
## More subpatterns cannot be captured!
|
||||
|
||||
proc regnexec(preg: ptr TRegExDesc, s: cstring, len, nmatch: int,
|
||||
pmatch: ptr array [0..maxSubpatterns-1, TRegMatch],
|
||||
eflags: cint): cint {.importc.}
|
||||
proc regncomp(preg: ptr TRegExDesc, regex: cstring, n: int,
|
||||
cflags: cint): cint {.importc.}
|
||||
proc regfree(preg: ptr TRegExDesc) {.importc.}
|
||||
|
||||
const
|
||||
# POSIX regcomp() flags
|
||||
REG_EXTENDED = 1
|
||||
REG_ICASE = (REG_EXTENDED shl 1)
|
||||
REG_NEWLINE = (REG_ICASE shl 1)
|
||||
REG_NOSUB = (REG_NEWLINE shl 1)
|
||||
# Extra regcomp() flags
|
||||
REG_BASIC = 0
|
||||
REG_LITERAL = (REG_NOSUB shl 1)
|
||||
REG_RIGHT_ASSOC = (REG_LITERAL shl 1)
|
||||
REG_UNGREEDY = (REG_RIGHT_ASSOC shl 1)
|
||||
|
||||
# POSIX regexec() flags
|
||||
REG_NOTBOL = 1
|
||||
REG_NOTEOL = (REG_NOTBOL shl 1)
|
||||
|
||||
# Extra regexec() flags
|
||||
REG_APPROX_MATCHER = (REG_NOTEOL shl 1)
|
||||
REG_BACKTRACKING_MATCHER = (REG_APPROX_MATCHER shl 1)
|
||||
|
||||
ErrorMessages = [
|
||||
"No error",
|
||||
"No match",
|
||||
"Invalid regexp",
|
||||
"Unknown collating element",
|
||||
"Unknown character class name",
|
||||
"Trailing backslash",
|
||||
"Invalid back reference",
|
||||
"Missing ']'",
|
||||
"Missing ')'",
|
||||
"Missing '}'",
|
||||
"Invalid contents of {}",
|
||||
"Invalid character range",
|
||||
"Out of memory",
|
||||
"Invalid use of repetition operators"
|
||||
]
|
||||
|
||||
proc finalizeRegEx(x: TRegEx) = regfree(addr(x^))
|
||||
|
||||
proc re*(s: string): TRegEx =
|
||||
## Constructor of regular expressions. Note that Nimrod's
|
||||
## extended raw string literals supports this syntax ``re"[abc]"`` as
|
||||
## a short form for ``re(r"[abc]")``.
|
||||
new(result, finalizeRegEx)
|
||||
var err = int(regncomp(addr(result^), s, s.len,
|
||||
cint(REG_EXTENDED or REG_NEWLINE)))
|
||||
if err != 0:
|
||||
var e: ref EInvalidRegEx
|
||||
new(e)
|
||||
e.msg = ErrorMessages[err]
|
||||
raise e
|
||||
|
||||
proc xre*(pattern: string): TRegEx =
|
||||
## deletes whitespace from a pattern that is not escaped or in a character
|
||||
## class. Then it constructs a regular expresion object via `re`.
|
||||
## This is modelled after Perl's ``/x`` modifier.
|
||||
var p = ""
|
||||
var i = 0
|
||||
while i < pattern.len:
|
||||
case pattern[i]
|
||||
of ' ', '\t':
|
||||
inc i
|
||||
of '\\':
|
||||
add p, '\\'
|
||||
add p, pattern[i+1]
|
||||
inc i, 2
|
||||
of '[':
|
||||
while pattern[i] != ']' and pattern[i] != '\0':
|
||||
add p, pattern[i]
|
||||
inc i
|
||||
else:
|
||||
add p, pattern[i]
|
||||
inc i
|
||||
result = re(p)
|
||||
|
||||
proc rawmatch(s: string, pattern: TRegEx, matches: var openarray[string],
|
||||
start: int): tuple[first, last: int] =
|
||||
var
|
||||
rawMatches: array [0..maxSubpatterns-1, TRegMatch]
|
||||
cs = cstring(s)
|
||||
res = int(regnexec(addr(pattern^), cast[cstring](addr(cs[start])),
|
||||
s.len-start, maxSubpatterns, addr(rawMatches), cint(0)))
|
||||
if res == 0:
|
||||
for i in 0..min(matches.len, int(pattern.re_nsub))-1:
|
||||
var
|
||||
a = int(rawMatches[i].so)
|
||||
b = int(rawMatches[i].eo)
|
||||
echo "a: ", a, " b: ", b
|
||||
if a >= 0 and b >= 0:
|
||||
matches[i] = copy(s, a+start, b - 1 + start)
|
||||
else:
|
||||
matches[i] = ""
|
||||
return (int(rawMatches[0].so), int(rawMatches[0].eo)-1)
|
||||
return (-1, -1)
|
||||
|
||||
proc match*(s: string, pattern: TRegEx, matches: var openarray[string],
|
||||
start = 0): bool =
|
||||
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
|
||||
## the captured substrings in the array ``matches``. If it does not
|
||||
## match, nothing is written into ``matches`` and ``false`` is
|
||||
## returned.
|
||||
result = rawmatch(s, pattern, matches, start).first == 0
|
||||
|
||||
proc match*(s: string, pattern: TRegEx, start: int = 0): bool =
|
||||
## returns ``true`` if ``s`` matches the ``pattern`` beginning
|
||||
## from ``start``.
|
||||
var matches: array [0..0, string]
|
||||
result = rawmatch(s, pattern, matches, start).first == 0
|
||||
|
||||
proc matchLen*(s: string, pattern: TRegEx, matches: var openarray[string],
|
||||
start = 0): int =
|
||||
## the same as ``match``, but it returns the length of the match,
|
||||
## if there is no match, -1 is returned. Note that a match length
|
||||
## of zero can happen.
|
||||
var (a, b) = rawmatch(s, pattern, matches, start)
|
||||
result = a - b + 1
|
||||
|
||||
proc matchLen*(s: string, pattern: TRegEx, start = 0): int =
|
||||
## the same as ``match``, but it returns the length of the match,
|
||||
## if there is no match, -1 is returned. Note that a match length
|
||||
## of zero can happen.
|
||||
var matches: array [0..0, string]
|
||||
var (a, b) = rawmatch(s, pattern, matches, start)
|
||||
result = a - b + 1
|
||||
|
||||
proc find*(s: string, pattern: TRegEx, matches: var openarray[string],
|
||||
start = 0): int =
|
||||
## returns ``true`` if ``pattern`` occurs in ``s`` and the captured
|
||||
## substrings in the array ``matches``. If it does not match, nothing
|
||||
## is written into ``matches``.
|
||||
result = rawmatch(s, pattern, matches, start).first
|
||||
if result >= 0: inc(result, start)
|
||||
|
||||
proc find*(s: string, pattern: TRegEx, start = 0): int =
|
||||
## returns ``true`` if ``pattern`` occurs in ``s``.
|
||||
var matches: array [0..0, string]
|
||||
result = rawmatch(s, pattern, matches, start).first
|
||||
if result >= 0: inc(result, start)
|
||||
|
||||
template `=~`*(s: string, pattern: TRegEx): expr =
|
||||
## This calls ``match`` with an implicit declared ``matches`` array that
|
||||
## can be used in the scope of the ``=~`` call:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
##
|
||||
## if line =~ r"\s*(\w+)\s*\=\s*(\w+)":
|
||||
## # matches a key=value pair:
|
||||
## echo("Key: ", matches[1])
|
||||
## echo("Value: ", matches[2])
|
||||
## elif line =~ r"\s*(\#.*)":
|
||||
## # matches a comment
|
||||
## # note that the implicit ``matches`` array is different from the
|
||||
## # ``matches`` array of the first branch
|
||||
## echo("comment: ", matches[1])
|
||||
## else:
|
||||
## echo("syntax error")
|
||||
##
|
||||
when not definedInScope(matches):
|
||||
var matches: array[0..maxSubPatterns-1, string]
|
||||
match(s, pattern, matches)
|
||||
|
||||
# ------------------------- more string handling ------------------------------
|
||||
|
||||
proc contains*(s: string, pattern: TRegEx, start = 0): bool =
|
||||
## same as ``find(s, pattern, start) >= 0``
|
||||
return find(s, pattern, start) >= 0
|
||||
|
||||
proc contains*(s: string, pattern: TRegEx, matches: var openArray[string],
|
||||
start = 0): bool =
|
||||
## same as ``find(s, pattern, matches, start) >= 0``
|
||||
return find(s, pattern, matches, start) >= 0
|
||||
|
||||
proc startsWith*(s: string, prefix: TRegEx): bool =
|
||||
## returns true if `s` starts with the pattern `prefix`
|
||||
result = matchLen(s, prefix) >= 0
|
||||
|
||||
proc endsWith*(s: string, suffix: TRegEx): bool =
|
||||
## returns true if `s` ends with the pattern `prefix`
|
||||
for i in 0 .. s.len-1:
|
||||
if matchLen(s, suffix, i) == s.len - i: return true
|
||||
|
||||
proc replace*(s: string, sub: TRegEx, by: string): string =
|
||||
## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
|
||||
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## "var1=key; var2=key2".replace(p"{\ident}'='{\ident}", "$1<-$2$2")
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
##
|
||||
## "var1<-keykey; val2<-key2key2"
|
||||
result = ""
|
||||
var i = 0
|
||||
var caps: array[0..maxSubpatterns-1, string]
|
||||
while i < s.len:
|
||||
var x = matchLen(s, sub, caps, i)
|
||||
if x <= 0:
|
||||
add(result, s[i])
|
||||
inc(i)
|
||||
else:
|
||||
addf(result, by, caps)
|
||||
inc(i, x)
|
||||
# copy the rest:
|
||||
add(result, copy(s, i))
|
||||
|
||||
proc parallelReplace*(s: string, subs: openArray[
|
||||
tuple[pattern: TRegEx, repl: string]]): string =
|
||||
## Returns a modified copy of `s` with the substitutions in `subs`
|
||||
## applied in parallel.
|
||||
result = ""
|
||||
var i = 0
|
||||
var caps: array[0..maxSubpatterns-1, string]
|
||||
while i < s.len:
|
||||
block searchSubs:
|
||||
for j in 0..high(subs):
|
||||
var x = matchLen(s, subs[j][0], caps, i)
|
||||
if x > 0:
|
||||
addf(result, subs[j][1], caps)
|
||||
inc(i, x)
|
||||
break searchSubs
|
||||
add(result, s[i])
|
||||
inc(i)
|
||||
# copy the rest:
|
||||
add(result, copy(s, i))
|
||||
|
||||
proc transformFile*(infile, outfile: string,
|
||||
subs: openArray[tuple[pattern: TRegEx, repl: string]]) =
|
||||
## reads in the file `infile`, performs a parallel replacement (calls
|
||||
## `parallelReplace`) and writes back to `outfile`. Calls ``quit`` if an
|
||||
## error occurs. This is supposed to be used for quick scripting.
|
||||
var x = readFile(infile)
|
||||
if not isNil(x):
|
||||
var f: TFile
|
||||
if open(f, outfile, fmWrite):
|
||||
write(f, x.parallelReplace(subs))
|
||||
close(f)
|
||||
else:
|
||||
quit("cannot open for writing: " & outfile)
|
||||
else:
|
||||
quit("cannot open for reading: " & infile)
|
||||
|
||||
iterator split*(s: string, sep: TRegEx): string =
|
||||
## Splits the string `s` into substrings.
|
||||
##
|
||||
## Substrings are separated by the regular expression `sep`.
|
||||
## Examples:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## for word in split("00232this02939is39an22example111", re"\d+"):
|
||||
## writeln(stdout, word)
|
||||
##
|
||||
## Results in:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## "this"
|
||||
## "is"
|
||||
## "an"
|
||||
## "example"
|
||||
##
|
||||
var
|
||||
first = 0
|
||||
last = 0
|
||||
while last < len(s):
|
||||
var x = matchLen(s, sep, last)
|
||||
if x > 0: inc(last, x)
|
||||
first = last
|
||||
while last < len(s):
|
||||
inc(last)
|
||||
x = matchLen(s, sep, last)
|
||||
if x > 0: break
|
||||
if first < last:
|
||||
yield copy(s, first, last-1)
|
||||
|
||||
proc split*(s: string, sep: TRegEx): seq[string] =
|
||||
## Splits the string `s` into substrings.
|
||||
accumulateResult(split(s, sep))
|
||||
|
||||
const ## common regular expressions
|
||||
reIdentifier* = r"\b[a-zA-Z_]+[a-zA-Z_0-9]*\b" ## describes an identifier
|
||||
reNatural* = r"\b\d+\b" ## describes a natural number
|
||||
reInteger* = r"\b[-+]?\d+\b" ## describes an integer
|
||||
reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number
|
||||
reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101)
|
||||
reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777)
|
||||
reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
|
||||
## describes a floating point number
|
||||
reEmail* = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)" &
|
||||
r"*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+(?:[a-zA-Z]{2}|com|org|" &
|
||||
r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b"
|
||||
## describes a common email address
|
||||
reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" &
|
||||
r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
|
||||
## describes an URL
|
||||
|
||||
when isMainModule:
|
||||
echo matchLen("key", re"[a-zA-Z_][a-zA-Z_0-9]*")
|
||||
|
||||
var pattern = re"[a-zA-Z_][a-zA-Z_0-9]*\s*=\s*[a-zA-Z_][a-zA-Z_0-9]*"
|
||||
echo matchLen("key1= cal9", pattern, 2)
|
||||
|
||||
echo find("_____abc_______", re("abc"), 3)
|
||||
#echo "var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2")
|
||||
#echo "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}")
|
||||
|
||||
if "abc" =~ re"(a)bc xyz|([a-z]+)":
|
||||
echo matches[0]
|
||||
else:
|
||||
echo "BUG"
|
||||
|
||||
# for word in split("00232this02939is39an22example111", peg"\d+"):
|
||||
# writeln(stdout, word)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -45,10 +45,6 @@ const
|
||||
IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
|
||||
## the set of characters an identifier can start with
|
||||
|
||||
# strStart* = 0 ## this is only for bootstraping
|
||||
# ## XXX: remove this someday
|
||||
# nl* = "\n" ## this is only for bootstraping XXX: remove this someday
|
||||
|
||||
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
|
||||
## The `substitution`:idx: operator performs string substitutions in
|
||||
## `formatstr` and returns a modified `formatstr`. This is often called
|
||||
@@ -98,51 +94,30 @@ proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect.}
|
||||
## If `leading` is true, leading whitespace is stripped.
|
||||
## If `trailing` is true, trailing whitespace is stripped.
|
||||
|
||||
proc toLower*(s: string): string {.noSideEffect.}
|
||||
proc toLower*(s: string): string {.noSideEffect, procvar.}
|
||||
## Converts `s` into lower case. This works only for the letters A-Z.
|
||||
## See `unicode.toLower` for a version that works for any Unicode character.
|
||||
|
||||
proc toLower*(c: Char): Char {.noSideEffect.}
|
||||
proc toLower*(c: Char): Char {.noSideEffect, procvar.}
|
||||
## Converts `c` into lower case. This works only for the letters A-Z.
|
||||
## See `unicode.toLower` for a version that works for any Unicode character.
|
||||
|
||||
proc toUpper*(s: string): string {.noSideEffect.}
|
||||
proc toUpper*(s: string): string {.noSideEffect, procvar.}
|
||||
## Converts `s` into upper case. This works only for the letters a-z.
|
||||
## See `unicode.toUpper` for a version that works for any Unicode character.
|
||||
|
||||
proc toUpper*(c: Char): Char {.noSideEffect.}
|
||||
proc toUpper*(c: Char): Char {.noSideEffect, procvar.}
|
||||
## Converts `c` into upper case. This works only for the letters a-z.
|
||||
## See `unicode.toUpper` for a version that works for any Unicode character.
|
||||
|
||||
proc capitalize*(s: string): string {.noSideEffect.}
|
||||
proc capitalize*(s: string): string {.noSideEffect, procvar.}
|
||||
## Converts the first character of `s` into upper case.
|
||||
## This works only for the letters a-z.
|
||||
|
||||
proc normalize*(s: string): string {.noSideEffect.}
|
||||
proc normalize*(s: string): string {.noSideEffect, procvar.}
|
||||
## Normalizes the string `s`. That means to convert it to lower case and
|
||||
## remove any '_'. This is needed for Nimrod identifiers for example.
|
||||
|
||||
proc findSubStr*(sub, s: string, start: int = 0): int {.
|
||||
noSideEffect, deprecated.}
|
||||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
|
||||
## this has a different parameter order.
|
||||
|
||||
proc findSubStr*(sub: char, s: string, start: int = 0): int {.
|
||||
noSideEffect, deprecated.}
|
||||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
|
||||
## this has a different parameter order.
|
||||
|
||||
proc findChars*(chars: set[char], s: string, start: int = 0): int {.
|
||||
noSideEffect, deprecated.}
|
||||
## Searches for `chars` in `s` starting at position `start`. If `s` contains
|
||||
## none of the characters in `chars`, -1 is returned.
|
||||
## **Deprecated since version 0.7.6**: Use `find` instead, but beware that
|
||||
## this has a different parameter order.
|
||||
|
||||
proc find*(s, sub: string, start: int = 0): int {.noSideEffect.}
|
||||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||
@@ -322,13 +297,13 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect.}
|
||||
## | > 0 iff a > b
|
||||
|
||||
proc contains*(s: string, c: char): bool {.noSideEffect.}
|
||||
## Same as ``findSubStr(c, s) >= 0``.
|
||||
## Same as ``find(s, c) >= 0``.
|
||||
|
||||
proc contains*(s, sub: string): bool {.noSideEffect.}
|
||||
## Same as ``findSubStr(sub, s) >= 0``.
|
||||
## Same as ``find(s, sub) >= 0``.
|
||||
|
||||
proc contains*(s: string, chars: set[char]): bool {.noSideEffect.}
|
||||
## Same as ``findChars(s, chars) >= 0``.
|
||||
## Same as ``find(s, chars) >= 0``.
|
||||
|
||||
proc toHex*(x: BiggestInt, len: int): string {.noSideEffect.}
|
||||
## Converts `x` to its hexadecimal representation. The resulting string
|
||||
@@ -340,15 +315,15 @@ proc intToStr*(x: int, minchars: int = 1): string
|
||||
## will be minimally `minchars` characters long. This is achieved by
|
||||
## adding leading zeros.
|
||||
|
||||
proc ParseInt*(s: string): int {.noSideEffect.}
|
||||
proc ParseInt*(s: string): int {.noSideEffect, procvar.}
|
||||
## Parses a decimal integer value contained in `s`. If `s` is not
|
||||
## a valid integer, `EInvalidValue` is raised.
|
||||
|
||||
proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect.}
|
||||
proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect, procvar.}
|
||||
## Parses a decimal integer value contained in `s`. If `s` is not
|
||||
## a valid integer, `EInvalidValue` is raised.
|
||||
|
||||
proc ParseFloat*(s: string): float {.noSideEffect.}
|
||||
proc ParseFloat*(s: string): float {.noSideEffect, procvar.}
|
||||
## Parses a decimal floating point value contained in `s`. If `s` is not
|
||||
## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
|
||||
## ``INF``, ``-INF`` are also supported (case insensitive comparison).
|
||||
@@ -616,7 +591,7 @@ proc preprocessSub(sub: string, a: var TSkipTable) =
|
||||
for i in 0..0xff: a[chr(i)] = m+1
|
||||
for i in 0..m-1: a[sub[i]] = m-i
|
||||
|
||||
proc findSubStrAux(s, sub: string, start: int, a: TSkipTable): int =
|
||||
proc findAux(s, sub: string, start: int, a: TSkipTable): int =
|
||||
# fast "quick search" algorithm:
|
||||
var
|
||||
m = len(sub)
|
||||
@@ -631,36 +606,10 @@ proc findSubStrAux(s, sub: string, start: int, a: TSkipTable): int =
|
||||
inc(j, a[s[j+m]])
|
||||
return -1
|
||||
|
||||
proc findSubStr(sub, s: string, start: int = 0): int =
|
||||
var a: TSkipTable
|
||||
preprocessSub(sub, a)
|
||||
result = findSubStrAux(s, sub, start, a)
|
||||
# slow linear search:
|
||||
#var
|
||||
# i, j, M, N: int
|
||||
#M = len(sub)
|
||||
#N = len(s)
|
||||
#i = start
|
||||
#j = 0
|
||||
#if i >= N:
|
||||
# result = -1
|
||||
#else:
|
||||
# while True:
|
||||
# if s[i] == sub[j]:
|
||||
# Inc(i)
|
||||
# Inc(j)
|
||||
# else:
|
||||
# i = i - j + 1
|
||||
# j = 0
|
||||
# if (j >= M):
|
||||
# return i - M
|
||||
# elif (i >= N):
|
||||
# return -1
|
||||
|
||||
proc find(s, sub: string, start: int = 0): int =
|
||||
var a: TSkipTable
|
||||
preprocessSub(sub, a)
|
||||
result = findSubStrAux(s, sub, start, a)
|
||||
result = findAux(s, sub, start, a)
|
||||
|
||||
proc find(s: string, sub: char, start: int = 0): int =
|
||||
for i in start..len(s)-1:
|
||||
@@ -672,16 +621,6 @@ proc find(s: string, chars: set[char], start: int = 0): int =
|
||||
if s[i] in chars: return i
|
||||
return -1
|
||||
|
||||
proc findSubStr(sub: char, s: string, start: int = 0): int =
|
||||
for i in start..len(s)-1:
|
||||
if sub == s[i]: return i
|
||||
return -1
|
||||
|
||||
proc findChars(chars: set[char], s: string, start: int = 0): int =
|
||||
for i in start..s.len-1:
|
||||
if s[i] in chars: return i
|
||||
return -1
|
||||
|
||||
proc contains(s: string, chars: set[char]): bool =
|
||||
return find(s, chars) >= 0
|
||||
|
||||
@@ -698,7 +637,7 @@ proc replace*(s, sub, by: string): string =
|
||||
preprocessSub(sub, a)
|
||||
var i = 0
|
||||
while true:
|
||||
var j = findSubStrAux(s, sub, i, a)
|
||||
var j = findAux(s, sub, i, a)
|
||||
if j < 0: break
|
||||
add result, copy(s, i, j - 1)
|
||||
add result, by
|
||||
|
||||
181
lib/pure/variants.nim
Executable file
181
lib/pure/variants.nim
Executable file
@@ -0,0 +1,181 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements Nimrod's support for the ``variant`` datatype.
|
||||
## `TVariant` shows how the flexibility of dynamic typing is achieved
|
||||
## within a static type system.
|
||||
|
||||
type
|
||||
TVarType* = enum
|
||||
vtNone,
|
||||
vtBool,
|
||||
vtChar,
|
||||
vtEnum,
|
||||
vtInt,
|
||||
vtFloat,
|
||||
vtString,
|
||||
vtSet,
|
||||
vtSeq,
|
||||
vtDict
|
||||
TVariant* {.final.} = object of TObject
|
||||
case vtype: TVarType
|
||||
of vtNone: nil
|
||||
of vtBool, vtChar, vtEnum, vtInt: vint: int64
|
||||
of vtFloat: vfloat: float64
|
||||
of vtString: vstring: string
|
||||
of vtSet, vtSeq: q: seq[TVariant]
|
||||
of vtDict: d: seq[tuple[key, val: TVariant]]
|
||||
|
||||
iterator objectFields*[T](x: T, skipInherited: bool): tuple[
|
||||
key: string, val: TVariant] {.magic: "ObjectFields"}
|
||||
|
||||
proc `<>`*(x: ordinal): TVariant =
|
||||
result.kind = vtEnum
|
||||
result.vint = x
|
||||
|
||||
proc `<>`*(x: biggestInt): TVariant =
|
||||
result.kind = vtInt
|
||||
result.vint = x
|
||||
|
||||
proc `<>`*(x: char): TVariant =
|
||||
result.kind = vtChar
|
||||
result.vint = ord(x)
|
||||
|
||||
proc `<>`*(x: bool): TVariant =
|
||||
result.kind = vtBool
|
||||
result.vint = ord(x)
|
||||
|
||||
proc `<>`*(x: biggestFloat): TVariant =
|
||||
result.kind = vtFloat
|
||||
result.vfloat = x
|
||||
|
||||
proc `<>`*(x: string): TVariant =
|
||||
result.kind = vtString
|
||||
result.vstring = x
|
||||
|
||||
proc `<>`*[T](x: openArray[T]): TVariant =
|
||||
result.kind = vtSeq
|
||||
newSeq(result.q, x.len)
|
||||
for i in 0..x.len-1: result.q[i] = <>x[i]
|
||||
|
||||
proc `<>`*[T](x: set[T]): TVariant =
|
||||
result.kind = vtSet
|
||||
result.q = @[]
|
||||
for a in items(x): result.q.add(<>a)
|
||||
|
||||
proc `<>`* [T: object](x: T): TVariant {.magic: "ToVariant".}
|
||||
## this converts a value to a variant ("boxing")
|
||||
|
||||
proc `><`*[T](v: TVariant, typ: T): T {.magic: "FromVariant".}
|
||||
|
||||
[<>5, <>67, <>"hallo"]
|
||||
myVar><int
|
||||
|
||||
|
||||
proc `==`* (x, y: TVariant): bool =
|
||||
if x.vtype == y.vtype:
|
||||
case x.vtype
|
||||
of vtNone: result = true
|
||||
of vtBool, vtChar, vtEnum, vtInt: result = x.vint == y.vint
|
||||
of vtFloat: result = x.vfloat == y.vfloat
|
||||
of vtString: result = x.vstring == y.vstring
|
||||
of vtSet:
|
||||
# complicated! We check that each a in x also occurs in y and that the
|
||||
# counts are identical:
|
||||
if x.q.len == y.q.len:
|
||||
for a in items(x.q):
|
||||
block inner:
|
||||
for b in items(y.q):
|
||||
if a == b: break inner
|
||||
return false
|
||||
result = true
|
||||
of vtSeq:
|
||||
if x.q.len == y.q.len:
|
||||
for i in 0..x.q.len-1:
|
||||
if x.q[i] != y.q[i]: return false
|
||||
result = true
|
||||
of vtDict:
|
||||
# it is an ordered dict:
|
||||
if x.d.len == y.d.len:
|
||||
for i in 0..x.d.len-1:
|
||||
if x.d[i].key != y.d[i].key: return false
|
||||
if x.d[i].val != y.d[i].val: return false
|
||||
result = true
|
||||
|
||||
proc `[]`* (a, b: TVariant): TVariant =
|
||||
case a.vtype
|
||||
of vtSeq:
|
||||
if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
|
||||
result = a.q[b.vint]
|
||||
else:
|
||||
variantError()
|
||||
of vtDict:
|
||||
for i in 0..a.d.len-1:
|
||||
if a.d[i].key == b: return a.d[i].val
|
||||
if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
|
||||
result = a.d[b.vint].val
|
||||
variantError()
|
||||
else: variantError()
|
||||
|
||||
proc `[]=`* (a, b, c: TVariant) =
|
||||
case a.vtype
|
||||
of vtSeq:
|
||||
if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
|
||||
a.q[b.vint] = b
|
||||
else:
|
||||
variantError()
|
||||
of vtDict:
|
||||
for i in 0..a.d.len-1:
|
||||
if a.d[i].key == b:
|
||||
a.d[i].val = c
|
||||
return
|
||||
if b.vtype in {vtBool, vtChar, vtEnum, vtInt}:
|
||||
a.d[b.vint].val = c
|
||||
variantError()
|
||||
else: variantError()
|
||||
|
||||
proc `[]`* (a: TVariant, b: int): TVariant {.inline} = return a[<>b]
|
||||
proc `[]`* (a: TVariant, b: string): TVariant {.inline} = return a[<>b]
|
||||
proc `[]=`* (a: TVariant, b: int, c: TVariant) {.inline} = a[<>b] = c
|
||||
proc `[]=`* (a: TVariant, b: string, c: TVariant) {.inline} = a[<>b] = c
|
||||
|
||||
proc `+`* (x, y: TVariant): TVariant =
|
||||
case x.vtype
|
||||
of vtBool, vtChar, vtEnum, vtInt:
|
||||
if y.vtype == x.vtype:
|
||||
result.vtype = x.vtype
|
||||
result.vint = x.vint + y.vint
|
||||
else:
|
||||
case y.vtype
|
||||
of vtBool, vtChar, vtEnum, vtInt:
|
||||
|
||||
|
||||
|
||||
vint: int64
|
||||
of vtFloat: vfloat: float64
|
||||
of vtString: vstring: string
|
||||
of vtSet, vtSeq: q: seq[TVariant]
|
||||
of vtDict: d: seq[tuple[key, val: TVariant]]
|
||||
|
||||
proc `-`* (x, y: TVariant): TVariant
|
||||
proc `*`* (x, y: TVariant): TVariant
|
||||
proc `/`* (x, y: TVariant): TVariant
|
||||
proc `div`* (x, y: TVariant): TVariant
|
||||
proc `mod`* (x, y: TVariant): TVariant
|
||||
proc `&`* (x, y: TVariant): TVariant
|
||||
proc `$`* (x: TVariant): string =
|
||||
# uses JS notation
|
||||
|
||||
proc parseVariant*(s: string): TVariant
|
||||
proc `<`* (x, y: TVariant): bool
|
||||
proc `<=`* (x, y: TVariant): bool
|
||||
|
||||
proc hash*(x: TVariant): int =
|
||||
|
||||
|
||||
@@ -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.} =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -59,6 +59,8 @@ const
|
||||
STD_OUTPUT_HANDLE* = -11'i32
|
||||
STD_ERROR_HANDLE* = -12'i32
|
||||
|
||||
DETACHED_PROCESS* = 8'i32
|
||||
|
||||
proc CloseHandle*(hObject: THANDLE): WINBOOL {.stdcall, dynlib: "kernel32",
|
||||
importc: "CloseHandle".}
|
||||
|
||||
@@ -184,3 +186,7 @@ proc GetCommandLineA*(): CString {.importc, stdcall, dynlib: "kernel32".}
|
||||
proc rdFileTime*(f: FILETIME): int64 =
|
||||
result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
|
||||
|
||||
proc Sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
|
||||
importc: "Sleep".}
|
||||
|
||||
|
||||
|
||||
0
lib/wrappers/tre/config.h
Normal file → Executable file
0
lib/wrappers/tre/config.h
Normal file → Executable file
0
lib/wrappers/tre/tre_all.c
Normal file → Executable file
0
lib/wrappers/tre/tre_all.c
Normal file → Executable file
0
lib/wrappers/tre/version.txt
Normal file → Executable file
0
lib/wrappers/tre/version.txt
Normal file → Executable file
87
nim/ast.pas
87
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
|
||||
);
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -659,6 +659,8 @@ var
|
||||
begin
|
||||
p := newProc(prc, m);
|
||||
header := genProcHeader(m, prc);
|
||||
if (gCmd <> cmdCompileToLLVM) and (lfExportLib in prc.loc.flags) then
|
||||
header := con('N_LIB_EXPORT ', header);
|
||||
returnStmt := nil;
|
||||
assert(prc.ast <> nil);
|
||||
|
||||
@@ -942,7 +944,11 @@ const
|
||||
' call void @NimMain()$n' +
|
||||
' ret i32 0$n' +
|
||||
'}$n';
|
||||
WinNimDllMain = WinNimMain;
|
||||
WinNimDllMain =
|
||||
'N_LIB_EXPORT N_CDECL(void, NimMain)(void) {$n' +
|
||||
' int dummy[8];$n' +{&}
|
||||
CommonMainBody +{&}
|
||||
'}$n';
|
||||
WinCDllMain =
|
||||
'BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $n' +
|
||||
' LPVOID lpvReserved) {$n' +
|
||||
|
||||
0
nim/cgmeth.pas
Normal file → Executable file
0
nim/cgmeth.pas
Normal file → Executable file
@@ -1,7 +1,7 @@
|
||||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
@@ -85,7 +85,7 @@ const
|
||||
+{&} ' -a, --assertions:on|off code generation for assertions ON|OFF' +{&} nl
|
||||
+{&} ' --dead_code_elim:on|off whole program dead code elimination ON|OFF' +{&} nl
|
||||
+{&} ' --opt:none|speed|size optimize not at all or for speed|size' +{&} nl
|
||||
+{&} ' --app:console|gui generate a console|GUI application' +{&} nl
|
||||
+{&} ' --app:console|gui|lib generate a console|GUI application|dynamic library' +{&} nl
|
||||
+{&} ' -r, --run run the compiled program with given arguments' +{&} nl
|
||||
+{&} ' --advanced show advanced command line switches' +{&} nl
|
||||
+{&} ' -h, --help show this help' +{&} nl
|
||||
@@ -127,10 +127,12 @@ const
|
||||
+{&} ' --checkpoints:on|off turn on|off checkpoints; for debugging Nimrod' +{&} nl
|
||||
+{&} ' --skip_cfg do not read the general configuration file' +{&} nl
|
||||
+{&} ' --skip_proj_cfg do not read the project''s configuration file' +{&} nl
|
||||
+{&} ' --gc:refc|boehm use Nimrod''s native GC|Boehm GC' +{&} nl
|
||||
+{&} ' --gc:refc|boehm|none use Nimrod''s native GC|Boehm GC|no GC' +{&} nl
|
||||
+{&} ' --index:FILE use FILE to generate a documenation index file' +{&} nl
|
||||
+{&} ' --putenv:key=value set an environment variable' +{&} nl
|
||||
+{&} ' --list_cmd list the commands used to execute external programs' +{&} nl
|
||||
+{&} ' --parallel_build=0|1|... perform a parallel build' +{&} nl
|
||||
+{&} ' value = number of processors (0 for auto-detect)' +{&} nl
|
||||
+{&} ' --verbosity:0|1|2|3 set Nimrod''s verbosity level (0 is default)' +{&} nl
|
||||
+{&} ' -v, --version show detailed version information' +{&} nl
|
||||
//[[[end]]]
|
||||
@@ -152,7 +154,7 @@ begin
|
||||
if (pass = passCmd1) and not helpWritten then begin
|
||||
// BUGFIX 19
|
||||
MessageOut(getCommandLineDesc());
|
||||
helpWritten := true;
|
||||
helpWritten := true;
|
||||
halt(0);
|
||||
end
|
||||
end;
|
||||
@@ -362,10 +364,10 @@ begin
|
||||
wNoLinking: begin
|
||||
expectNoArg(switch, arg, pass, info);
|
||||
include(gGlobalOptions, optNoLinking);
|
||||
end;
|
||||
wNoMain: begin
|
||||
end;
|
||||
wNoMain: begin
|
||||
expectNoArg(switch, arg, pass, info);
|
||||
include(gGlobalOptions, optNoMain);
|
||||
include(gGlobalOptions, optNoMain);
|
||||
end;
|
||||
wForceBuild, wF: begin
|
||||
expectNoArg(switch, arg, pass, info);
|
||||
@@ -523,6 +525,10 @@ begin
|
||||
expectArg(switch, arg, pass, info);
|
||||
gVerbosity := parseInt(arg);
|
||||
end;
|
||||
wParallelBuild: begin
|
||||
expectArg(switch, arg, pass, info);
|
||||
gNumberOfProcessors := parseInt(arg);
|
||||
end;
|
||||
wVersion, wV: begin
|
||||
expectNoArg(switch, arg, pass, info);
|
||||
writeVersionInfo(pass);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
@@ -304,10 +304,10 @@ procedure setCC(const ccname: string);
|
||||
var
|
||||
i: TSystemCC;
|
||||
begin
|
||||
linkOptions := '';
|
||||
ccompiler := nameToCC(ccname);
|
||||
if ccompiler = ccNone then rawMessage(errUnknownCcompiler, ccname);
|
||||
compileOptions := getConfigVar(CC[ccompiler].name + '.options.always');
|
||||
linkOptions := getConfigVar(CC[ccompiler].name + '.options.linker');
|
||||
ccompilerpath := getConfigVar(CC[ccompiler].name + '.path');
|
||||
for i := low(CC) to high(CC) do undefSymbol(CC[i].name);
|
||||
defineSymbol(CC[ccompiler].name);
|
||||
@@ -323,6 +323,7 @@ begin
|
||||
if gCmd = cmdCompileToCpp then
|
||||
cExt := '.cpp';
|
||||
addCompileOption(getConfigVar(CC[ccompiler].name + '.options.always'));
|
||||
addLinkOption(getConfigVar(CC[ccompiler].name + '.options.linker'));
|
||||
if length(ccompilerPath) = 0 then
|
||||
ccompilerpath := getConfigVar(CC[ccompiler].name + '.path');
|
||||
end;
|
||||
@@ -387,8 +388,8 @@ end;
|
||||
procedure execExternalProgram(const cmd: string);
|
||||
begin
|
||||
if (optListCmd in gGlobalOptions) or (gVerbosity > 0) then
|
||||
MessageOut('Executing: ' +{&} nl +{&} cmd);
|
||||
if executeCommand(cmd) <> 0 then
|
||||
MessageOut(cmd);
|
||||
if execCmd(cmd) <> 0 then
|
||||
rawMessage(errExecutionOfProgramFailed);
|
||||
end;
|
||||
|
||||
@@ -398,7 +399,7 @@ var
|
||||
begin
|
||||
splitPath(projectFile, path, scriptname);
|
||||
SplitFilename(scriptname, name, ext);
|
||||
name := appendFileExt('compile_' + name, platform.os[targetOS].scriptExt);
|
||||
name := addFileExt('compile_' + name, platform.os[targetOS].scriptExt);
|
||||
WriteRope(script, joinPath(path, name));
|
||||
end;
|
||||
|
||||
@@ -437,7 +438,7 @@ var
|
||||
begin
|
||||
c := ccompiler;
|
||||
options := compileOptions;
|
||||
trunk := getFileTrunk(cfilename);
|
||||
trunk := extractFileTrunk(cfilename);
|
||||
if optCDebug in gGlobalOptions then begin
|
||||
key := trunk + '.debug';
|
||||
if existsConfigVar(key) then
|
||||
@@ -468,7 +469,7 @@ begin
|
||||
key := cc[c].name + '.exe';
|
||||
if existsConfigVar(key) then
|
||||
exe := getConfigVar(key);
|
||||
if targetOS = osWindows then exe := appendFileExt(exe, 'exe');
|
||||
if targetOS = osWindows then exe := addFileExt(exe, 'exe');
|
||||
|
||||
if (optGenDynLib in gGlobalOptions)
|
||||
and (ospNeedsPIC in platform.OS[targetOS].props) then
|
||||
@@ -494,7 +495,7 @@ begin
|
||||
objfile := toObjFile(cfile)
|
||||
else
|
||||
objfile := completeCFilePath(toObjFile(cfile));
|
||||
cfile := quoteIfContainsWhite(AppendFileExt(cfile, cExt));
|
||||
cfile := quoteIfContainsWhite(AddFileExt(cfile, cExt));
|
||||
objfile := quoteIfContainsWhite(objfile);
|
||||
|
||||
result := quoteIfContainsWhite(format(compilePattern,
|
||||
@@ -517,7 +518,9 @@ begin
|
||||
end;
|
||||
|
||||
procedure CompileCFile(const list: TLinkedList;
|
||||
var script: PRope; isExternal: Boolean);
|
||||
var script: PRope;
|
||||
var cmds: TStringSeq;
|
||||
isExternal: Boolean);
|
||||
var
|
||||
it: PStrEntry;
|
||||
compileCmd: string;
|
||||
@@ -528,7 +531,7 @@ begin
|
||||
// call the C compiler for the .c file:
|
||||
compileCmd := getCompileCFileCmd(it.data, isExternal);
|
||||
if not (optCompileOnly in gGlobalOptions) then
|
||||
execExternalProgram(compileCmd);
|
||||
add(cmds, compileCmd); //execExternalProgram(compileCmd);
|
||||
if (optGenScript in gGlobalOptions) then begin
|
||||
app(script, compileCmd);
|
||||
app(script, tnl);
|
||||
@@ -543,6 +546,8 @@ var
|
||||
linkCmd, objfiles, exefile, buildgui, builddll, linkerExe: string;
|
||||
c: TSystemCC; // an alias to ccompiler
|
||||
script: PRope;
|
||||
cmds: TStringSeq;
|
||||
res, i: int;
|
||||
begin
|
||||
if (gGlobalOptions * [optCompileOnly, optGenScript] = [optCompileOnly]) then
|
||||
exit; // speed up that call if only compiling and no script shall be
|
||||
@@ -551,41 +556,53 @@ begin
|
||||
fileCounter := 0;
|
||||
c := ccompiler;
|
||||
script := nil;
|
||||
CompileCFile(toCompile, script, false);
|
||||
CompileCFile(externalToCompile, script, true);
|
||||
|
||||
cmds := {@ignore} nil {@emit @[]};
|
||||
CompileCFile(toCompile, script, cmds, false);
|
||||
CompileCFile(externalToCompile, script, cmds, true);
|
||||
if not (optCompileOnly in gGlobalOptions) then begin
|
||||
if gNumberOfProcessors = 0 then
|
||||
gNumberOfProcessors := countProcessors();
|
||||
if gNumberOfProcessors <= 1 then begin
|
||||
res := 0;
|
||||
for i := 0 to high(cmds) do res := max(execCmd(cmds[i]), res);
|
||||
end
|
||||
else if (optListCmd in gGlobalOptions) or (gVerbosity > 0) then
|
||||
res := execProcesses(cmds, {@set}[poEchoCmd, poUseShell, poParentStreams],
|
||||
gNumberOfProcessors)
|
||||
else
|
||||
res := execProcesses(cmds, {@set}[poUseShell, poParentStreams],
|
||||
gNumberOfProcessors);
|
||||
if res <> 0 then
|
||||
rawMessage(errExecutionOfProgramFailed);
|
||||
end;
|
||||
|
||||
if not (optNoLinking in gGlobalOptions) then begin
|
||||
// call the linker:
|
||||
linkerExe := getConfigVar(cc[c].name + '.linkerexe');
|
||||
if length(linkerExe) = 0 then linkerExe := cc[c].linkerExe;
|
||||
if targetOS = osWindows then linkerExe := appendFileExt(linkerExe, 'exe');
|
||||
if targetOS = osWindows then linkerExe := addFileExt(linkerExe, 'exe');
|
||||
|
||||
if (platform.hostOS <> targetOS) then
|
||||
linkCmd := quoteIfContainsWhite(linkerExe)
|
||||
else
|
||||
linkCmd := quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe));
|
||||
|
||||
if optGenDynLib in gGlobalOptions then
|
||||
buildDll := cc[c].buildDll
|
||||
else
|
||||
buildDll := '';
|
||||
if optGenGuiApp in gGlobalOptions then
|
||||
buildGui := cc[c].buildGui
|
||||
else
|
||||
buildGui := '';
|
||||
|
||||
if optGenDynLib in gGlobalOptions then
|
||||
exefile := platform.os[targetOS].dllPrefix
|
||||
else
|
||||
exefile := '';
|
||||
|
||||
if optGenDynLib in gGlobalOptions then begin
|
||||
exefile := format(platform.os[targetOS].dllFrmt,
|
||||
[extractFileTrunk(projectFile)]);
|
||||
buildDll := cc[c].buildDll;
|
||||
end
|
||||
else begin
|
||||
exefile := extractFileTrunk(projectFile) +{&} platform.os[targetOS].exeExt;
|
||||
buildDll := '';
|
||||
end;
|
||||
if targetOS = platform.hostOS then
|
||||
add(exefile, projectFile)
|
||||
else
|
||||
add(exefile, extractFileName(projectFile));
|
||||
if optGenDynLib in gGlobalOptions then
|
||||
add(exefile, platform.os[targetOS].dllExt)
|
||||
else
|
||||
add(exefile, platform.os[targetOS].exeExt);
|
||||
exefile := joinPath(extractDir(projectFile), exefile);
|
||||
exefile := quoteIfContainsWhite(exefile);
|
||||
|
||||
it := PStrEntry(toLink.head);
|
||||
@@ -639,7 +656,7 @@ begin
|
||||
result := nil;
|
||||
it := PStrEntry(list.head);
|
||||
while it <> nil do begin
|
||||
appf(result, '--file:r"$1"$n', [toRope(AppendFileExt(it.data, cExt))]);
|
||||
appf(result, '--file:r"$1"$n', [toRope(AddFileExt(it.data, cExt))]);
|
||||
it := PStrEntry(it.next);
|
||||
end;
|
||||
end;
|
||||
|
||||
137
nim/filters.pas
Executable file
137
nim/filters.pas
Executable file
@@ -0,0 +1,137 @@
|
||||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
//
|
||||
unit filters;
|
||||
|
||||
// This module implements Nimrod's simple filters and helpers for filters.
|
||||
|
||||
{$include config.inc}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
nsystem, llstream, nos, charsets, wordrecg, idents, strutils,
|
||||
ast, astalgo, msgs, options, rnimsyn;
|
||||
|
||||
function filterReplace(input: PLLStream; const filename: string;
|
||||
call: PNode): PLLStream;
|
||||
function filterStrip(input: PLLStream; const filename: string;
|
||||
call: PNode): PLLStream;
|
||||
|
||||
// helpers to retrieve arguments:
|
||||
function charArg(n: PNode; const name: string; pos: int; default: Char): Char;
|
||||
function strArg(n: PNode; const name: string; pos: int;
|
||||
const default: string): string;
|
||||
function boolArg(n: PNode; const name: string; pos: int; default: bool): bool;
|
||||
|
||||
implementation
|
||||
|
||||
procedure invalidPragma(n: PNode);
|
||||
begin
|
||||
liMessage(n.info, errXNotAllowedHere, renderTree(n, {@set}[renderNoComments]));
|
||||
end;
|
||||
|
||||
function getArg(n: PNode; const name: string; pos: int): PNode;
|
||||
var
|
||||
i: int;
|
||||
begin
|
||||
result := nil;
|
||||
if n.kind in [nkEmpty..nkNilLit] then exit;
|
||||
for i := 1 to sonsLen(n)-1 do
|
||||
if n.sons[i].kind = nkExprEqExpr then begin
|
||||
if n.sons[i].sons[0].kind <> nkIdent then invalidPragma(n);
|
||||
if IdentEq(n.sons[i].sons[0].ident, name) then begin
|
||||
result := n.sons[i].sons[1];
|
||||
exit
|
||||
end
|
||||
end
|
||||
else if i = pos then begin
|
||||
result := n.sons[i]; exit
|
||||
end
|
||||
end;
|
||||
|
||||
function charArg(n: PNode; const name: string; pos: int; default: Char): Char;
|
||||
var
|
||||
x: PNode;
|
||||
begin
|
||||
x := getArg(n, name, pos);
|
||||
if x = nil then result := default
|
||||
else if x.kind = nkCharLit then result := chr(int(x.intVal))
|
||||
else invalidPragma(n);
|
||||
end;
|
||||
|
||||
function strArg(n: PNode; const name: string; pos: int;
|
||||
const default: string): string;
|
||||
var
|
||||
x: PNode;
|
||||
begin
|
||||
x := getArg(n, name, pos);
|
||||
if x = nil then result := default
|
||||
else if x.kind in [nkStrLit..nkTripleStrLit] then result := x.strVal
|
||||
else invalidPragma(n);
|
||||
end;
|
||||
|
||||
function boolArg(n: PNode; const name: string; pos: int; default: bool): bool;
|
||||
var
|
||||
x: PNode;
|
||||
begin
|
||||
x := getArg(n, name, pos);
|
||||
if x = nil then result := default
|
||||
else if (x.kind = nkIdent) and IdentEq(x.ident, 'true') then result := true
|
||||
else if (x.kind = nkIdent) and IdentEq(x.ident, 'false') then result := false
|
||||
else invalidPragma(n);
|
||||
end;
|
||||
|
||||
// -------------------------- strip filter -----------------------------------
|
||||
|
||||
function filterStrip(input: PLLStream; const filename: string;
|
||||
call: PNode): PLLStream;
|
||||
var
|
||||
line, pattern, stripped: string;
|
||||
leading, trailing: bool;
|
||||
begin
|
||||
pattern := strArg(call, 'startswith', 1, '');
|
||||
leading := boolArg(call, 'leading', 2, true);
|
||||
trailing := boolArg(call, 'trailing', 3, true);
|
||||
|
||||
result := LLStreamOpen('');
|
||||
while not LLStreamAtEnd(input) do begin
|
||||
line := LLStreamReadLine(input);
|
||||
{@ignore}
|
||||
stripped := strip(line);
|
||||
{@emit
|
||||
stripped := strip(line, leading, trailing);
|
||||
}
|
||||
if (length(pattern) = 0) or startsWith(stripped, pattern) then
|
||||
LLStreamWriteln(result, stripped)
|
||||
else
|
||||
LLStreamWriteln(result, line)
|
||||
end;
|
||||
LLStreamClose(input);
|
||||
end;
|
||||
|
||||
// -------------------------- replace filter ---------------------------------
|
||||
|
||||
function filterReplace(input: PLLStream; const filename: string;
|
||||
call: PNode): PLLStream;
|
||||
var
|
||||
line, sub, by: string;
|
||||
begin
|
||||
sub := strArg(call, 'sub', 1, '');
|
||||
if length(sub) = 0 then invalidPragma(call);
|
||||
by := strArg(call, 'by', 2, '');
|
||||
|
||||
result := LLStreamOpen('');
|
||||
while not LLStreamAtEnd(input) do begin
|
||||
line := LLStreamReadLine(input);
|
||||
LLStreamWriteln(result, replace(line, sub, by))
|
||||
end;
|
||||
LLStreamClose(input);
|
||||
end;
|
||||
|
||||
end.
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
31
nim/main.pas
31
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;
|
||||
|
||||
15
nim/msgs.pas
15
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
|
||||
|
||||
@@ -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);
|
||||
|
||||
18
nim/nos.pas
18
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
|
||||
|
||||
@@ -55,6 +55,11 @@ const
|
||||
|
||||
snil = '';
|
||||
|
||||
type
|
||||
TStringSeq = array of string;
|
||||
TCharSet = set of Char;
|
||||
|
||||
|
||||
type
|
||||
Natural = 0..high(int);
|
||||
Positive = 1..high(int);
|
||||
@@ -214,9 +219,11 @@ function readFile(const filename: string): string;
|
||||
|
||||
procedure nimWrite(var f: tBinaryFile; const str: string); overload;
|
||||
|
||||
procedure add(var x: string; const y: string);
|
||||
procedure add(var x: string; const y: string); overload;
|
||||
// Pascal version of string appending. Terminating zero is ignored.
|
||||
|
||||
procedure add(var s: TStringSeq; const y: string); overload;
|
||||
|
||||
function isNil(s: string): bool;
|
||||
|
||||
implementation
|
||||
@@ -238,6 +245,15 @@ begin
|
||||
else x := x + y;
|
||||
end
|
||||
end;
|
||||
|
||||
procedure add(var s: TStringSeq; const y: string); overload;
|
||||
var
|
||||
L: int;
|
||||
begin
|
||||
L := length(s);
|
||||
setLength(s, L+1);
|
||||
s[L] := y;
|
||||
end;
|
||||
{@emit}
|
||||
|
||||
function alloc(size: int): Pointer;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -17,14 +17,42 @@ interface
|
||||
|
||||
uses
|
||||
nsystem, nos;
|
||||
|
||||
type
|
||||
TProcessOption = (poEchoCmd, poUseShell, poStdErrToStdOut, poParentStreams);
|
||||
TProcessOptions = set of TProcessOption;
|
||||
|
||||
function executeCommand(const cmd: string): int;
|
||||
function execCmd(const cmd: string): int;
|
||||
function execProcesses(const cmds: array of string;
|
||||
options: TProcessOptions;
|
||||
n: int): int;
|
||||
|
||||
function countProcessors(): int;
|
||||
|
||||
implementation
|
||||
|
||||
function executeCommand(const cmd: string): int;
|
||||
begin
|
||||
function execCmd(const cmd: string): int;
|
||||
begin
|
||||
writeln(output, cmd);
|
||||
result := executeShellCommand(cmd);
|
||||
end;
|
||||
|
||||
function execProcesses(const cmds: array of string;
|
||||
options: TProcessOptions;
|
||||
n: int): int;
|
||||
var
|
||||
i: int;
|
||||
begin
|
||||
result := 0;
|
||||
for i := 0 to high(cmds) do begin
|
||||
//if poEchoCmd in options then writeln(output, cmds[i]);
|
||||
result := max(result, execCmd(cmds[i]))
|
||||
end
|
||||
end;
|
||||
|
||||
function countProcessors(): int;
|
||||
begin
|
||||
result := 1;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//
|
||||
//
|
||||
// The Nimrod Compiler
|
||||
// (c) Copyright 2008 Andreas Rumpf
|
||||
// (c) Copyright 2009 Andreas Rumpf
|
||||
//
|
||||
// See the file "copying.txt", included in this
|
||||
// distribution, for details about the copyright.
|
||||
@@ -19,7 +19,7 @@ uses
|
||||
nsystem, charsets, strutils,
|
||||
lists, options, ast, astalgo, llstream,
|
||||
msgs, platform, nos, condsyms, idents, rnimsyn, types,
|
||||
extccomp, nmath, magicsys, nversion, nimsets, pnimsyn, ntime, rodread;
|
||||
extccomp, nmath, magicsys, nversion, nimsets, syntaxes, ntime, rodread;
|
||||
|
||||
type
|
||||
TPassContext = object(NObject) // the pass's context
|
||||
@@ -61,7 +61,6 @@ function astNeeded(s: PSym): bool;
|
||||
var
|
||||
gImportModule: function (const filename: string): PSym;
|
||||
gIncludeFile: function (const filename: string): PNode;
|
||||
gIncludeTmplFile: function (const filename: string): PNode;
|
||||
|
||||
implementation
|
||||
|
||||
@@ -165,7 +164,7 @@ end;
|
||||
procedure processModule(module: PSym; const filename: string;
|
||||
stream: PLLStream; rd: PRodReader);
|
||||
var
|
||||
p: TParser;
|
||||
p: TParsers;
|
||||
n: PNode;
|
||||
a: TPassContextArray;
|
||||
s: PLLStream;
|
||||
@@ -183,13 +182,13 @@ begin
|
||||
else
|
||||
s := stream;
|
||||
while true do begin
|
||||
openParser(p, filename, s);
|
||||
openParsers(p, filename, s);
|
||||
while true do begin
|
||||
n := parseTopLevelStmt(p);
|
||||
if n = nil then break;
|
||||
processTopLevelStmt(n, a)
|
||||
end;
|
||||
closeParser(p);
|
||||
closeParsers(p);
|
||||
if s.kind <> llsStdIn then break;
|
||||
end;
|
||||
closePasses(a);
|
||||
|
||||
1499
nim/pbraces.pas
Executable file
1499
nim/pbraces.pas
Executable file
File diff suppressed because it is too large
Load Diff
@@ -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: ':'+'';
|
||||
|
||||
528
nim/pnimsyn.pas
528
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
|
||||
|
||||
@@ -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
|
||||
|
||||
212
nim/ptmplsyn.pas
212
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;
|
||||
p.state := psDirective;
|
||||
end
|
||||
wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
|
||||
wConverter, wMacro, wTemplate, wMethod: begin
|
||||
LLStreamWrite(p.outp, repeatChar(p.indent));
|
||||
LLStreamWrite(p.outp, ncopy(p.x, d));
|
||||
inc(p.indent, 2);
|
||||
end;
|
||||
wElif, wOf, wElse, wExcept, wFinally: begin
|
||||
LLStreamWrite(p.outp, repeatChar(p.indent-2));
|
||||
LLStreamWrite(p.outp, ncopy(p.x, d));
|
||||
end
|
||||
else begin
|
||||
LLStreamWrite(p.outp, repeatChar(p.indent));
|
||||
LLStreamWrite(p.outp, ncopy(p.x, d));
|
||||
end
|
||||
end;
|
||||
p.state := psDirective
|
||||
end
|
||||
else begin
|
||||
// data line
|
||||
@@ -133,17 +100,18 @@ begin
|
||||
case p.state of
|
||||
psTempl: begin
|
||||
// next line of string literal:
|
||||
LLStreamWrite(p.outp, ' &'+nl);
|
||||
LLStreamWrite(p.outp, p.conc);
|
||||
LLStreamWrite(p.outp, nl);
|
||||
LLStreamWrite(p.outp, repeatChar(p.indent + 2));
|
||||
LLStreamWrite(p.outp, '"'+'');
|
||||
end;
|
||||
psDirective: begin
|
||||
newLine(p);
|
||||
LLStreamWrite(p.outp, repeatChar(p.indent));
|
||||
LLStreamWrite(p.outp, 'add(result, "');
|
||||
LLStreamWrite(p.outp, p.emit);
|
||||
LLStreamWrite(p.outp, '("');
|
||||
inc(p.par);
|
||||
end;
|
||||
else InternalError(p.info, 'parser in invalid state');
|
||||
end
|
||||
end;
|
||||
p.state := psTempl;
|
||||
while true do begin
|
||||
@@ -162,7 +130,10 @@ begin
|
||||
case p.x[j] of
|
||||
'{': begin
|
||||
p.info.col := int16(j);
|
||||
LLStreamWrite(p.outp, '" & $(');
|
||||
LLStreamWrite(p.outp, '"');
|
||||
LLStreamWrite(p.outp, p.conc);
|
||||
LLStreamWrite(p.outp, p.toStr);
|
||||
LLStreamWrite(p.outp, '(');
|
||||
inc(j);
|
||||
curly := 0;
|
||||
while true do begin
|
||||
@@ -171,13 +142,13 @@ begin
|
||||
'{': begin
|
||||
inc(j);
|
||||
inc(curly);
|
||||
LLStreamWrite(p.outp, '{'+'');
|
||||
LLStreamWrite(p.outp, '{');
|
||||
end;
|
||||
'}': begin
|
||||
inc(j);
|
||||
if curly = 0 then break;
|
||||
if curly > 0 then dec(curly);
|
||||
LLStreamWrite(p.outp, '}'+'');
|
||||
LLStreamWrite(p.outp, '}');
|
||||
end;
|
||||
else begin
|
||||
LLStreamWrite(p.outp, p.x[j]);
|
||||
@@ -185,15 +156,22 @@ begin
|
||||
end
|
||||
end
|
||||
end;
|
||||
LLStreamWrite(p.outp, ') & "')
|
||||
LLStreamWrite(p.outp, ')');
|
||||
LLStreamWrite(p.outp, p.conc);
|
||||
LLStreamWrite(p.outp, '"');
|
||||
end;
|
||||
'A'..'Z', 'a'..'z', '_': begin
|
||||
LLStreamWrite(p.outp, '" & $');
|
||||
'a'..'z', 'A'..'Z', #128..#255: begin
|
||||
LLStreamWrite(p.outp, '"');
|
||||
LLStreamWrite(p.outp, p.conc);
|
||||
LLStreamWrite(p.outp, p.toStr);
|
||||
LLStreamWrite(p.outp, '(');
|
||||
while p.x[j] in PatternChars do begin
|
||||
LLStreamWrite(p.outp, p.x[j]);
|
||||
inc(j)
|
||||
end;
|
||||
LLStreamWrite(p.outp, ' & "')
|
||||
LLStreamWrite(p.outp, ')');
|
||||
LLStreamWrite(p.outp, p.conc);
|
||||
LLStreamWrite(p.outp, '"')
|
||||
end;
|
||||
else if p.x[j] = p.subsChar then begin
|
||||
LLStreamWrite(p.outp, p.subsChar);
|
||||
@@ -203,7 +181,7 @@ begin
|
||||
p.info.col := int16(j);
|
||||
liMessage(p.info, errInvalidExpression, '$'+'');
|
||||
end
|
||||
end;
|
||||
end
|
||||
end
|
||||
else begin
|
||||
LLStreamWrite(p.outp, p.x[j]);
|
||||
@@ -215,56 +193,30 @@ begin
|
||||
end
|
||||
end;
|
||||
|
||||
function ParseTmpl(var p: TTmplParser): PNode;
|
||||
function filterTmpl(input: PLLStream; const filename: string;
|
||||
call: PNode): PLLStream;
|
||||
var
|
||||
q: TParser;
|
||||
begin
|
||||
while not LLStreamAtEnd(p.inp) do begin
|
||||
p.x := LLStreamReadLine(p.inp) {@ignore} + #0 {@emit};
|
||||
p.info.line := p.info.line + int16(1);
|
||||
parseLine(p);
|
||||
end;
|
||||
newLine(p);
|
||||
if gVerbosity >= 2 then begin
|
||||
rawMessage(hintCodeBegin);
|
||||
messageOut(p.outp.s);
|
||||
rawMessage(hintCodeEnd);
|
||||
end;
|
||||
openParser(q, toFilename(p.info), p.outp);
|
||||
result := ParseModule(q);
|
||||
closeParser(q);
|
||||
end;
|
||||
|
||||
procedure openTmplParser(var p: TTmplParser; const filename: string;
|
||||
inputStream: PLLStream);
|
||||
p: TTmplParser;
|
||||
begin
|
||||
{@ignore}
|
||||
FillChar(p, sizeof(p), 0);
|
||||
{@emit}
|
||||
p.info := newLineInfo(filename, 0, 0);
|
||||
p.outp := LLStreamOpen('');
|
||||
p.inp := inputStream;
|
||||
p.subsChar := '$';
|
||||
end;
|
||||
|
||||
procedure CloseTmplParser(var p: TTmplParser);
|
||||
begin
|
||||
p.inp := input;
|
||||
p.subsChar := charArg(call, 'subschar', 1, '$');
|
||||
p.nimDirective := charArg(call, 'metachar', 2, '#');
|
||||
p.emit := strArg(call, 'emit', 3, 'result.add');
|
||||
p.conc := strArg(call, 'conc', 4, ' & ');
|
||||
p.toStr := strArg(call, 'tostring', 5, '$'+'');
|
||||
while not LLStreamAtEnd(p.inp) do begin
|
||||
p.x := LLStreamReadLine(p.inp) {@ignore} + #0 {@emit};
|
||||
p.info.line := p.info.line + int16(1);
|
||||
parseLine(p);
|
||||
end;
|
||||
newLine(p);
|
||||
result := p.outp;
|
||||
LLStreamClose(p.inp);
|
||||
end;
|
||||
|
||||
function ParseTmplFile(const filename: string): PNode;
|
||||
var
|
||||
p: TTmplParser;
|
||||
f: TBinaryFile;
|
||||
begin
|
||||
if not OpenFile(f, filename) then begin
|
||||
rawMessage(errCannotOpenFile, filename);
|
||||
result := nil;
|
||||
exit
|
||||
end;
|
||||
OpenTmplParser(p, filename, LLStreamOpen(f));
|
||||
result := ParseTmpl(p);
|
||||
CloseTmplParser(p);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -374,15 +374,34 @@ begin
|
||||
end
|
||||
end;
|
||||
|
||||
function isAssignable(n: PNode): bool;
|
||||
type
|
||||
TAssignableResult = (
|
||||
arNone, // no l-value and no discriminant
|
||||
arLValue, // is an l-value
|
||||
arDiscriminant // is a discriminant
|
||||
);
|
||||
|
||||
function isAssignable(n: PNode): TAssignableResult;
|
||||
begin
|
||||
result := false;
|
||||
result := arNone;
|
||||
case n.kind of
|
||||
nkSym: result := (n.sym.kind in [skVar, skTemp]);
|
||||
nkDotExpr, nkQualified, nkBracketExpr: begin
|
||||
nkSym: begin
|
||||
if (n.sym.kind in [skVar, skTemp]) then
|
||||
result := arLValue
|
||||
end;
|
||||
nkDotExpr: begin
|
||||
checkMinSonsLen(n, 1);
|
||||
if skipTypes(n.sons[0].typ, abstractInst).kind in [tyVar, tyPtr, tyRef] then
|
||||
result := true
|
||||
result := arLValue
|
||||
else
|
||||
result := isAssignable(n.sons[0]);
|
||||
if (result = arLValue) and (sfDiscriminant in n.sons[1].sym.flags) then
|
||||
result := arDiscriminant
|
||||
end;
|
||||
nkBracketExpr: begin
|
||||
checkMinSonsLen(n, 1);
|
||||
if skipTypes(n.sons[0].typ, abstractInst).kind in [tyVar, tyPtr, tyRef] then
|
||||
result := arLValue
|
||||
else
|
||||
result := isAssignable(n.sons[0]);
|
||||
end;
|
||||
@@ -393,7 +412,7 @@ begin
|
||||
if skipTypes(n.typ, abstractPtrs).kind in [tyOpenArray, tyTuple, tyObject] then
|
||||
result := isAssignable(n.sons[1])
|
||||
end;
|
||||
nkHiddenDeref, nkDerefExpr: result := true;
|
||||
nkHiddenDeref, nkDerefExpr: result := arLValue;
|
||||
nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||
result := isAssignable(n.sons[0]);
|
||||
else begin end
|
||||
@@ -409,7 +428,7 @@ begin
|
||||
else begin
|
||||
result := newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ));
|
||||
addSon(result, n);
|
||||
if not isAssignable(n) then begin
|
||||
if isAssignable(n) <> arLValue then begin
|
||||
liMessage(n.info, errVarForOutParamNeeded);
|
||||
end
|
||||
end
|
||||
@@ -425,7 +444,7 @@ begin
|
||||
result := newHiddenAddrTaken(c, n);
|
||||
end
|
||||
end;
|
||||
nkDotExpr, nkQualified: begin
|
||||
nkDotExpr: begin
|
||||
checkSonsLen(n, 2);
|
||||
if n.sons[1].kind <> nkSym then
|
||||
internalError(n.info, 'analyseIfAddressTaken');
|
||||
@@ -499,7 +518,7 @@ begin
|
||||
prc := n.sons[0];
|
||||
checkMinSonsLen(n, 1);
|
||||
case n.sons[0].kind of
|
||||
nkDotExpr, nkQualified: begin
|
||||
nkDotExpr: begin
|
||||
checkSonsLen(n.sons[0], 2);
|
||||
n.sons[0] := semDotExpr(c, n.sons[0]);
|
||||
if n.sons[0].kind = nkDotCall then begin // it is a static call!
|
||||
@@ -595,7 +614,7 @@ begin
|
||||
else
|
||||
result := SymtabGet(c.Tab, n.ident); // no need for stub loading
|
||||
end;
|
||||
nkDotExpr, nkQualified: begin
|
||||
nkDotExpr: begin
|
||||
result := nil;
|
||||
if onlyCurrentScope then exit;
|
||||
checkSonsLen(n, 2);
|
||||
@@ -1174,8 +1193,14 @@ begin
|
||||
liMessage(n.info, errATypeHasNoValue);
|
||||
case s.kind of
|
||||
skProc, skMethod, skIterator, skConverter: begin
|
||||
if (s.magic <> mNone) then
|
||||
liMessage(n.info, errInvalidContextForBuiltinX, s.name.s);
|
||||
if not (sfProcVar in s.flags)
|
||||
and (s.typ.callConv = ccDefault)
|
||||
and (getModule(s).id <> c.module.id) then
|
||||
liMessage(n.info, warnXisPassedToProcVar, s.name.s);
|
||||
// XXX change this to errXCannotBePassedToProcVar after version 0.8.2
|
||||
// TODO VERSION 0.8.4
|
||||
//if (s.magic <> mNone) then
|
||||
// liMessage(n.info, errInvalidContextForBuiltinX, s.name.s);
|
||||
result := symChoice(c, n, s);
|
||||
end;
|
||||
skConst: begin
|
||||
@@ -1253,10 +1278,7 @@ begin
|
||||
(*s := n.sym;
|
||||
include(s.flags, sfUsed);
|
||||
if (s.kind = skType) and not (efAllowType in flags) then
|
||||
liMessage(n.info, errATypeHasNoValue);
|
||||
if (s.magic <> mNone) and
|
||||
(s.kind in [skProc, skMethod, skIterator, skConverter]) then
|
||||
liMessage(n.info, errInvalidContextForBuiltinX, s.name.s); *)
|
||||
liMessage(n.info, errATypeHasNoValue);*)
|
||||
// because of the changed symbol binding, this does not mean that we
|
||||
// don't have to check the symbol for semantics here again!
|
||||
result := semSym(c, n, n.sym, flags);
|
||||
@@ -1279,7 +1301,7 @@ begin
|
||||
if result.typ = nil then result.typ := getSysType(tyString);
|
||||
nkCharLit:
|
||||
if result.typ = nil then result.typ := getSysType(tyChar);
|
||||
nkQualified, nkDotExpr: begin
|
||||
nkDotExpr: begin
|
||||
result := semDotExpr(c, n, flags);
|
||||
if result.kind = nkDotCall then begin
|
||||
result.kind := nkCall;
|
||||
@@ -1364,7 +1386,8 @@ begin
|
||||
result := n;
|
||||
checkSonsLen(n, 1);
|
||||
n.sons[0] := semExprWithType(c, n.sons[0]);
|
||||
if not isAssignable(n.sons[0]) then liMessage(n.info, errExprHasNoAddress);
|
||||
if isAssignable(n.sons[0]) <> arLValue then
|
||||
liMessage(n.info, errExprHasNoAddress);
|
||||
n.typ := makePtrType(c, n.sons[0].typ);
|
||||
end;
|
||||
nkHiddenAddr, nkHiddenDeref: begin
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
248
nim/syntaxes.pas
248
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
|
||||
end;
|
||||
|
||||
function ParseFile(const filename: string): PNode;
|
||||
var
|
||||
syntaxes: array [TSyntaxes] of TSyntax;
|
||||
|
||||
procedure addSyntax(const s: TSyntax);
|
||||
var
|
||||
len: int;
|
||||
p: TParsers;
|
||||
f: TBinaryFile;
|
||||
begin
|
||||
len := length(syntaxes);
|
||||
setLength(syntaxes, len+1);
|
||||
syntaxes[len] := s;
|
||||
if not OpenFile(f, filename) then begin
|
||||
rawMessage(errCannotOpenFile, filename);
|
||||
exit
|
||||
end;
|
||||
OpenParsers(p, filename, LLStreamOpen(f));
|
||||
result := ParseAll(p);
|
||||
CloseParsers(p);
|
||||
end;
|
||||
|
||||
function parseAll(var p: TParsers): PNode;
|
||||
begin
|
||||
case p.skin of
|
||||
skinStandard: result := pnimsyn.parseAll(p.parser);
|
||||
skinBraces, skinEndX: InternalError('parser to implement');
|
||||
// skinBraces: result := pbraces.parseAll(p.parser);
|
||||
// skinEndX: result := pendx.parseAll(p.parser);
|
||||
end
|
||||
end;
|
||||
|
||||
function parseTopLevelStmt(var p: TParsers): PNode;
|
||||
begin
|
||||
case p.skin of
|
||||
skinStandard: result := pnimsyn.parseTopLevelStmt(p.parser);
|
||||
skinBraces, skinEndX: InternalError('parser to implement');
|
||||
//skinBraces: result := pbraces.parseTopLevelStmt(p.parser);
|
||||
//skinEndX: result := pendx.parseTopLevelStmt(p.parser);
|
||||
end
|
||||
end;
|
||||
|
||||
function UTF8_BOM(const s: string): int;
|
||||
begin
|
||||
if (s[strStart] = #239) and (s[strStart+1] = #187)
|
||||
and (s[strStart+2] = #191) then result := 3
|
||||
else result := 0
|
||||
end;
|
||||
|
||||
function containsShebang(const s: string; i: int): bool;
|
||||
var
|
||||
j: int;
|
||||
begin
|
||||
result := false;
|
||||
if (s[i] = '#') and (s[i+1] = '!') then begin
|
||||
j := i+2;
|
||||
while s[j] in WhiteSpace do inc(j);
|
||||
result := s[j] = '/'
|
||||
end
|
||||
end;
|
||||
|
||||
function parsePipe(const filename: string; inputStream: PLLStream): PNode;
|
||||
var
|
||||
line: string;
|
||||
s: PLLStream;
|
||||
i: int;
|
||||
q: TParser;
|
||||
begin
|
||||
result := nil;
|
||||
s := LLStreamOpen(filename, fmRead);
|
||||
if s <> nil then begin
|
||||
line := LLStreamReadLine(s) {@ignore} + #0 {@emit};
|
||||
i := UTF8_Bom(line) + strStart;
|
||||
if containsShebang(line, i) then begin
|
||||
line := LLStreamReadLine(s) {@ignore} + #0 {@emit};
|
||||
i := strStart;
|
||||
end;
|
||||
if (line[i] = '#') and (line[i+1] = '!') then begin
|
||||
inc(i, 2);
|
||||
while line[i] in WhiteSpace do inc(i);
|
||||
OpenParser(q, filename, LLStreamOpen(ncopy(line, i)));
|
||||
result := pnimsyn.parseAll(q);
|
||||
CloseParser(q);
|
||||
end;
|
||||
LLStreamClose(s);
|
||||
end
|
||||
end;
|
||||
|
||||
function getFilter(ident: PIdent): TFilterKind;
|
||||
var
|
||||
i: TFilterKind;
|
||||
begin
|
||||
for i := low(TFilterKind) to high(TFilterKind) do
|
||||
if IdentEq(ident, filterNames[i]) then begin
|
||||
result := i; exit
|
||||
end;
|
||||
result := filtNone
|
||||
end;
|
||||
|
||||
function getParser(ident: PIdent): TParserKind;
|
||||
var
|
||||
i: TParserKind;
|
||||
begin
|
||||
for i := low(TParserKind) to high(TParserKind) do
|
||||
if IdentEq(ident, parserNames[i]) then begin
|
||||
result := i; exit
|
||||
end;
|
||||
rawMessage(errInvalidDirectiveX, ident.s);
|
||||
end;
|
||||
|
||||
function getCallee(n: PNode): PIdent;
|
||||
begin
|
||||
if (n.kind = nkCall) and (n.sons[0].kind = nkIdent) then
|
||||
result := n.sons[0].ident
|
||||
else if n.kind = nkIdent then result := n.ident
|
||||
else rawMessage(errXNotAllowedHere, renderTree(n));
|
||||
end;
|
||||
|
||||
function applyFilter(var p: TParsers; n: PNode; const filename: string;
|
||||
input: PLLStream): PLLStream;
|
||||
var
|
||||
ident: PIdent;
|
||||
f: TFilterKind;
|
||||
begin
|
||||
ident := getCallee(n);
|
||||
f := getFilter(ident);
|
||||
case f of
|
||||
filtNone: begin
|
||||
p.skin := getParser(ident);
|
||||
result := input
|
||||
end;
|
||||
filtTemplate: result := filterTmpl(input, filename, n);
|
||||
filtStrip: result := filterStrip(input, filename, n);
|
||||
filtReplace: result := filterReplace(input, filename, n);
|
||||
end;
|
||||
if f <> filtNone then begin
|
||||
if gVerbosity >= 2 then begin
|
||||
rawMessage(hintCodeBegin);
|
||||
messageOut(result.s);
|
||||
rawMessage(hintCodeEnd);
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
||||
function evalPipe(var p: TParsers; n: PNode; const filename: string;
|
||||
start: PLLStream): PLLStream;
|
||||
var
|
||||
i: int;
|
||||
begin
|
||||
result := start;
|
||||
if n = nil then exit;
|
||||
if (n.kind = nkInfix) and (n.sons[0].kind = nkIdent)
|
||||
and IdentEq(n.sons[0].ident, '|'+'') then begin
|
||||
for i := 1 to 2 do begin
|
||||
if n.sons[i].kind = nkInfix then
|
||||
result := evalPipe(p, n.sons[i], filename, result)
|
||||
else
|
||||
result := applyFilter(p, n.sons[i], filename, result)
|
||||
end
|
||||
end
|
||||
else if n.kind = nkStmtList then
|
||||
result := evalPipe(p, n.sons[0], filename, result)
|
||||
else
|
||||
result := applyFilter(p, n, filename, result)
|
||||
end;
|
||||
|
||||
procedure openParsers(var p: TParsers; const filename: string;
|
||||
inputstream: PLLStream);
|
||||
var
|
||||
pipe: PNode;
|
||||
s: PLLStream;
|
||||
begin
|
||||
p.skin := skinStandard;
|
||||
pipe := parsePipe(filename, inputStream);
|
||||
if pipe <> nil then
|
||||
s := evalPipe(p, pipe, filename, inputStream)
|
||||
else
|
||||
s := inputStream;
|
||||
case p.skin of
|
||||
skinStandard, skinBraces, skinEndX:
|
||||
pnimsyn.openParser(p.parser, filename, s);
|
||||
end
|
||||
end;
|
||||
|
||||
procedure closeParsers(var p: TParsers);
|
||||
begin
|
||||
pnimsyn.closeParser(p.parser);
|
||||
end;
|
||||
|
||||
initialization
|
||||
syntaxes[synStandard].name = 'Standard';
|
||||
|
||||
end.
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#! stdtmpl
|
||||
#proc sunsetTemplate*(current, ticker, content: string,
|
||||
# tabs: openarray[array[0..1, string]]): string =
|
||||
# result = ""
|
||||
|
||||
16
tests/tadrdisc.nim
Executable file
16
tests/tadrdisc.nim
Executable file
@@ -0,0 +1,16 @@
|
||||
# Test that the address of a dicriminants cannot be taken
|
||||
|
||||
type
|
||||
TKind = enum ka, kb, kc
|
||||
TA = object
|
||||
case k: TKind
|
||||
of ka: x, y: int
|
||||
of kb: a, b: string
|
||||
of kc: c, d: float
|
||||
|
||||
proc setKind(k: var TKind) =
|
||||
k = kc
|
||||
|
||||
var a: TA
|
||||
setKind(a.k) #ERROR_MSG for a 'var' type a variable needs to be passed
|
||||
|
||||
14
tests/tbind1.nim
Executable file
14
tests/tbind1.nim
Executable file
@@ -0,0 +1,14 @@
|
||||
# Test the new ``bind`` keyword for templates
|
||||
|
||||
proc p1(x: int8, y: int): int = return x + y
|
||||
|
||||
template tempBind(x, y: expr): expr =
|
||||
bind p1(x, y)
|
||||
|
||||
proc p1(x: int, y: int8): int = return x - y
|
||||
|
||||
# This is tricky: the call to ``p1(1'i8, 2'i8)`` should not fail in line 6,
|
||||
# because it is not ambiguous there. But it is ambiguous after line 8.
|
||||
|
||||
echo tempBind(1'i8, 2'i8) #OUT 3
|
||||
|
||||
10
tests/tbind2.nim
Executable file
10
tests/tbind2.nim
Executable file
@@ -0,0 +1,10 @@
|
||||
# Test the new ``bind`` keyword for templates
|
||||
|
||||
proc p1(x: int8, y: int): int = return x + y
|
||||
proc p1(x: int, y: int8): int = return x - y
|
||||
|
||||
template tempBind(x, y: expr): expr =
|
||||
bind p1(x, y) #ERROR_MSG ambiguous call
|
||||
|
||||
echo tempBind(1'i8, 2'i8)
|
||||
|
||||
25
tests/tbintre2.nim
Executable file
25
tests/tbintre2.nim
Executable file
@@ -0,0 +1,25 @@
|
||||
# Same test, but check module boundaries
|
||||
|
||||
import tbintree
|
||||
|
||||
var
|
||||
root: PBinaryTree[string]
|
||||
x = newNode("hallo")
|
||||
add(root, x)
|
||||
add(root, "world")
|
||||
if find(root, "world"):
|
||||
for str in items(root):
|
||||
stdout.write(str)
|
||||
else:
|
||||
stdout.writeln("BUG")
|
||||
|
||||
var
|
||||
r2: PBinaryTree[int]
|
||||
add(r2, newNode(110))
|
||||
add(r2, 223)
|
||||
add(r2, 99)
|
||||
for y in items(r2):
|
||||
stdout.write(y)
|
||||
|
||||
#OUT halloworld99110223
|
||||
|
||||
@@ -4,29 +4,29 @@ type
|
||||
tenum = enum eA, eB, eC
|
||||
|
||||
var
|
||||
x: string
|
||||
x: string = "yyy"
|
||||
y: Tenum = eA
|
||||
i: int
|
||||
|
||||
case y
|
||||
of eA: write(stdout, "a\n")
|
||||
of eB, eC: write(stdout, "b oder c\n")
|
||||
of eA: write(stdout, "a")
|
||||
of eB, eC: write(stdout, "b or c")
|
||||
|
||||
x = readLine(stdin)
|
||||
case x
|
||||
of "Andreas", "Rumpf": write(stdout, "Hallo Meister!\n")
|
||||
of "aa", "bb": write(stdout, "Du bist nicht mein Meister\n")
|
||||
of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
|
||||
of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
|
||||
of "cc", "hash", "when": nil
|
||||
of "will", "it", "finally", "be", "generated": nil
|
||||
else: write(stdout, "das sollte nicht passieren!\N")
|
||||
|
||||
case i
|
||||
of 0..5, 8, 9: nil
|
||||
of 1..5, 8, 9: nil
|
||||
of 6, 7: nil
|
||||
elif x == "Ha":
|
||||
nil
|
||||
elif x == "Ho":
|
||||
nil
|
||||
elif x == "yyy":
|
||||
write(stdout, x)
|
||||
else:
|
||||
nil
|
||||
|
||||
#OUT ayyy
|
||||
|
||||
|
||||
11
tests/tcnstseq.nim
Executable file
11
tests/tcnstseq.nim
Executable file
@@ -0,0 +1,11 @@
|
||||
# Test the new implicit conversion from sequences to arrays in a constant
|
||||
# context.
|
||||
|
||||
import strutils
|
||||
|
||||
const
|
||||
myWords = "Angelika Anne Anna Anka Anja".split()
|
||||
|
||||
for x in items(myWords):
|
||||
write(stdout, x) #OUT AngelikaAnneAnnaAnkaAnja
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -157,7 +157,7 @@ proc main(options: string) =
|
||||
var comp = callCompiler(filename, options)
|
||||
if sameResults(filename, spec, comp): inc(passed)
|
||||
inc(total)
|
||||
# ensure that the examples at least compiles
|
||||
# ensure that the examples at least compile
|
||||
for filename in os.walkFiles("examples/*.nim"):
|
||||
var comp = callCompiler(filename, options)
|
||||
var shortfile = os.extractFilename(filename)
|
||||
|
||||
3
tests/titer4.nim
Executable file
3
tests/titer4.nim
Executable file
@@ -0,0 +1,3 @@
|
||||
|
||||
for x in {'a'..'z'}: #ERROR_MSG iterator within for loop context expected
|
||||
nil
|
||||
23
tests/tmultim1.nim
Executable file
23
tests/tmultim1.nim
Executable file
@@ -0,0 +1,23 @@
|
||||
# Test multi methods
|
||||
|
||||
type
|
||||
TExpr = object
|
||||
TLiteral = object of TExpr
|
||||
x: int
|
||||
TPlusExpr = object of TExpr
|
||||
a, b: ref TExpr
|
||||
|
||||
method eval(e: ref TExpr): int = quit "to override!"
|
||||
method eval(e: ref TLiteral): int = return e.x
|
||||
method eval(e: ref TPlusExpr): int = return eval(e.a) + eval(e.b)
|
||||
|
||||
proc newLit(x: int): ref TLiteral =
|
||||
new(result)
|
||||
result.x = x
|
||||
|
||||
proc newPlus(a, b: ref TExpr): ref TPlusExpr =
|
||||
new(result)
|
||||
result.a = a
|
||||
result.b = b
|
||||
|
||||
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) #OUT 7
|
||||
30
tests/tmultim2.nim
Executable file
30
tests/tmultim2.nim
Executable file
@@ -0,0 +1,30 @@
|
||||
# Test multi methods
|
||||
|
||||
type
|
||||
TThing = object
|
||||
TUnit = object of TThing
|
||||
x: int
|
||||
TParticle = object of TThing
|
||||
a, b: int
|
||||
|
||||
method collide(a, b: TThing) {.inline.} =
|
||||
quit "to override!"
|
||||
|
||||
method collide(a: TThing, b: TUnit) {.inline.} =
|
||||
write stdout, "collide: thing, unit "
|
||||
|
||||
method collide(a: TUnit, b: TThing) {.inline.} =
|
||||
write stdout, "collide: unit, thing "
|
||||
|
||||
proc test(a, b: TThing) {.inline.} =
|
||||
collide(a, b)
|
||||
|
||||
var
|
||||
a: TThing
|
||||
b, c: TUnit
|
||||
collide(b, c) # ambiguous unit, thing or thing, unit? -> prefer unit, thing!
|
||||
test(b, c)
|
||||
collide(a, b)
|
||||
#OUT collide: unit, thing collide: unit, thing collide: thing, unit
|
||||
|
||||
|
||||
17
tests/tobjcov.nim
Executable file
17
tests/tobjcov.nim
Executable file
@@ -0,0 +1,17 @@
|
||||
# Covariance is not type safe:
|
||||
|
||||
type
|
||||
TA = object
|
||||
a: int
|
||||
TB = object of TA
|
||||
b: array[0..5000_000, int]
|
||||
|
||||
proc ap(x: var TA) = x.a = -1
|
||||
proc bp(x: var TB) = x.b[high(x.b)] = -1
|
||||
|
||||
# in Nimrod proc (x: TB) is compatible to proc (x: TA),
|
||||
# but this is not type safe:
|
||||
var f: proc (x: var TA) = bp
|
||||
var a: TA
|
||||
f(a) # bp expects a TB, but gets a TA
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user