version 0.7.4

This commit is contained in:
Andreas Rumpf 2009-01-07 17:03:25 +01:00
commit 439aa2d04d
114 changed files with 13664 additions and 10110 deletions

View file

@ -3,7 +3,7 @@
# Feel free to edit the templates as you need.
split.item.toc = "20"
# too long entries in the table of contents get truncated
# too long entries in the table of contents wrap around
# after this number of characters
doc.section = """
@ -138,6 +138,10 @@ pre, span.tok {
color:black;
}
span.red {
color: #A80000;
}
/*
:Author: David Goodger
:Contact: goodger@python.org

View file

@ -33,6 +33,7 @@ path="$lib/extra"
debugger:off
line_dir:off
opt:speed
dead_code_elim:on
@end
# additional options always passed to the compiler:
@ -51,12 +52,6 @@ hint[LineTooLong]=off
@end
# Configuration for the LLVM GCC compiler:
@if windows:
llvm_gcc.path = r"$nimrod\dist\llvm-gcc4.2\bin"
@elif macosx:
llvm_gcc.path =
r"/Users/andreasrumpf/download/C/llvm-gcc4.2-2.3-x86-darwin8/bin"
@end
llvm_gcc.options.debug = "-g"
llvm_gcc.options.always = "-w"
llvm_gcc.options.speed = "-O2"

View file

@ -1,6 +1,6 @@
=======================================================
The Nimrod Compiler
Copyright (C) 2004-2008 Andreas Rumpf
Copyright (C) 2004-2009 Andreas Rumpf
=======================================================
This program is free software; you can redistribute it and/or

View file

@ -1,5 +1,5 @@
Advanced commands::
pas convert a Pascal file to Nimrod standard syntax
pas convert a Pascal file to Nimrod syntax
pretty pretty print the inputfile
gen_depend generate a DOT file containing the
module dependency graph

View file

@ -41,6 +41,7 @@
'sfCompileTime', # proc can be evaluated at compile time
'sfThreadVar', # variable is a thread variable
'sfMerge', # proc can be merged with itself
'sfDeadCodeElim', # dead code elimination for the module is turned on
],
'TypeFlag': [
@ -116,7 +117,7 @@
# end of atoms
'nkDotCall', # used to temporarily flag a nkCall node; this is used
# for transforming ``s.len`` to ``len(s)``
'nkCommand', # a call like ``p 2 4`` without parenthesis
'nkCommand', # a call like ``p 2, 4`` without parenthesis
'nkCall', # a call like p(x, y) or an operation like +(a, b)
'nkGenericCall', # a call with given type parameters
'nkExplicitTypeListCall', # a call with given explicit typelist
@ -125,6 +126,7 @@
'nkIdentDefs', # a definition like `a, b: typeDesc = expr`
# either typeDesc or expr may be nil; used in
# formal parameters, var statements, etc.
'nkVarTuple', # a ``var (a, b) = expr`` construct
'nkInfix', # a call like (a + b)
'nkPrefix', # a call like !a
'nkPostfix', # something like a! (also used for visibility)
@ -166,6 +168,7 @@
# end of expressions
'nkAsgn', # a = b
'nkFastAsgn', # internal node for a fast ``a = b`` (no string copy)
'nkDefaultTypeParam', # `ident = typeDesc` in generic parameters
'nkGenericParams', # generic parameters
'nkFormalParams', # formal parameters

View file

@ -24,6 +24,7 @@ Options:
--bound_checks:on|off code generation for bound checks ON|OFF
--overflow_checks:on|off code generation for over-/underflow checks ON|OFF
-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|lib generate a console|GUI application or a shared lib
-r, --run run the compiled program with given arguments

View file

@ -140,6 +140,7 @@
'FloatToStr',
'CStrToStr',
'StrToStr',
'EnumToStr',
# special ones:
'And',
@ -209,6 +210,8 @@
'NimrodMinor',
'NimrodPatch',
'CpuEndian',
'HostOS',
'HostCPU',
'NaN',
'Inf',
'NegInf',

View file

@ -142,7 +142,7 @@
'selector must be of an ordinal type'},
{'errOrdXMustNotBeNegative': 'ord($1) must not be negative'},
{'errLenXinvalid': 'len($1) must be less than 32768'},
{'errWrongNumberOfLoopVariables': 'wrong number of loop variables'},
{'errWrongNumberOfVariables': 'wrong number of variables'},
{'errExprCannotBeRaised': 'only objects can be raised'},
{'errBreakOnlyInLoop': "'break' only allowed in loop construct"},
{'errTypeXhasUnknownSize': "type '$1' has unknown size"},

View file

@ -1,38 +0,0 @@
#! /bin/sh
#
# Nimrod deinstallation script
# (c) 2008 Andreas Rumpf
#
if [ $# -eq 1 ] ; then
case $1 in
"/usr/bin")
rm -rf /usr/lib/nimrod
rm -rf /usr/share/nimrod/doc
rm -f /usr/bin/nimrod
rm -f /etc/nimrod.cfg
rm -f /etc/nimdoc.cfg || exit 1
;;
"/usr/local/bin")
rm -rf /usr/local/lib/nimrod
rm -rf /usr/local/share/nimrod/doc
rm -f /usr/local/bin/nimrod
rm -f /etc/nimrod.cfg
rm -f /etc/nimdoc.cfg || exit 1
;;
*)
rm -rf $1/nimrod || exit 1
;;
esac
echo "deinstallation successful"
else
echo "Nimrod deinstallation script"
echo "Usage: [sudo] sh deinstall.sh DIR"
echo "Where DIR may be:"
echo " /usr/bin"
echo " /usr/local/bin"
echo " /opt"
echo " <some other dir> (treated like '/opt')"
exit 1
fi

View file

@ -3,25 +3,28 @@
The documentation consists of several documents:
- | `First steps after installation <steps.html>`_
| Read this after installation for a quick introduction.
- | `Nimrod tutorial (part I) <tut1.html>`_
| The Nimrod tutorial part one deals with the basics.
- | `Nimrod tutorial (part II) <tut2.html>`_
| The Nimrod tutorial part two deals with the advanced language constructs.
- | `Nimrod manual <manual.html>`_
| Read this to get to know the Nimrod programming system.
| The Nimrod manual is a draft that will evolve into a proper specification.
- | `User guide for the Nimrod Compiler <nimrodc.html>`_
| The user guide lists command line arguments, Nimrodc's special features, etc.
| The user guide lists command line arguments, special features of the
compiler, etc.
- | `User guide for the Embedded Nimrod Debugger <endb.html>`_
| This document describes how to use the Embedded debugger. The embedded
debugger currently has no GUI. Please help!
| This document describes how to use the Embedded Debugger.
- | `Nimrod library documentation <lib.html>`_
| This document describes Nimrod's standard library.
- | `Nimrod internal documentation <intern.html>`_
| The internal documentation describes how the compiler is implemented. Read
this if you want to hack the compiler or develop advanced macros.
this if you want to hack the compiler.
- | `Index <theindex.html>`_
| The generated index. Often the quickest way to find the piece of

View file

@ -4,9 +4,19 @@ Short description of Nimrod's modules
============== ==========================================================
Module Description
============== ==========================================================
nimrod main module: parses the command line and calls
```main.MainCommand``
main implements the top-level command dispatching
lexbase buffer handling of the lexical analyser
scanner lexical analyser
pnimsyn Nimrod's parser
rnimsyn Nimrod code renderer (AST back to its textual form)
paslex lexer for Pascal
pasparse parser for Pascal; Pascal's advanced OO features are not
supported
options contains global and local compiler options
ast type definitions of the abstract syntax tree (AST) and
node constructors
astalgo algorithms for containers of AST nodes; converting the
@ -21,17 +31,20 @@ sigmatch contains the matching algorithm that is used for proc
semexprs contains the semantic checking phase for expressions
semstmts contains the semantic checking phase for statements
semtypes contains the semantic checking phase for types
semfold contains code to deal with constant folding
evals contains an AST interpreter for compile time evaluation
pragmas semantic checking of pragmas
idents implements a general mapping from identifiers to an internal
representation (``PIdent``) that is used, so that a simple
id-comparison suffices to say whether two Nimrod identifiers
are equivalent
ropes implements long strings using represented as trees for
ropes implements long strings represented as trees for
lazy evaluation; used mainly by the code generators
ccgobj contains type definitions neeeded for C code generation
and some helpers
transf transformations on the AST that need to be done before
code generation
cgen main file of the C code generator
ccgutils contains helpers for the C code generator
ccgtypes the generator for C types
ccgstmts the generator for statements

View file

@ -1,45 +1,43 @@
module ::= ([COMMENT] [SAD] stmt)*
comma ::= ',' [COMMENT] [IND]
operator ::= OP0 | OR | XOR | AND | OP3 | OP4 | OP5 | IS | ISNOT | IN | NOTIN
| OP6 | DIV | MOD | SHL | SHR | OP7 | NOT
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
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 )*
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)*
namedTypeOrExpr ::=
DOTDOT [expr]
| expr [EQUALS (expr [DOTDOT expr] | typeDescK | DOTDOT [expr] )
| DOTDOT [expr]]
'..' [expr]
| expr ['=' (expr ['..' expr] | typeDescK | '..' [expr]) | '..' [expr]]
| typeDescK
castExpr ::= CAST BRACKET_LE optInd typeDesc BRACKERT_RI
PAR_LE optInd expr PAR_RI
addrExpr ::= ADDR PAR_LE optInd expr PAR_RI
symbol ::= ACC (KEYWORD | IDENT | operator | PAR_LE PAR_RI
| BRACKET_LE BRACKET_RI | EQUALS | literal )+ ACC
castExpr ::= 'cast' '[' optInd typeDesc [SAD] ']' '(' optInd expr [SAD] ')'
addrExpr ::= 'addr' '(' optInd expr ')'
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')'
| '[' ']' | '=' | literal)+ '`'
| IDENT
primary ::= ( prefixOperator optInd )* ( symbol | constructor |
| castExpr | addrExpr ) (
DOT optInd symbol
#| CURLY_LE namedTypeDescList CURLY_RI
| PAR_LE optInd namedExprList PAR_RI
| BRACKET_LE optInd
[ namedTypeOrExpr (comma namedTypeOrExpr)* [comma] ]
BRACKET_RI
| CIRCUM
| pragma )*
primary ::= (prefixOperator optInd)* (symbol | constructor |
| castExpr | addrExpr) (
'.' optInd symbol
| '(' optInd namedExprList [SAD] ')'
| '[' optInd
[namedTypeOrExpr (comma namedTypeOrExpr)* [comma]]
[SAD] ']'
| '^'
| pragma)*
literal ::= INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
| FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
@ -48,48 +46,42 @@ literal ::= INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
| NIL
constructor ::= literal
| BRACKET_LE optInd colonExprList BRACKET_RI # []-Constructor
| CURLY_LE optInd sliceExprList CURLY_RI # {}-Constructor
| PAR_LE optInd colonExprList PAR_RI # ()-Constructor
| '[' optInd colonExprList [SAD] ']'
| '{' optInd sliceExprList [SAD] '}'
| '(' optInd colonExprList [SAD] ')'
exprList ::= [ expr (comma expr)* [comma] ]
colonExpr ::= expr [':' expr]
colonExprList ::= [colonExpr (comma colonExpr)* [comma]]
colonExpr ::= expr [COLON expr]
colonExprList ::= [ colonExpr (comma colonExpr)* [comma] ]
namedExpr ::= expr ['=' expr]
namedExprList ::= [namedExpr (comma namedExpr)* [comma]]
namedExpr ::= expr [EQUALS expr] # actually this is symbol EQUALS expr|expr
namedExprList ::= [ namedExpr (comma namedExpr)* [comma] ]
sliceExpr ::= expr ['..' expr]
sliceExprList ::= [sliceExpr (comma sliceExpr)* [comma]]
sliceExpr ::= expr [ DOTDOT expr ]
sliceExprList ::= [ sliceExpr (comma sliceExpr)* [comma] ]
anonymousProc ::= LAMBDA paramList [pragma] EQUALS stmt
anonymousProc ::= 'lambda' paramList [pragma] '=' stmt
expr ::= lowestExpr
| anonymousProc
| IF expr COLON expr
(ELIF expr COLON expr)*
ELSE COLON expr
| 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
namedTypeDesc ::= typeDescK | expr [EQUALS (typeDescK | expr)]
namedTypeDescList ::= [ namedTypeDesc (comma namedTypeDesc)* [comma] ]
namedTypeDesc ::= typeDescK | expr ['=' (typeDescK | expr)]
namedTypeDescList ::= [namedTypeDesc (comma namedTypeDesc)* [comma]]
qualifiedIdent ::= symbol [ DOT symbol ]
qualifiedIdent ::= symbol ['.' symbol]
typeDescK ::= VAR typeDesc
| REF typeDesc
| PTR typeDesc
| TYPE expr
| TUPLE tupleDesc
| PROC paramList [pragma]
typeDescK ::= 'var' typeDesc
| 'ref' typeDesc
| 'ptr' typeDesc
| 'type' expr
| 'tuple' tupleDesc
| 'proc' paramList [pragma]
typeDesc ::= typeDescK | primary
optSemicolon ::= [SEMICOLON]
macroStmt ::= COLON [stmt] (OF [sliceExprList] COLON stmt
| ELIF expr COLON stmt
| EXCEPT exceptList COLON stmt )*
[ELSE COLON stmt]
macroStmt ::= ':' [stmt] ('of' [sliceExprList] ':' stmt
|'elif' expr ':' stmt
|'except' exceptList ':' stmt )*
['else' ':' stmt]
simpleStmt ::= returnStmt
| yieldStmt
@ -107,88 +99,91 @@ complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
| procDecl | iteratorDecl | macroDecl | templateDecl
| constSection | typeSection | whenStmt | varSection
indPush ::= IND # push
indPush ::= IND # and push indentation onto the stack
indPop ::= # pop indentation from the stack
stmt ::= simpleStmt [SAD]
| indPush (complexStmt | simpleStmt)
([SAD] (complexStmt | simpleStmt) )*
DED
([SAD] (complexStmt | simpleStmt))*
DED indPop
exprStmt ::= lowestExpr [EQUALS expr | [expr (comma expr)* [comma]] [macroStmt]]
returnStmt ::= RETURN [expr]
yieldStmt ::= YIELD expr
discardStmt ::= DISCARD expr
raiseStmt ::= RAISE [expr]
breakStmt ::= BREAK [symbol]
continueStmt ::= CONTINUE
ifStmt ::= IF expr COLON stmt (ELIF expr COLON stmt)* [ELSE COLON stmt]
whenStmt ::= WHEN expr COLON stmt (ELIF expr COLON stmt)* [ELSE COLON stmt]
caseStmt ::= CASE expr (OF sliceExprList COLON stmt)*
(ELIF expr COLON stmt)*
[ELSE COLON stmt]
whileStmt ::= WHILE expr COLON stmt
forStmt ::= FOR symbol (comma symbol)* [comma] IN expr [DOTDOT expr] COLON stmt
exceptList ::= [qualifiedIdent (comma qualifiedIdent)* [comma]]
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 COLON stmt
(EXCEPT exceptList COLON stmt)*
[FINALLY COLON stmt]
asmStmt ::= ASM [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
blockStmt ::= BLOCK [symbol] COLON stmt
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)* [comma]
includeStmt ::= INCLUDE filename (comma filename)* [comma]
fromStmt ::= FROM filename IMPORT symbol (comma symbol)* [comma]
importStmt ::= 'import' filename (comma filename)*
includeStmt ::= 'include' filename (comma filename)*
fromStmt ::= 'from' filename 'import' symbol (comma symbol)*
pragma ::= CURLYDOT_LE colonExprList (CURLYDOT_RI | CURLY_RI)
pragma ::= '{.' optInd (colonExpr [comma])* [SAD] ('.}' | '}')
param ::= symbol (comma symbol)* [comma] COLON typeDesc
paramList ::= [PAR_LE [param (comma param)* [comma]] PAR_RI] [COLON typeDesc]
param ::= symbol (comma symbol)* ':' typeDesc
paramList ::= ['(' [param (comma param)*] [SAD] ')'] [':' typeDesc]
genericParams ::= BRACKET_LE (symbol [EQUALS typeDesc] )* BRACKET_RI
genericParam ::= symbol [':' typeDesc]
genericParams ::= '[' genericParam (comma genericParam)* [SAD] ']'
procDecl ::= PROC symbol ["*"] [genericParams]
paramList [pragma]
[EQUALS stmt]
macroDecl ::= MACRO symbol ["*"] [genericParams] paramList [pragma]
[EQUALS stmt]
iteratorDecl ::= ITERATOR symbol ["*"] [genericParams] paramList [pragma]
[EQUALS stmt]
templateDecl ::= TEMPLATE symbol ["*"] [genericParams] paramList [pragma]
[EQUALS stmt]
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]
colonAndEquals ::= [COLON typeDesc] EQUALS expr
colonAndEquals ::= [':' typeDesc] '=' expr
constDecl ::= symbol ["*"] [pragma] colonAndEquals [COMMENT | IND COMMENT]
constDecl ::= symbol ['*'] [pragma] colonAndEquals [COMMENT | IND COMMENT]
| COMMENT
constSection ::= CONST indPush constDecl (SAD constDecl)* DED
constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop
typeDef ::= typeDesc | objectDef | enumDef
objectField ::= symbol ["*"] [pragma]
objectField ::= symbol ['*'] [pragma]
objectIdentPart ::=
objectField (comma objectField)* [comma] COLON typeDesc [COMMENT|IND COMMENT]
objectField (comma objectField)* ':' typeDesc [COMMENT|IND COMMENT]
objectWhen ::= WHEN expr COLON [COMMENT] objectPart
(ELIF expr COLON [COMMENT] objectPart)*
[ELSE COLON [COMMENT] objectPart]
objectCase ::= CASE expr COLON typeDesc [COMMENT]
(OF sliceExprList COLON [COMMENT] objectPart)*
[ELSE COLON [COMMENT] objectPart]
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
tupleDesc ::= BRACKET_LE optInd [param (comma param)* [comma]] BRACKET_RI
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 [EQUALS expr]
enumDef ::= ENUM [OF typeDesc] (enumField [comma | COMMENT | IND COMMENT])+
objectDef ::= 'object' [pragma] ['of' typeDesc] objectPart
enumField ::= symbol ['=' expr]
enumDef ::= 'enum' ['of' typeDesc] (enumField [comma] [COMMENT | IND COMMENT])+
typeDecl ::= COMMENT
| symbol ["*"] [genericParams] [EQUALS typeDef] [COMMENT | IND COMMENT]
| symbol ['*'] [genericParams] ['=' typeDef] [COMMENT | IND COMMENT]
typeSection ::= TYPE indPush typeDecl (SAD typeDecl)* DED
typeSection ::= 'type' indPush typeDecl (SAD typeDecl)* DED indPop
colonOrEquals ::= COLON typeDesc [EQUALS expr] | EQUALS expr
varField ::= symbol ["*"] [pragma]
varPart ::= symbol (comma symbol)* [comma] colonOrEquals [COMMENT | IND COMMENT]
varSection ::= VAR (varPart
| indPush (COMMENT|varPart) (SAD (COMMENT|varPart))* DED)
colonOrEquals ::= ':' typeDesc ['=' expr] | '=' expr
varField ::= symbol ['*'] [pragma]
varPart ::= symbol (comma symbol)* colonOrEquals [COMMENT | IND COMMENT]
varSection ::= 'var' (varPart
| indPush (COMMENT|varPart)
(SAD (COMMENT|varPart))* DED indPop)

View file

@ -34,7 +34,6 @@ Path Purpose
on it!
``web`` website of Nimrod; generated by ``koch.py``
from the ``*.txt`` and ``*.tmpl`` files
``koch`` the Koch Build System (written for Nimrod)
``obj`` generated ``*.obj`` files go into here
============ ==============================================
@ -45,44 +44,76 @@ Bootstrapping the compiler
The compiler is written in a subset of Pascal with special annotations so
that it can be translated to Nimrod code automatically. This conversion is
done by Nimrod itself via the undocumented ``boot`` command. Thus both Nimrod
and Free Pascal can compile the Nimrod compiler.
and Free Pascal can compile the Nimrod compiler. However, the Pascal version
has no garbage collector and leaks memory like crazy! So the Pascal version
should only be used for bootstrapping.
Requirements for bootstrapping:
- Free Pascal (I used version 2.2) [optional]
- Python (should work with version 1.5 or higher)
- Python (should work with version 1.5 or higher) (optional)
- supported C compiler
- C compiler -- one of:
Compiling the compiler is a simple matter of running::
* win32-lcc (currently broken)
* Borland C++ (tested with 5.5; currently broken)
* Microsoft C++
* Digital Mars C++
* Watcom C++ (currently broken)
* GCC
* Intel C++
* Pelles C (currently broken)
* llvm-gcc
koch.py boot
| Compiling the compiler is a simple matter of running:
| ``koch.py boot``
| Or you can compile by hand, this is not difficult.
For a release version use::
If you want to debug the compiler, use the command::
koch.py boot -d:release
koch.py boot --debugger:on
The ``koch.py`` script is Nimrod's maintainance script. It is a replacement for
make and shell scripting with the advantage that it is much more portable.
The ``koch.py`` script is Nimrod's maintainance script: Everything that has
been automated is accessible with it. It is a replacement for make and shell
scripting with the advantage that it is more portable.
If you don't have Python, there is a ``boot`` Nimrod program which does roughly
the same::
nimrod cc boot.nim
./boot [-d:release]
Coding standards
================
Pascal annotations
==================
There are some annotations that the Pascal sources use so that they can
be converted to Nimrod automatically:
The compiler is written in a subset of Pascal with special annotations so
that it can be translated to Nimrod code automatically. As a general rule,
Pascal code that does not translate to Nimrod automatically is forbidden.
``{@discard} <expr>``
Tells the compiler that a ``discard`` statement is needed for Nimrod
here.
``{@cast}typ(expr)``
Tells the compiler that the Pascal conversion is a ``cast`` in Nimrod.
``{@emit <code>}``
Emits ``<code>``. The code fragment needs to be in Pascal syntax.
``{@ignore} <codeA> {@emit <codeB>}``
Ignores ``<codeA>`` and instead emits ``<codeB>`` which needs to be in
Pascal syntax. An empty ``{@emit}`` is possible too (it then only closes
the ``<codeA>`` part).
``record {@tuple}``
Is used to tell the compiler that the record type should be transformed
to a Nimrod tuple type.
``^ {@ptr}``
Is used to tell the compiler that the pointer type should be transformed
to a Nimrod ``ptr`` type. The default is a ``ref`` type.
``'a' + ''``
The idiom ``+''`` is used to tell the compiler that it is a string
literal and not a character literal. (Pascal does not distinguish between
character literals and string literals of length 1.)
``+{&}``
This tells the compiler that Pascal's ``+`` here is a string concatenation
and thus should be converted to ``&``. Note that this is not needed if
any of the operands is a string literal because the compiler then can
figure this out by itself.
``{@set}['a', 'b', 'c']``
Tells the compiler that Pascal's ``[]`` constructor is a set and not an
array. This is only needed if the compiler cannot figure this out for
itself.
Porting to new platforms
@ -99,7 +130,7 @@ check that the OS, System modules work and recompile Nimrod.
The only case where things aren't as easy is when the garbage
collector needs some assembler tweaking to work. The standard
version of the GC uses C's ``setjmp`` function to store all registers
on the hardware stack. It may be that the new platform needs to
on the hardware stack. It may be necessary that the new platform needs to
replace this generic code by some assembler code.
@ -132,11 +163,11 @@ The Garbage Collector
Introduction
------------
We use the term *cell* here to refer to everything that is traced
I use the term *cell* here to refer to everything that is traced
(sequences, refs, strings).
This section describes how the new GC works.
The basic algorithm is *Deferrent reference counting* with cycle detection.
The basic algorithm is *Deferrent Reference Counting* with cycle detection.
References in the stack are not counted for better performance and easier C
code generation.
@ -170,7 +201,7 @@ modifying a ``TCellSet`` during traversation leads to undefined behaviour.
iterator elements(s: TCellSet): (elem: PCell)
All the operations have to be perform efficiently. Because a Cellset can
All the operations have to perform efficiently. Because a Cellset can
become huge a hash table alone is not suitable for this.
We use a mixture of bitset and hash table for this. The hash table maps *pages*
@ -246,16 +277,10 @@ This syntax tree is the interface between the parser and the code generator.
It is essential to understand most of the compiler's code.
In order to compile Nimrod correctly, type-checking has to be seperated from
parsing. Otherwise generics would not work. Code generation is done for a
whole module only after it has been checked for semantics.
parsing. Otherwise generics would not work.
.. include:: filelist.txt
The first command line argument selects the backend. Thus the backend is
responsible for calling the parser and semantic checker. However, when
compiling ``import`` or ``include`` statements, the semantic checker needs to
call the backend, this is done by embedding a PBackend into a TContext.
The syntax tree
---------------
@ -265,7 +290,7 @@ may contain cycles. The AST changes its shape after semantic checking. This
is needed to make life easier for the code generators. See the "ast" module
for the type definitions.
We use the notation ``nodeKind(fields, [sons])`` for describing
I use the notation ``nodeKind(fields, [sons])`` for describing
nodes. ``nodeKind[sons]`` is a short-cut for ``nodeKind([sons])``.
XXX: Description of the language's syntax and the corresponding trees.
@ -273,12 +298,16 @@ XXX: Description of the language's syntax and the corresponding trees.
How the RTL is compiled
=======================
The system module contains the part of the RTL which needs support by
The ``system`` module contains the part of the RTL which needs support by
compiler magic (and the stuff that needs to be in it because the spec
says so). The C code generator generates the C code for it just like any other
module. However, calls to some procedures like ``addInt`` are inserted by
the CCG. Therefore the module ``magicsys`` contains a table
(``compilerprocs``) with all symbols that are marked as ``compilerproc``.
the CCG. Therefore the module ``magicsys`` contains a table (``compilerprocs``)
with all symbols that are marked as ``compilerproc``. ``compilerprocs`` are
needed by the code generator. A ``magic`` proc is not the same as a
``compilerproc``: A ``magic`` is a proc that needs compiler magic for its
semantic checking, a ``compilerproc`` is a proc that is used by the code
generator.
@ -290,77 +319,3 @@ underlying C compiler already does all the hard work for us. The problem is the
common runtime library, especially the memory manager. Note that Borland's
Delphi had exactly the same problem. The workaround is to not link the GC with
the Dll and provide an extra runtime dll that needs to be initialized.
How to implement closures
=========================
A closure is a record of a proc pointer and a context ref. The context ref
points to a garbage collected record that contains the needed variables.
An example:
.. code-block:: Nimrod
type
TListRec = record
data: string
next: ref TListRec
proc forEach(head: ref TListRec, visitor: proc (s: string) {.closure.}) =
var it = head
while it != nil:
visit(it.data)
it = it.next
proc sayHello() =
var L = new List(["hallo", "Andreas"])
var temp = "jup\xff"
forEach(L, lambda(s: string) =
io.write(temp)
io.write(s)
)
This should become the following in C:
.. code-block:: C
typedef struct ... /* List type */
typedef struct closure {
void (*PrcPart)(string, void*);
void* ClPart;
}
typedef struct Tcl_data {
string temp; // all accessed variables are put in here!
}
void forEach(TListRec* head, const closure visitor) {
TListRec* it = head;
while (it != NIM_NULL) {
visitor.prc(it->data, visitor->cl_data);
it = it->next;
}
}
void printStr(string s, void* cl_data) {
Tcl_data* x = (Tcl_data*) cl_data;
io_write(x->temp);
io_write(s);
}
void sayhello() {
Tcl_data* data = new(...);
asgnRef(&data->temp, "jup\xff");
...
closure cl;
cl.prc = printStr;
cl.cl_data = data;
foreach(L, cl);
}
What about nested closure? - There's not much difference: Just put all used
variables in the data record.

View file

@ -11,6 +11,10 @@ Nimrod Manual
About this document
===================
**Note**: This document is a draft! Several of Nimrod's features need more
precise wording. This manual will evolve into a proper specification some
day.
This document describes the lexis, the syntax, and the semantics of Nimrod.
The language constructs are explained using an extended BNF, in
@ -18,10 +22,11 @@ which ``(a)*`` means 0 or more ``a``'s, ``a+`` means 1 or more ``a``'s, and
``(a)?`` means an optional *a*; an alternative spelling for optional parts is
``[a]``. The ``|`` symbol is used to mark alternatives
and has the lowest precedence. Parentheses may be used to group elements.
Non-terminals are in lowercase, terminal symbols (including keywords) are in
UPPERCASE. An example::
Non-terminals start with a lowercase letter, abstract terminal symbols are in
UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
with ``'``. An example::
if_stmt ::= IF expr COLON stmts (ELIF expr COLON stmts)* [ELSE stmts]
ifStmt ::= 'if' expr ':' stmts ('elif' expr ':' stmts)* ['else' stmts]
Other parts of Nimrod - like scoping rules or runtime semantics are only
described in an informal manner. The reason is that formal semantics are
@ -90,8 +95,7 @@ Indentation consists only of spaces; tabulators are not allowed.
The terminals ``IND`` (indentation), ``DED`` (dedentation) and ``SAD``
(same indentation) are generated by the scanner, denoting an indentation.
These terminals are only generated for lines that are not empty or contain
only whitespace and comments.
These terminals are only generated for lines that are not empty.
The parser and the scanner communicate over a stack which indentation terminal
should be generated: The stack consists of integers counting the spaces. The
@ -100,14 +104,17 @@ If the current indentation token consists of more spaces than the entry at the
top of the stack, a ``IND`` token is generated, else if it consists of the same
number of spaces, a ``SAD`` token is generated. If it consists of fewer spaces,
a ``DED`` token is generated for any item on the stack that is greater than the
current. These items are then popped from the stack by the scanner. At the end
current. These items are later popped from the stack by the parser. At the end
of the file, a ``DED`` token is generated for each number remaining on the
stack that is larger than zero.
Because the grammar contains some optional ``IND`` tokens, the scanner cannot
push new indentation levels. This has to be done by the parser. The symbol
``indPush`` indicates that an ``IND`` token is expected; the current number of
leading spaces is pushed onto the stack by the parser.
leading spaces is pushed onto the stack by the parser. The symbol ``indPop``
denotes that the parser pops an item from the indentation stack. No token is
consumed by ``indPop``.
Comments
--------
@ -131,8 +138,8 @@ aligned to the preceding one, it does not start a new comment:
Comments are tokens; they are only allowed at certain places in the input file
as they belong to the syntax tree! This feature enables perfect source-to-source
transformations (such as pretty-printing) and superior documentation generators.
A side-effect is that the human reader of the code always knows exactly which
code snippet the comment refers to.
A nice side-effect is that the human reader of the code always knows exactly
which code snippet the comment refers to.
Identifiers & Keywords
@ -159,9 +166,9 @@ case-sensitive and even underscores are ignored:
**type** is a reserved word, and so is **TYPE** or **T_Y_P_E**. The idea behind
this is that this allows programmers to use their own prefered spelling style
and libraries written by different programmers cannot use incompatible
conventions. The editors or IDE can show the identifiers as preferred. Another
advantage is that it frees the programmer from remembering the exact spelling
of an identifier.
conventions. A Nimrod-aware editor or IDE can show the identifiers as
preferred. Another advantage is that it frees the programmer from remembering
the exact spelling of an identifier.
Literal strings
@ -174,7 +181,7 @@ contain the following `escape sequences`:idx:\ :
Escape sequence Meaning
================== ===================================================
``\n`` `newline`:idx:
``\r`` `carriage return`:idx:
``\r``, ``\c`` `carriage return`:idx:
``\l`` `line feed`:idx:
``\f`` `form feed`:idx:
``\t`` `tabulator`:idx:
@ -184,8 +191,7 @@ contain the following `escape sequences`:idx:\ :
``\'`` `apostrophe`:idx:
``\d+`` `character with decimal value d`:idx:;
all decimal digits directly
following are used for the
character
following are used for the character
``\a`` `alert`:idx:
``\b`` `backspace`:idx:
``\e`` `escape`:idx: `[ESC]`:idx:
@ -194,15 +200,14 @@ contain the following `escape sequences`:idx:\ :
================== ===================================================
Strings in Nimrod may contain any 8-bit value, except embedded zeros
which are not allowed for compability with `C`:idx:.
Strings in Nimrod may contain any 8-bit value, except embedded zeros.
Literal strings can also be delimited by three double squotes
``"""`` ... ``"""``.
Literals in this form may run for several lines, may contain ``"`` and do not
interpret any escape sequences.
For convenience, when the opening ``"""`` is immediately
followed by a newline, the newline is not included in the string.
For convenience, when the opening ``"""`` is immediately followed by a newline,
the newline is not included in the string.
There are also `raw string literals` that are preceded with the letter ``r``
(or ``R``) and are delimited by matching double quotes (just like ordinary
string literals) and do not interpret the escape sequences. This is especially
@ -253,8 +258,8 @@ Numerical constants
As can be seen in the productions, numerical constants can contain unterscores
for readability. Integer and floating point literals may be given in decimal (no
prefix), binary (prefix ``0b``), octal (prefix ``0o``) and
hexadecimal (prefix ``0x``) notation.
prefix), binary (prefix ``0b``), octal (prefix ``0o``) and hexadecimal
(prefix ``0x``) notation.
There exists a literal for each numerical type that is
defined. The suffix starting with an apostophe ('\'') is called a
@ -262,7 +267,7 @@ defined. The suffix starting with an apostophe ('\'') is called a
unless the literal contains a dot or an ``E`` in which case it is of
type ``float``.
The following table specifies type suffixes:
The type suffixes are:
================= =========================
Type Suffix Resulting type of literal
@ -295,11 +300,11 @@ the three tokens `{`:tok:, `..`:tok:, `}`:tok: and not the two tokens
`{.`:tok:, `.}`:tok:.
In Nimrod one can define his own operators. An `operator`:idx: is any
combination of the following characters that are not listed above::
combination of the following characters that is not listed above::
+ - * / < >
= @ $ ~ & %
! ? ^ . |
! ? ^ . | \
These keywords are also operators:
``and or not xor shl shr div mod in notin is isnot``.
@ -348,16 +353,13 @@ Constants
cannot change. The compiler must be able to evaluate the expression in a
constant declaration at compile time.
..
Nimrod contains a sophisticated
compile-time evaluator, so procedures declared with the ``{.noSideEffect.}``
pragma can be used in constant expressions:
Nimrod contains a sophisticated compile-time evaluator, so procedures which
have no side-effect can be used in constant expressions too:
.. code-block:: nimrod
from strutils import findSubStr
const
x = findSubStr('a', "hallo") # x is 1; this is computed at compile time!
.. code-block:: nimrod
import strutils
const
constEval = contains("abc", 'b') # computed at compile time!
Types
@ -414,8 +416,8 @@ intXX
There are no `unsigned integer`:idx: types, only `unsigned operations`:idx:
that treat their arguments as unsigned. Unsigned operations all wrap around;
they may not lead to over- or underflow errors. Unsigned operations use the
``%`` postfix as convention:
they cannot lead to over- or underflow errors. Unsigned operations use the
``%`` suffix as convention:
====================== ======================================================
operation meaning
@ -498,16 +500,15 @@ the resulting programs will still handle UTF-8 properly as UTF-8 was specially
designed for this.
Another reason is that Nimrod can support ``array[char, int]`` or
``set[char]`` efficiently as many algorithms rely on this feature. The
`TUniChar` type is used for Unicode characters, it can represent any Unicode
character. ``TUniChar`` is declared the ``unicode`` standard module.
`TRune` type is used for Unicode characters, it can represent any Unicode
character. ``TRune`` is declared the ``unicode`` module.
Enumeration types
~~~~~~~~~~~~~~~~~
`Enumeration`:idx: types define a new type whose values consist only of the ones
specified.
The values are ordered by the order in enum's declaration. Example:
`Enumeration`:idx: types define a new type whose values consist of the ones
specified. The values are ordered. Example:
.. code-block:: nimrod
@ -528,8 +529,8 @@ with enumeration types.
For better interfacing to other programming languages, the fields of enum
types can be assigned an explicit ordinal value. However, the ordinal values
have to be in ascending order. A field whose ordinal value that is not
explicitly given, is assigned the value of the previous field + 1.
have to be in ascending order. A field whose ordinal value is not
explicitly given is assigned the value of the previous field + 1.
An explicit ordered enum can have *wholes*:
@ -545,7 +546,7 @@ and ``pred`` are not available for them either.
Subrange types
~~~~~~~~~~~~~~
A `subrange`:idx: type is a range of values from an ordinal type (the host
A `subrange`:idx: type is a range of values from an ordinal type (the base
type). To define a subrange type, one must specify it's limiting values: the
highest and lowest value of the type:
@ -566,7 +567,7 @@ A subrange type has the same size as its base type (``int`` in the example).
String type
~~~~~~~~~~~
All string literals are of the type `string`:idx:. A string in Nimrod is very
similar to a sequence of characters. However, strings in Nimrod both are
similar to a sequence of characters. However, strings in Nimrod are both
zero-terminated and have a length field. One can retrieve the length with the
builtin ``len`` procedure; the length never counts the terminating zero.
The assignment operator for strings always copies the string.
@ -585,7 +586,7 @@ arrays, they can be used in case statements:
Per convention, all strings are UTF-8 strings, but this is not enforced. For
example, when reading strings from binary files, they are merely a sequence of
bytes. The index operation ``s[i]`` means the i-th *char* of ``s``, not the
i-th *unichar*. The iterator ``unichars`` from the ``unicode`` standard
i-th *unichar*. The iterator ``runes`` from the ``unicode``
module can be used for iteration over all unicode characters.
@ -611,9 +612,7 @@ constructed by the array constructor ``[]`` in conjunction with the array to
sequence operator ``@``. Another way to allocate space for a sequence is to
call the built-in ``newSeq`` procedure.
A sequence may be passed to a parameter that is of type *open array*, but
not to a multi-dimensional open array, because it is impossible to do so in an
efficient manner.
A sequence may be passed to a parameter that is of type *open array*.
Example:
@ -639,6 +638,24 @@ Arrays are always bounds checked (at compile-time or at runtime). These
checks can be disabled via pragmas or invoking the compiler with the
``--bound_checks:off`` command line switch.
An open array is also a means to implement passing a variable number of
arguments to a procedure. The compiler converts the list of arguments
to an array automatically:
.. code-block:: nimrod
proc myWriteln(f: TFile, a: openarray[string]) =
for s in items(a):
write(f, s)
write(f, "\n")
myWriteln(stdout, "abc", "def", "xyz")
# is transformed by the compiler to:
myWriteln(stdout, ["abc", "def", "xyz"])
This transformation is only done if the openarray parameter is the
last parameter in the procedure header. The current implementation does not
support nested open arrays.
Tuples and object types
~~~~~~~~~~~~~~~~~~~~~~~
@ -691,7 +708,7 @@ the ``is`` operator can be used to determine the object's type.
person: TPerson
assert(student is TStudent) # is true
Object fields that should be visible outside from the defining module, have to
Object fields that should be visible from outside the defining module, have to
marked by ``*``. In contrast to tuples, different object types are
never *equivalent*.
@ -904,9 +921,9 @@ Most calling conventions exist only for the Windows 32-bit platform.
Statements
----------
Nimrod uses the common statement/expression paradigma: `Statements`:idx: do not
Statements and expressions
--------------------------
Nimrod uses the common statement/expression paradigm: `Statements`:idx: do not
produce a value in contrast to expressions. Call expressions are statements.
If the called procedure returns a value, it is not a valid statement
as statements do not produce values. To evaluate an expression for
@ -943,7 +960,7 @@ Discard statement
Syntax::
discardStmt ::= DISCARD expr
discardStmt ::= 'discard' expr
Example:
@ -962,11 +979,13 @@ Var statement
Syntax::
colonOrEquals ::= COLON typeDesc [EQUALS expr] | EQUALS expr
varField ::= symbol ["*"] [pragma]
colonOrEquals ::= ':' typeDesc ['=' expr] | '=' expr
varField ::= symbol ['*'] [pragma]
varPart ::= symbol (comma symbol)* [comma] colonOrEquals [COMMENT | IND COMMENT]
varSection ::= VAR (varPart
| indPush (COMMENT|varPart) (SAD (COMMENT|varPart))* DED)
varSection ::= 'var' (varPart
| indPush (COMMENT|varPart)
(SAD (COMMENT|varPart))* DED indPop)
`Var`:idx: statements declare new local and global variables and
initialize them. A comma seperated list of variables can be used to specify
@ -992,7 +1011,7 @@ char '\0'
bool false
ref or pointer type nil
procedural type nil
sequence nil
sequence nil (**not** ``@[]``)
string nil (**not** "")
tuple[x: A, y: B, ...] (default(A), default(B), ...)
(analogous for objects)
@ -1007,12 +1026,12 @@ Const section
Syntax::
colonAndEquals ::= [COLON typeDesc] EQUALS expr
constDecl ::= CONST
indPush
symbol ["*"] [pragma] colonAndEquals
(SAD symbol ["*"] [pragma] colonAndEquals)*
DED
colonAndEquals ::= [':' typeDesc] '=' expr
constDecl ::= symbol ['*'] [pragma] colonAndEquals [COMMENT | IND COMMENT]
| COMMENT
constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop
Example:
@ -1031,7 +1050,7 @@ If statement
Syntax::
ifStmt ::= IF expr COLON stmt (ELIF expr COLON stmt)* [ELSE COLON stmt]
ifStmt ::= 'if' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
Example:
@ -1061,9 +1080,9 @@ Case statement
Syntax::
caseStmt ::= CASE expr (OF sliceList COLON stmt)*
(ELIF expr COLON stmt)*
[ELSE COLON stmt]
caseStmt ::= 'case' expr ('of' sliceExprList ':' stmt)*
('elif' expr ':' stmt)*
['else' ':' stmt]
Example:
@ -1094,7 +1113,7 @@ When statement
Syntax::
whenStmt ::= WHEN expr COLON stmt (ELIF expr COLON stmt)* [ELSE COLON stmt]
whenStmt ::= 'when' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
Example:
@ -1116,8 +1135,7 @@ exceptions:
* The statements do not open a new scope if they introduce new identifiers.
* The statements that belong to the expression that evaluated to true are
translated by the compiler, the other statements are not checked for
syntax or semantics at all! This holds also for any ``expr`` coming
after the expression that evaluated to true.
semantics! However, each ``expr`` is checked for semantics.
The ``when`` statement enables conditional compilation techniques. As
a special syntatic extension, the ``when`` construct is also available
@ -1129,7 +1147,7 @@ Raise statement
Syntax::
raiseStmt ::= RAISE [expr]
raiseStmt ::= 'raise' [expr]
Example:
@ -1152,11 +1170,11 @@ Try statement
Syntax::
qualifiedIdent ::= symbol ['.' symbol]
exceptList ::= [qualifiedIdent (comma qualifiedIdent)* [comma]]
tryStmt ::= TRY COLON stmt
(EXCEPT exceptList COLON stmt)*
[FINALLY COLON stmt]
tryStmt ::= 'try' ':' stmt
('except' exceptList ':' stmt)*
['finally' ':' stmt]
Example:
@ -1176,6 +1194,8 @@ Example:
echo("could not convert string to integer")
except EIO:
echo("IO error!")
except:
echo("Unknown exception!")
finally:
closeFile(f)
@ -1203,7 +1223,7 @@ Return statement
Syntax::
returnStmt ::= RETURN [expr]
returnStmt ::= 'return' [expr]
Example:
@ -1224,7 +1244,8 @@ value of the procedure. It is automatically declared by the compiler. As all
variables, ``result`` is initialized to (binary) zero::
.. code-block:: nimrod
proc returnZero(): int = nil # implicitely returns 0
proc returnZero(): int =
# implicitely returns 0
Yield statement
@ -1232,7 +1253,7 @@ Yield statement
Syntax::
yieldStmt ::= YIELD expr
yieldStmt ::= 'yield' expr
Example:
@ -1252,7 +1273,7 @@ Block statement
Syntax::
blockStmt ::= BLOCK [symbol] COLON stmt
blockStmt ::= 'block' [symbol] ':' stmt
Example:
@ -1277,7 +1298,7 @@ Break statement
Syntax::
breakStmt ::= BREAK [symbol]
breakStmt ::= 'break' [symbol]
Example:
@ -1294,7 +1315,7 @@ While statement
Syntax::
whileStmt ::= WHILE expr COLON stmt
whileStmt ::= 'while' expr ':' stmt
Example:
@ -1316,7 +1337,7 @@ Continue statement
Syntax::
continueStmt ::= CONTINUE
continueStmt ::= 'continue'
A `continue`:idx: statement leads to the immediate next iteration of the
surrounding loop construct. It is only allowed within a loop. A continue
@ -1340,7 +1361,7 @@ Assembler statement
~~~~~~~~~~~~~~~~~~~
Syntax::
asmStmt ::= ASM [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
asmStmt ::= 'asm' [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
The direct embedding of `assembler`:idx: code into Nimrod code is supported
by the unsafe ``asm`` statement. Identifiers in the assembler code that refer to
@ -1348,6 +1369,49 @@ Nimrod identifiers shall be enclosed in a special character which can be
specified in the statement's pragmas. The default special character is ``'`'``.
If expression
~~~~~~~~~~~~~
An `if expression` is almost like an if statement, but it is an expression.
Example:
.. code-block:: nimrod
p(if x > 8: 9 else: 10)
An if expression always results in a value, so the ``else`` part is
required. ``Elif`` parts are also allowed (but unlikely to be good
style).
Type convertions
~~~~~~~~~~~~~~~~
Syntactically a `type conversion` is like a procedure call, but a
type name replaces the procedure name. A type conversion is always
safe in the sense that a failure to convert a type to another
results in an exception (if it cannot be determined statically).
Type casts
~~~~~~~~~~
Example:
.. code-block:: nimrod
cast[int](x)
Type casts are a crude mechanism to interpret the bit pattern of
an expression as if it would be of another type. Type casts are
only needed for low-level programming and are inherently unsafe.
The addr operator
~~~~~~~~~~~~~~~~~
The `addr` operator returns the address of an l-value. If the
type of the location is ``T``, the `addr` operator result is
of the type ``ptr T``. Taking the address of an object that resides
on the stack is **unsafe**, as the pointer may live longer than the
object on the stack and can thus reference a non-existing object.
Procedures
~~~~~~~~~~
What most programming languages call `methods`:idx: or `funtions`:idx: are
@ -1355,16 +1419,16 @@ called `procedures`:idx: in Nimrod (which is the correct terminology). A
procedure declaration defines an identifier and associates it with a block
of code. A procedure may call itself recursively. The syntax is::
param ::= symbol (comma symbol)* [comma] COLON typeDesc
paramList ::= [PAR_LE [param (comma param)* [comma]] PAR_RI] [COLON typeDesc]
param ::= symbol (comma symbol)* [comma] ':' typeDesc
paramList ::= ['(' [param (comma param)* [comma]] ')'] [':' typeDesc]
genericParams ::= BRACKET_LE (symbol [EQUALS typeDesc] )* BRACKET_RI
genericParam ::= symbol [':' typeDesc]
genericParams ::= '[' genericParam (comma genericParam)* [comma] ']'
procDecl ::= PROC symbol ["*"] [genericParams]
paramList [pragma]
[EQUALS stmt]
procDecl ::= 'proc' symbol ['*'] [genericParams] paramList [pragma]
['=' stmt]
If the ``EQUALS stmt`` part is missing, it is a `forward`:idx: declaration. If
If the ``= stmt`` part is missing, it is a `forward`:idx: declaration. If
the proc returns a value, the procedure body can access an implicit declared
variable named `result`:idx: that represents the return value. Procs can be
overloaded. The overloading resolution algorithm tries to find the proc that is
@ -1384,6 +1448,24 @@ is used if the caller does not provide a value for this parameter. Example:
for i in 0..len(s) - 1:
result[i] = toLower(s[i]) # calls toLower for characters; no recursion!
Calling a procedure can be done in many different ways:
.. code-block:: nimrod
proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ...
# call with positional arguments # parameter bindings:
callme(0, 1, "abc", '\t', true) # (x=0, y=1, s="abc", c='\t', b=true)
# call with named and positional arguments:
callme(y=1, x=0, "abd", '\t') # (x=0, y=1, s="abd", c='\t', b=false)
# call with named arguments (order is not relevant):
callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false)
# call as a command statement: no () needed:
callme 0, 1, "abc", '\t'
A procedure cannot modify its parameters (unless the parameters have the
type `var`).
`Operators`:idx: are procedures with a special operator symbol as identifier:
.. code-block:: nimrod
@ -1391,19 +1473,79 @@ is used if the caller does not provide a value for this parameter. Example:
# converts an integer to a string; this is a prefix operator.
return intToStr(x)
Calling a procedure can be done in many different ways:
Operators with one parameter are prefix operators, operators with two
parameters are infix operators. There is no way to declare postfix
operators: All postfix operators are built-in and handled by the
grammar explicitely.
Any operator can be called like an ordinary proc with the '`opr`'
notation. (Thus an operator can have more than two parameters):
.. code-block:: nimrod
proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ...
proc `*+` (a, b, c: int): int =
# Multiply and add
return a * b + c
assert `*+`(3, 4, 6) == `*`(a, `+`(b, c))
Var parameters
~~~~~~~~~~~~~~
The type of a parameter may be prefixed with the ``var`` keyword:
.. code-block:: nimrod
proc divmod(a, b: int, res, remainder: var int) =
res = a div b
remainder = a mod b
var
x, y: int
divmod(8, 5, x, y) # modifies x and y
assert x == 1
assert y == 3
In the example, ``res`` and ``remainder`` are `var parameters`.
Var parameters can be modified by the procedure and the changes are
visible to the caller. The argument passed to a var parameter has to be
an l-value. Var parameters are implemented as hidden pointers. The
above example is equivalent to:
.. code-block:: nimrod
proc divmod(a, b: int, res, remainder: ptr int) =
res = a div b
remainder = a mod b
var
x, y: int
divmod(8, 5, addr(x), addr(y))
assert x == 1
assert y == 3
In the examples, var parameters or pointers are used to provide two
return values. This can be done in a cleaner way by returning a tuple:
.. code-block:: nimrod
proc divmod(a, b: int): tuple[res, remainder: int] =
return (a div b, a mod b)
var t = divmod(8, 5)
assert t.res == 1
assert t.remainder = 3
Even more elegant is to use `tuple unpacking` to access the tuple's fields:
.. code-block:: nimrod
var (x, y) = divmod(8, 5) # tuple unpacking
assert x == 1
assert y == 3
Unfortunately, this form of tuple unpacking is not yet implemented.
..
XXX remove this as soon as tuple unpacking is implemented
# call with positional arguments# parameter bindings:
callme(0, 1, "abc", '\t', true) # (x=0, y=1, s="abc", c='\t', b=true)
# call with named and positional arguments:
callme(y=1, x=0, "abd", '\t') # (x=0, y=1, s="abd", c='\t', b=false)
# call with named arguments (order is not relevant):
callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false)
# call as a command statement: no () needed:
callme 0, 1, "abc", '\t'
Iterators and the for statement
@ -1411,15 +1553,16 @@ Iterators and the for statement
Syntax::
forStmt ::= FOR symbol (comma symbol)* [comma] IN expr [DOTDOT expr] COLON stmt
forStmt ::= 'for' symbol (comma symbol)* [comma] 'in' expr ['..' expr] ':' stmt
param ::= symbol (comma symbol)* [comma] COLON typeDesc
paramList ::= [PAR_LE [param (comma param)* [comma]] PAR_RI] [COLON typeDesc]
param ::= symbol (comma symbol)* [comma] ':' typeDesc
paramList ::= ['(' [param (comma param)* [comma]] ')'] [':' typeDesc]
genericParams ::= BRACKET_LE (symbol [EQUALS typeDesc] )* BRACKET_RI
genericParam ::= symbol [':' typeDesc]
genericParams ::= '[' genericParam (comma genericParam)* [comma] ']'
iteratorDecl ::= ITERATOR symbol ["*"] [genericParams] paramList [pragma]
[EQUALS stmt]
iteratorDecl ::= 'iterator' symbol ['*'] [genericParams] paramList [pragma]
['=' stmt]
The `for`:idx: statement is an abstract mechanism to iterate over the elements
of a container. It relies on an `iterator`:idx: to do so. Like ``while``
@ -1473,13 +1616,15 @@ Type sections
Syntax::
typeDef ::= typeDesc | objectDef | enumDef
genericParams ::= BRACKET_LE (symbol [EQUALS typeDesc] )* BRACKET_RI
typeDecl ::= TYPE
indPush
symbol ["*"] [genericParams] [EQUALS typeDef]
(SAD symbol ["*"] [genericParams] [EQUALS typeDef])*
DED
genericParam ::= symbol [':' typeDesc]
genericParams ::= '[' genericParam (comma genericParam)* [comma] ']'
typeDecl ::= COMMENT
| symbol ['*'] [genericParams] ['=' typeDef] [COMMENT|IND COMMENT]
typeSection ::= 'type' indPush typeDecl (SAD typeDecl)* DED indPop
Example:
@ -1504,6 +1649,8 @@ possible within a single ``type`` section.
Generics
~~~~~~~~
`Version 0.7.4: Complex generic types like in the example do not work.`:red:
Example:
.. code-block:: nimrod
@ -1578,17 +1725,110 @@ Example:
# this definition exists in the System module
not (a == b)
writeln(5 != 6) # the compiler rewrites that to: writeln(not (5 == 6))
assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
Macros
~~~~~~
------
`Macros`:idx: are the most powerful feature of Nimrod. They should be used
only to implement `domain specific languages`:idx:. They may lead to code
`Macros`:idx: are the most powerful feature of Nimrod. They can be used
to implement `domain specific languages`:idx:. But they may lead to code
that is harder to understand and maintain. So one ought to use them sparingly.
The usage of ordinary procs, iterators or generics is preferred to the usage of
macros.
While macros enable advanced compile-time code tranformations, they
cannot change Nimrod's syntax. However, this is no real restriction because
Nimrod's syntax is flexible enough anyway.
To write macros, one needs to know how the Nimrod concrete syntax is converted
to an abstract syntax tree. (Unfortunately the AST is not yet documented.)
There are two ways to invoke a macro:
(1) invoking a macro like a procedure call (`expression macros`)
(2) invoking a macro with the special ``macrostmt`` syntax (`statement macros`)
Expression Macros
~~~~~~~~~~~~~~~~~
The following example implements a powerful ``debug`` command that accepts a
variable number of arguments:
.. code-block:: nimrod
# to work with Nimrod syntax trees, we need an API that is defined in the
# ``macros`` module:
import macros
macro debug(n: expr): stmt =
# `n` is a Nimrod AST that contains the whole macro expression
# this macro returns a list of statements:
result = newNimNode(nnkStmtList, n)
# iterate over any argument that is passed to this macro:
for i in 1..n.len-1:
# add a call to the statement list that writes the expression;
# `toStrLit` converts an AST to its string representation:
add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i])))
# add a call to the statement list that writes ": "
add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
# add a call to the statement list that writes the expressions value:
add(result, newCall("writeln", newIdentNode("stdout"), n[i]))
var
a: array [0..10, int]
x = "some string"
a[0] = 42
a[1] = 45
debug(a[0], a[1], x)
The macro call expands to:
.. code-block:: nimrod
write(stdout, "a[0]")
write(stdout, ": ")
writeln(stdout, a[0])
write(stdout, "a[1]")
write(stdout, ": ")
writeln(stdout, a[1])
write(stdout, "x")
write(stdout, ": ")
writeln(stdout, x)
Statement Macros
~~~~~~~~~~~~~~~~
Statement macros are defined just as expression macros. However, they are
invoked by an expression following a colon::
exprStmt ::= lowestExpr ['=' expr | [expr (comma expr)* [comma]] [macroStmt]]
macroStmt ::= ':' [stmt] ('of' [sliceExprList] ':' stmt
| 'elif' expr ':' stmt
| 'except' exceptList ':' stmt )*
['else' ':' stmt]
The following example outlines a macro that generates a lexical analyser from
regular expressions:
.. code-block:: nimrod
import macros
macro case_token(n: stmt): stmt =
# creates a lexical analyser from regular expressions
# ... (implementation is an exercise for the reader :-)
nil
case_token: # this colon tells the parser it is a macro statement
of r"[A-Za-z_]+[A-Za-z_0-9]*":
return tkIdentifier
of r"0-9+":
return tkInteger
of r"[\+\-\*\?]+":
return tkOperator
else:
return tkUnknown
Modules
@ -1622,12 +1862,12 @@ This is best illustrated by an example:
# Module B
import A # A is not parsed here! Only the already known symbols
# of A are imported here.
# of A are imported.
proc p*(x: A.T1): A.T1 # this works because the compiler has already
# added T1 to A's interface symbol table
proc p(x: A.T1): A.T1 = return x + 1
proc p*(x: A.T1): A.T1 =
# this works because the compiler has already
# added T1 to A's interface symbol table
return x + 1
Scope rules
@ -1649,7 +1889,7 @@ procedure or iterator overloading purposes.
Tuple or object scope
~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~
The field identifiers inside a tuple or object definition are valid in the
following places:
@ -1659,16 +1899,14 @@ following places:
Module scope
~~~~~~~~~~~~
All identifiers in the interface part of a module are valid from the point of
declaration, until the end of the module. Furthermore, the identifiers are
known in other modules that import the module. Identifiers from indirectly
dependent modules are *not* available. The `system`:idx: module is automatically
imported in all other modules.
All identifiers of a module are valid from the point of declaration until
the end of the module. Identifiers from indirectly dependent modules are *not*
available. The `system`:idx: module is automatically imported in every other
module.
If a module imports an identifier by two different modules,
each occurance of the identifier has to be qualified, unless it is an
overloaded procedure or iterator in which case the overloading
resolution takes place:
If a module imports an identifier by two different modules, each occurance of
the identifier has to be qualified, unless it is an overloaded procedure or
iterator in which case the overloading resolution takes place:
.. code-block:: nimrod
# Module A
@ -1680,7 +1918,7 @@ resolution takes place:
# Module C
import A, B
write(stdout, x) # error: x is ambigious
write(sdtout, A.x) # no error: qualifier used
write(stdout, A.x) # no error: qualifier used
var x = 4
write(stdout, x) # not ambigious: uses the module C's x
@ -1698,14 +1936,14 @@ Pragmas
Syntax::
colonExpr ::= expr [COLON expr]
colonExprList ::= [ colonExpr (comma colonExpr)* [comma] ]
colonExpr ::= expr [':' expr]
colonExprList ::= [colonExpr (comma colonExpr)* [comma]]
pragma ::= CURLYDOT_LE colonExprList (CURLYDOT_RI | CURLY_RI)
pragma ::= '{.' optInd (colonExpr [comma])* [SAD] ('.}' | '}')
Pragmas are Nimrod's method to give the compiler additional information/
commands without introducing a massive number of new keywords. Pragmas are
processed on the fly during parsing. Pragmas are always enclosed in the
processed on the fly during semantic checking. Pragmas are enclosed in the
special ``{.`` and ``.}`` curly brackets.
@ -1759,10 +1997,6 @@ compilation option pragmas
--------------------------
The listed pragmas here can be used to override the code generation options
for a section of code.
::
"{." pragma: val {pragma: val} ".}"
The implementation currently provides the following possible options (later
various others may be added).
@ -1785,8 +2019,7 @@ warnings on|off Turns the warning messages of the compiler
hints on|off Turns the hint messages of the compiler
on or off.
optimization none|speed|size Optimize the code for speed or size, or
disable optimization. For non-optimizing
compilers this option has no effect.
disable optimization.
callconv cdecl|... Specifies the default calling convention for
all procedures (and procedure types) that
follow.

View file

@ -12,7 +12,7 @@ Introduction
This document describes the usage of the *Nimrod compiler*
on the different supported platforms. It is not a definition of the Nimrod
programming system (therefore is the Nimrod manual).
programming language (therefore is the manual).
Nimrod is free software; it is licensed under the
`GNU General Public License <gpl.html>`_.
@ -61,14 +61,6 @@ However, the generated C code is not platform independant. C code generated for
Linux does not compile on Windows, for instance. The comment on top of the
C file lists the OS, CPU and CC the file has been compiled for.
The library lies in ``lib``. Directly in the library directory are essential
Nimrod modules like the ``system`` and ``os`` modules. Under ``lib/base``
are additional specialized libraries or interfaces to foreign libraries which
are included in the standard distribution. The ``lib/extra`` directory is
initially empty. Third party libraries should go there. In the default
configuration the compiler always searches for libraries in ``lib``,
``lib/base`` and ``lib/extra``.
Additional Features
===================
@ -128,8 +120,8 @@ the C code. Thus it makes the following possible, for example:
.. code-block:: Nimrod
var
EOF {.importc: "EOF", no_decl.}: cint # pretend EOF was a variable, as
# Nimrod does not know its value
EACCES {.importc, no_decl.}: cint # pretend EACCES was a variable, as
# Nimrod does not know its value
However, the ``header`` pragma is often the better alternative.
@ -164,14 +156,6 @@ strings automatically:
printf("hallo %s", "world") # "world" will be passed as C string
No_static Pragma
~~~~~~~~~~~~~~~~
The `no_static`:idx: pragma can be applied to almost any symbol and specifies
that it shall not be declared ``static`` in the generated C code. Note that
symbols in the interface part of a module never get declared ``static``, so
only in very special cases this pragma is necessary.
Line_dir Option
~~~~~~~~~~~~~~~
The `line_dir`:idx: option can be turned on or off. If on the generated C code
@ -240,6 +224,22 @@ information that this cannot happen to the GC. If the programmer uses the
memory, but nothing worse happens.
Dead_code_elim Pragma
~~~~~~~~~~~~~~~~~~~~~
The `dead_code_elim`:idx: pragma only applies to whole modules: It tells the
compiler to active (or deactivate) dead code elimination for the module the
pragma appers in.
The ``--dead_code_elim:on`` command line switch has the same effect as marking
any module with ``{.dead_code_elim:on}``. However, for some modules such as
the GTK wrapper it makes sense to *always* turn on dead code elimination -
no matter if it is globally active or not.
Example:
.. code-block:: nimrod
{.dead_code_elim: on.}
Disabling certain messages
--------------------------
@ -280,8 +280,8 @@ However, sometimes one has to optimize. Do it in the following order:
This section can only help you with the last item. Note that rewriting parts
of your program in C is *never* necessary to speed up your program, because
everything that can be done in C can be done in Nimrod. Rewriting parts in
assembler *might*.
everything that can be done in C can be done in Nimrod.
Optimizing string handling
--------------------------

View file

@ -18,8 +18,7 @@ compatible to the original implementation as one would like.
Even though Nimrod's |rst| parser does not parse all constructs, it is pretty
usable. The missing features can easily be circumvented. An indication of this
fact is that Nimrod's
*whole* documentation itself (including this document) is
fact is that Nimrod's *whole* documentation itself (including this document) is
processed by Nimrod's |rst| parser. (Which is an order of magnitude faster than
Docutils' parser.)

File diff suppressed because it is too large Load diff

1382
doc/tut1.txt Normal file

File diff suppressed because it is too large Load diff

718
doc/tut2.txt Normal file
View file

@ -0,0 +1,718 @@
=============================
The Nimrod Tutorial (Part II)
=============================
:Author: Andreas Rumpf
:Version: |nimrodversion|
.. contents::
Introduction
============
"With great power comes great responsibility." -- Spider-man
This document is a tutorial for the advanced constructs of the *Nimrod*
programming language.
Pragmas
=======
Pragmas are Nimrod's method to give the compiler additional information/
commands without introducing a massive number of new keywords. Pragmas are
processed during semantic checking. Pragmas are enclosed in the
special ``{.`` and ``.}`` curly dot brackets. This tutorial does not cover
pragmas. See the `manual <manual.html>`_ or `user guide <nimrodc.html>`_ for
a description of the available pragmas.
Object Oriented Programming
===========================
While Nimrod's support for object oriented programming (OOP) is minimalistic,
powerful OOP technics can be used. OOP is seen as *one* way to design a
program, not *the only* way. Often a procedural approach leads to simpler
and more efficient code.
Objects
-------
Like tuples, objects are a means to pack different values together in a
structured way. However, objects provide many features that tuples do not:
They provide inheritance and information hiding. Because objects encapsulate
data, the ``()`` tuple constructor cannot be used to construct objects. So
the order of the object's fields is not as important as it is for tuples. The
programmer should provide a proc to initialize the object (this is called
a *constructor*).
Objects have access to their type at runtime. There is an
``is`` operator that can be used to check the object's type:
.. code-block:: nimrod
type
TPerson = object of TObject
name*: string # the * means that `name` is accessible from other modules
age: int # no * means that the field is hidden from other modules
TStudent = object of TPerson # TStudent inherits from TPerson
id: int # with an id field
var
student: TStudent
person: TPerson
assert(student is TStudent) # is true
Object fields that should be visible from outside the defining module, have to
be marked by ``*``. In contrast to tuples, different object types are
never *equivalent*. New object types can only be defined within a type
section.
Inheritance is done with the ``object of`` syntax. Multiple inheritance is
currently not supported. If an object type has no suitable ancestor, ``TObject``
should be used as its ancestor, but this is only a convention.
Note that aggregation (*has-a* relation) is often preferable to inheritance
(*is-a* relation) for simple code reuse. Since objects are value types in
Nimrod, aggregation is as efficient as inheritance.
Mutually recursive types
------------------------
Objects, tuples and references can model quite complex data structures which
depend on each other. This is called *mutually recursive types*. In Nimrod
these types need to be declared within a single type section. Anything else
would require arbitrary symbol lookahead which slows down compilation.
Example:
.. code-block:: nimrod
type
PNode = ref TNode # a traced reference to a TNode
TNode = object
le, ri: PNode # left and right subtrees
sym: ref TSym # leaves contain a reference to a TSym
TSym = object # a symbol
name: string # the symbol's name
line: int # the line the symbol was declared in
code: PNode # the symbol's abstract syntax tree
Type conversions
----------------
Nimrod distinguishes between `type casts`:idx: and `type conversions`:idx:.
Casts are done with the ``cast`` operator and force the compiler to
interpret a bit pattern to be of another type.
Type conversions are a much more polite way to convert a type into another:
They preserve the abstract *value*, not necessarily the *bit-pattern*. If a
type conversion is not possible, the compiler complains or an exception is
raised.
The syntax for type conversions is ``destination_type(expression_to_convert)``
(like an ordinary call):
.. code-block:: nimrod
proc getID(x: TPerson): int =
return TStudent(x).id
The ``EInvalidObjectConversion`` exception is raised if ``x`` is not a
``TStudent``.
Object variants
---------------
Often an object hierarchy is overkill in certain situations where simple
`variant`:idx: types are needed.
An example:
.. code-block:: nimrod
# This is an example how an abstract syntax tree could be modelled in Nimrod
type
TNodeKind = enum # the different node types
nkInt, # a leaf with an integer value
nkFloat, # a leaf with a float value
nkString, # a leaf with a string value
nkAdd, # an addition
nkSub, # a subtraction
nkIf # an if statement
PNode = ref TNode
TNode = object
case kind: TNodeKind # the ``kind`` field is the discriminator
of nkInt: intVal: int
of nkFloat: floavVal: float
of nkString: strVal: string
of nkAdd, nkSub:
leftOp, rightOp: PNode
of nkIf:
condition, thenPart, elsePart: PNode
var
n: PNode
new(n) # creates a new node
n.kind = nkFloat
n.floatVal = 0.0 # valid, because ``n.kind==nkFloat``
# the following statement raises an `EInvalidField` exception, because
# n.kind's value does not fit:
n.strVal = ""
As can been seen from the example, an advantage to an object hierarchy is that
no conversion between different object types is needed. Yet, access to invalid
object fields raises an exception.
Methods
-------
In ordinary object oriented languages, procedures (also called *methods*) are
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 procedure 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?
Nimrod avoids these problems by not distinguishing between methods and
procedures. Methods are just ordinary procedures. However, there is a special
syntactic sugar for calling procedures: The syntax ``obj.method(args)`` can be
used instead of ``method(obj, args)``. If there are no remaining arguments, the
parentheses can be omitted: ``obj.len`` (instead of ``len(obj)``).
This `method call syntax`:idx: is not restricted to objects, it can be used
for any type:
.. code-block:: nimrod
echo("abc".len) # is the same as echo(len("abc"))
echo("abc".toUpper())
echo({'a', 'b', 'c'}.card)
stdout.writeln("Hallo") # the same as write(stdout, "Hallo")
If it gives you warm fuzzy feelings, you can even write ``1.`+`(2)`` instead of
``1 + 2`` and claim that Nimrod is a pure object oriented language. (That
would not even be lying: *pure OO* has no meaning anyway. :-)
Properties
----------
As the above example shows, Nimrod has no need for *get-properties*:
Ordinary get-procedures that are called with the *method call syntax* achieve
the same. But setting a value is different; for this a special setter syntax
is needed:
.. code-block:: nimrod
type
TSocket* = object of TObject
FHost: int # cannot be accessed from the outside of the module
# the `F` prefix is a convention to avoid clashes since
# the accessors are named `host`
proc `host=`*(s: var TSocket, value: int) {.inline.} =
## setter of hostAddr
s.FHost = value
proc host*(s: TSocket): int {.inline.} =
## getter of hostAddr
return s.FHost
var
s: TSocket
s.host = 34 # same as `host=`(s, 34)
(The example also shows ``inline`` procedures.)
The ``[]`` array access operator can be overloaded to provide
`array properties`:idx:\ :
.. code-block:: nimrod
type
TVector* = object
x, y, z: float
proc `[]=`* (v: var TVector, i: int, value: float) =
# setter
case i
of 0: v.x = value
of 1: v.y = value
of 2: v.z = value
else: assert(false)
proc `[]`* (v: TVector, i: int): float =
# getter
case i
of 0: result = v.x
of 1: result = v.y
of 2: result = v.z
else: assert(false)
The example is silly, since a vector is better modelled by a tuple which
already provides ``v[]`` access.
Dynamic binding
---------------
In Nimrod procedural types are used to implement dynamic binding. The following
example also shows some more conventions: The ``self`` or ``this`` object
is named ``my`` (because it is shorter than the alternatives), each class
provides a constructor, etc.
.. code-block:: nimrod
type
TFigure = object of TObject # abstract base class:
draw: proc (my: var TFigure) # concrete classes implement this proc
proc init(f: var TFigure) =
f.draw = nil
type
TCircle = object of TFigure
radius: int
proc drawCircle(my: var TCircle) = echo("o " & $my.radius)
proc init(my: var TCircle) =
init(TFigure(my)) # call base constructor
my.radius = 5
my.draw = drawCircle
type
TRectangle = object of TFigure
width, height: int
proc drawRectangle(my: var TRectangle) = echo("[]")
proc init(my: var TRectangle) =
init(TFigure(my)) # call base constructor
my.width = 5
my.height = 10
my.draw = drawRectangle
# now use these classes:
var
r: TRectangle
c: TCircle
init(r)
init(c)
r.draw(r)
c.draw(c)
The last line shows the syntactical difference between static and dynamic
binding: The ``r.draw(r)`` dynamic call refers to ``r`` twice. This difference
is not necessarily bad. But if you want to eliminate the somewhat redundant
``r``, it can be done by using *closures*:
.. code-block:: nimrod
type
TFigure = object of TObject # abstract base class:
draw: proc () {.closure.} # concrete classes implement this proc
proc init(f: var TFigure) =
f.draw = nil
type
TCircle = object of TFigure
radius: int
proc init(me: var TCircle) =
init(TFigure(me)) # call base constructor
me.radius = 5
me.draw = lambda () =
echo("o " & $me.radius)
type
TRectangle = object of TFigure
width, height: int
proc init(me: var TRectangle) =
init(TFigure(me)) # call base constructor
me.width = 5
me.height = 10
me.draw = lambda () =
echo("[]")
# now use these classes:
var
r: TRectangle
c: TCircle
init(r)
init(c)
r.draw()
c.draw()
The example also introduces `lambda`:idx: expressions: A ``lambda`` expression
defines a new proc with the ``closure`` calling convention on the fly.
`Version 0.7.4: Closures and lambda expressions are not implemented.`:red:
Exceptions
==========
In Nimrod `exceptions`:idx: are objects. By convention, exception types are
prefixed with an 'E', not 'T'. The ``system`` module defines an exception
hierarchy that you should stick to. Reusing an existing exception type is
often better than defining a new exception type: It avoids a proliferation of
types.
Exceptions should be allocated on the heap because their lifetime is unknown.
A convention is that exceptions should be raised in *exceptional* cases:
For example, if a file cannot be opened, this should not raise an exception
since this is quite common (the file may have been deleted).
Raise statement
---------------
Raising an exception is done with the ``raise`` statement:
.. code-block:: nimrod
var
e: ref EOS
new(e)
e.msg = "the request to the OS failed"
raise e
If the ``raise`` keyword is not followed by an expression, the last exception
is *re-raised*.
Try statement
-------------
The `try`:idx: statement handles exceptions:
.. code-block:: nimrod
# read the first two lines of a text file that should contain numbers
# and tries to add them
var
f: TFile
if openFile(f, "numbers.txt"):
try:
var a = readLine(f)
var b = readLine(f)
echo("sum: " & $(parseInt(a) + parseInt(b)))
except EOverflow:
echo("overflow!")
except EInvalidValue:
echo("could not convert string to integer")
except EIO:
echo("IO error!")
except:
echo("Unknown exception!")
# reraise the unknown exception:
raise
finally:
closeFile(f)
The statements after the ``try`` are executed unless an exception is
raised. Then the appropriate ``except`` part is executed.
The empty ``except`` part is executed if there is an exception that is
not explicitely listed. It is similiar to an ``else`` part in ``if``
statements.
If there is a ``finally`` part, it is always executed after the
exception handlers.
The exception is *consumed* in an ``except`` part. If an exception is not
handled, it is propagated through the call stack. This means that often
the rest of the procedure - that is not within a ``finally`` clause -
is not executed (if an exception occurs).
Generics
========
`Version 0.7.4: Complex generic types like in the example do not work.`:red:
`Generics`:idx: are Nimrod's means to parametrize procs, iterators or types
with `type parameters`:idx:. They are most useful for efficient type safe
containers:
.. code-block:: nimrod
type
TBinaryTree[T] = object # TBinaryTree is a generic type with
# with generic param ``T``
le, ri: ref TBinaryTree[T] # left and right subtrees; may be nil
data: T # the data stored in a node
PBinaryTree*[T] = ref TBinaryTree[T] # type that is exported
proc newNode*[T](data: T): PBinaryTree[T] =
# constructor for a node
new(result)
result.dat = data
proc add*[T](root: var PBinaryTree[T], n: PBinaryTree[T]) =
# insert a node into the tree
if root == nil:
root = n
else:
var it = root
while it != nil:
# compare the data items; uses the generic ``cmd`` proc that works for
# any type that has a ``==`` and ``<`` operator
var c = cmp(it.data, n.data)
if c < 0:
if it.le == nil:
it.le = n
return
it = it.le
else:
if it.ri == nil:
it.ri = n
return
it = it.ri
proc add*[T](root: var PBinaryTree[T], data: T) =
# convenience proc:
add(root, newNode(data))
iterator preorder*[T](root: PBinaryTree[T]): T =
# Preorder traversal of a binary tree.
# Since recursive iterators are not yet implemented,
# this uses an explicit stack (which is more efficient anyway):
var stack: seq[PBinaryTree[T]] = @[root]
while stack.len > 0:
var n = stack[stack.len-1]
setLen(stack, stack.len-1) # pop `n` of the stack
while n != nil:
yield n
add(stack, n.ri) # push right subtree onto the stack
n = n.le # and follow the left pointer
var
root: PBinaryTree[string] # instantiate a PBinaryTree with ``string``
add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and ``add``
add(root, "world") # instantiates the second ``add`` proc
for str in preorder(root):
stdout.writeln(str)
The example shows a generic binary tree. Depending on context, the brackets are
used either to introduce type parameters or to instantiate a generic proc,
iterator or type. As the example shows, generics work with overloading: The
best match of ``add`` is used. The built-in ``add`` procedure for sequences
is not hidden and used in the ``preorder`` iterator.
Templates
=========
Templates are a simple substitution mechanism that operates on Nimrod's
abstract syntax trees. Templates are processed in the semantic pass of the
compiler. They integrate well with the rest of the language and share none
of C's preprocessor macros flaws. However, they may lead to code that is harder
to understand and maintain. So one should use them sparingly.
To *invoke* a template, call it like a procedure.
Example:
.. code-block:: nimrod
template `!=` (a, b: expr): expr =
# this definition exists in the System module
not (a == b)
assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact
templates: This has the benefit that if you overload the ``==`` operator,
the ``!=`` operator is available automatically and does the right thing.
``a > b`` is transformed into ``b < a``.
``a in b`` is transformed into ``contains(b, a)``.
``notin`` and ``isnot`` have the obvious meanings.
Templates are especially useful for lazy evaluation purposes. Consider a
simple proc for logging:
.. code-block:: nimrod
const
debug = True
proc log(msg: string) {.inline.} =
if debug:
stdout.writeln(msg)
var
x = 4
log("x has the value: " & $x)
This code has a shortcoming: If ``debug`` is set to false someday, the quite
expensive ``$`` and ``&`` operations are still performed! (The argument
evaluation for procedures is said to be *eager*).
Turning the ``log`` proc into a template solves this problem in an elegant way:
.. code-block:: nimrod
const
debug = True
template log(msg: expr): stmt =
if debug:
stdout.writeln(msg)
var
x = 4
log("x has the value: " & $x)
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
description*). These are no real types, they just help the compiler parsing.
The template body does not open a new scope. To open a new scope
use a ``block`` statement:
.. code-block:: nimrod
template declareInScope(x: expr, t: typeDesc): stmt =
var x: t
template declareInNewScope(x: expr, t: typeDesc): stmt =
# open a new scope:
block:
var x: t
declareInScope(a, int)
a = 42 # works, `a` is known here
declareInNewScope(b, int)
b = 42 # does not work, `b` is unknown
Macros
======
If the template mechanism scares you, you will be pleased to hear that
templates are not really necessary: Macros can do anything that templates can
do and much more. Macros are harder to write than templates and even harder
to get right :-). Now that you have been warned, lets see what a macro *is*.
Macros enable advanced compile-time code tranformations, but they
cannot change Nimrod's syntax. However, this is no real restriction because
Nimrod's syntax is flexible enough anyway.
`Macros`:idx: can be used to implement `domain specific languages`:idx:.
To write macros, one needs to know how the Nimrod concrete syntax is converted
to an abstract syntax tree (AST). (Unfortunately the AST is not documented yet.)
There are two ways to invoke a macro:
(1) invoking a macro like a procedure call (`expression macros`:idx:)
(2) invoking a macro with the special ``macrostmt`` syntax (`statement macros`:idx:)
Expression Macros
-----------------
The following example implements a powerful ``debug`` command that accepts a
variable number of arguments (this cannot be done with templates):
.. code-block:: nimrod
# to work with Nimrod syntax trees, we need an API that is defined in the
# ``macros`` module:
import macros
macro debug(n: expr): stmt =
# `n` is a Nimrod AST that contains the whole macro expression
# this macro returns a list of statements:
result = newNimNode(nnkStmtList, n)
# iterate over any argument that is passed to this macro:
for i in 1..n.len-1:
# add a call to the statement list that writes the expression;
# `toStrLit` converts an AST to its string representation:
result.add(newCall("write", newIdentNode("stdout"), toStrLit(n[i])))
# add a call to the statement list that writes ": "
result.add(newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
# add a call to the statement list that writes the expressions value:
result.add(newCall("writeln", newIdentNode("stdout"), n[i]))
var
a: array[0..10, int]
x = "some string"
a[0] = 42
a[1] = 45
debug(a[0], a[1], x)
The macro call expands to:
.. code-block:: nimrod
write(stdout, "a[0]")
write(stdout, ": ")
writeln(stdout, a[0])
write(stdout, "a[1]")
write(stdout, ": ")
writeln(stdout, a[1])
write(stdout, "x")
write(stdout, ": ")
writeln(stdout, x)
Lets return to the dynamic binding ``r.draw(r)`` notational "problem". Apart
from closures, there is another "solution": Define an infix ``!`` macro
operator which hides it:
.. code-block::
macro `!` (n: expr): expr =
result = newNimNode(nnkCall, n)
var dot = newNimNode(nnkDotExpr, n)
dot.add(n[1]) # obj
if n[2].kind == nnkCall:
# transforms ``obj!method(arg1, arg2, ...)`` to
# ``(obj.method)(obj, arg1, arg2, ...)``
dot.add(n[2][0]) # method
result.add(dot)
result.add(n[1]) # obj
for i in 1..n[2].len-1:
result.add(n[2][i])
else:
# transforms ``obj!method`` to
# ``(obj.method)(obj)``
dot.add(n[2]) # method
result.add(dot)
result.add(n[1]) # obj
r!draw(a, b, c) # will be transfomed into ``r.draw(r, a, b, c)``
Great! 20 lines of complex code to safe a few keystrokes! Obviously, this is
exactly you should not do! (But it makes a cool example.)
Statement Macros
----------------
Statement macros are defined just as expression macros. However, they are
invoked by an expression following a colon.
The following example outlines a macro that generates a lexical analyser from
regular expressions:
.. code-block:: nimrod
macro case_token(n: stmt): stmt =
# creates a lexical analyser from regular expressions
# ... (implementation is an exercise for the reader :-)
nil
case_token: # this colon tells the parser it is a macro statement
of r"[A-Za-z_]+[A-Za-z_0-9]*":
return tkIdentifier
of r"0-9+":
return tkInteger
of r"[\+\-\*\?]+":
return tkOperator
else:
return tkUnknown

View file

@ -1,215 +0,0 @@
===========================================
Tutorial of the Nimrod Programming Language
===========================================
:Author: Andreas Rumpf
Motivation
==========
Why yet another programming language?
Look at the trends behind all the new programming languages:
* They try to be dynamic: Dynamic typing, dynamic method binding, etc.
In my opinion the most things the dynamic features buy could be achieved
with static means in a more efficient and *understandable* way.
* They depend on big runtime environments which you need to
ship with your program as each new version of these may break compability
in subtle ways or you use recently added features - thus forcing your
users to update their runtime environment. Compiled programs where the
executable contains all needed code are simply the better solution.
* They are unsuitable for systems programming: Do you really want to
write an operating system, a device driver or an interpreter in a language
that is just-in-time compiled (or interpreted)?
So what lacks are *good* systems programming languages. Nimrod is such a
language. It offers the following features:
* It is readable: It reads from left to right (unlike the C-syntax
languages).
* It is strongly and statically typed: This enables the compiler to find
more errors. Static typing also makes programs more *readable*.
* It is compiled. (Currently this is done via compilation to C.)
* It is garbage collected. Big systems need garbage collection. Manuell
memory management is also supported through *untraced pointers*.
* It scales because high level features are also available: It has built-in
bit sets, strings, enumerations, objects, arrays and dynamically resizeable
arrays (called *sequences*).
* It has high performance: The current implementation compiles to C
and uses a Deutsch-Bobrow garbage collector together with Christoper's
partial mark-sweep garbage collector leading to excellent execution
speed and a small memory footprint.
* It has real modules with proper interfaces and supports separate
compilation.
* It is portable: It compiles to C and platform specific features have
been separated and documented. So even if your platform is not supported
porting should be easy.
* It is flexible: Although primilarily a procedural language, generic,
functional and object-oriented programming is also supported.
* It is easy to learn, easy to use and leads to elegant programs.
* You can link an embedded debugger to your program (ENDB). ENDB is
very easy to use - there is no need to clutter your code with
``echo`` statements for proper debugging.
Introduction
============
This document is a tutorial for the programming language *Nimrod*. It should
be a readable quick tour through the language instead of a dry specification
(which can be found `here <manual.html>`_). This tutorial assumes that
the reader already knows some other programming language such as Pascal. Thus
it is detailed in cases where Nimrod differs from other programming languages
and kept short where Nimrod is more or less the same.
A quick tour through the language
=================================
The first program
-----------------
We start the tour with a modified "hallo world" program:
.. code-block:: Nimrod
# This is a comment
# Standard IO-routines are always accessible
write(stdout, "What's your name? ")
var name: string = readLine(stdin)
write(stdout, "Hi, " & name & "!\n")
Save this code to the file "greeting.nim". Now compile and run it::
nimrod compile --run greeting.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
appending them after the filename that is to be compiled and run::
nimrod compile --run greeting.nim arg1 arg2
Though it should be pretty obvious what the program does, I will explain the
syntax: Statements which are not indented are executed when the program
starts. Indentation is Nimrod's way of grouping statements. String literals
are enclosed in double quotes. The ``var`` statement declares a new variable
named ``name`` of type ``string`` with the value that is returned by the
``readline`` procedure. Since the compiler knows that ``readline`` returns
a string, you can leave out the type in the declaration. So this will work too:
.. code-block:: Nimrod
var name = readline(stdin)
Note that this is the only form of type inference that exists in Nimrod:
This is because it yields a good compromise between brevity and readability.
The ``&`` operator concates strings together. ``\n`` stands for the
new line character(s). On several operating systems ``\n`` is represented by
*two* characters: Linefeed and Carriage Return. That is why
*character literals* cannot contain ``\n``. But since Nimrod handles strings
so well, this is a nonissue.
The "hallo world" program contains several identifiers that are already
known to the compiler: ``write``, ``stdout``, ``readLine``, etc. These
built-in items are declared in the system_ module which is implicitly
imported by any other module.
Lexical elements
----------------
Let us look into Nimrod's lexical elements in more detail: Like other
programming languages Nimrod consists of identifiers, keywords, comments,
operators, and other punctation marks. Case is *insignificant* in Nimrod and
even underscores are ignored: ``This_is_an_identifier`` and this is the same
identifier ``ThisIsAnIdentifier``. This feature enables one to use other
peoples code without bothering about a naming convention that one does not
like.
String literals are enclosed in double quotes, character literals in single
quotes. There exist also *raw* string and character literals:
.. code-block:: Nimrod
r"C:\program files\nim"
In raw literals the backslash is not an escape character, so they fit
the principle *what you see is what you get*. *Long string literals*
are also available (``""" ... """``); they can span over multiple lines
and the ``\`` is not an escape character either. They are very useful
for embedding SQL code templates for example.
Comments start with ``#`` and run till the end of the line. (Well this is not
quite true, but you should read the manual for a proper explanation.)
... XXX number literals
The usual statements - if, while, for, case
-------------------------------------------
In Nimrod indentation is used to group statements.
An example showing the most common statement types:
.. code-block:: Nimrod
var name = readLine(stdin)
if name == "Andreas":
echo("What a nice name!")
elif name == "":
echo("Don't you have a name?")
else:
echo("Boring name...")
for i in 0..length(name)-1:
if name[i] == 'm':
echo("hey, there is an *m* in your name!")
echo("Please give your password: \n")
var pw = readLine(stdin)
while pw != "12345":
echo("Wrong password! Next try: \n")
pw = readLine(stdin)
echo("""Login complete!
What do you want to do?
delete-everything
restart-computer
go-for-a-walk
""")
case readline(stdin)
of "delete-everything", "restart-computer":
echo("permission denied")
of "go-for-a-walk": echo("please yourself")
else: echo("unknown command")
..
Types
-----
Nimrod has a rich type system. This tutorial only gives a few examples. Read
the `manual <manual.html>`_ for further information:
.. code-block:: Nimrod
type
TMyRecord = object
x, y: int
Procedures
----------
Procedures are subroutines. They are declared in this way:
.. code-block:: Nimrod
proc findSubStr(sub: string,
.. _strutils: strutils.html
.. _system: system.html

View file

@ -1,126 +0,0 @@
#! /bin/sh
#
# Nimrod installation script
# (c) 2008 Andreas Rumpf
#
if [ $# -eq 1 ] ; then
if test -f bin/nimrod
then
echo "Nimrod already built -- skipping this phase"
else
echo "building Nimrod..."
sh ./build.sh || exit 1
echo "...done"
fi
case $1 in
"/usr/bin")
configdir=/etc
libdir=/usr/lib/nimrod
mkdir -p /usr/lib/nimrod
mkdir -p /usr/share/nimrod/doc
cp bin/nimrod /usr/bin/nimrod
cp config/nimdoc.cfg /etc/nimdoc.cfg
cp -r -p lib /usr/lib/nimrod
cp -r -p doc /usr/share/nimrod/doc
;;
"/usr/local/bin")
configdir=/etc
libdir=/usr/local/lib/nimrod
mkdir -p /usr/local/lib/nimrod
mkdir -p /usr/local/share/nimrod/doc
cp bin/nimrod /usr/local/bin/nimrod
cp config/nimdoc.cfg /etc/nimdoc.cfg
cp -r -p lib /usr/local/lib/nimrod
cp -r -p doc /usr/local/share/nimrod/doc
;;
*)
configdir="$1/nimrod/config"
libdir="$1/nimrod/lib"
mkdir -p $1/nimrod
mkdir -p $1/nimrod/bin
mkdir -p $1/nimrod/config
mkdir -p $1/nimrod/lib
mkdir -p $1/nimrod/doc
cp bin/nimrod $1/nimrod/bin/nimrod
cp config/nimdoc.cfg $1/nimrod/config/nimdoc.cfg
cp -r -p lib $1/nimrod
cp -r -p doc $1/nimrod
;;
esac
# write the configuration file
cat >$configdir/nimrod.cfg <<EOF
# Configuration file for the Nimrod Compiler.
# Feel free to edit the default values as you need.
cc = gcc
lib=$libdir
path="\$lib/base"
path="\$lib/base/gtk"
path="\$lib/base/cairo"
path="\$lib/base/x11"
path="\$lib/base/sdl"
path="\$lib/base/opengl"
path="\$lib/base/zip"
path="\$lib/windows"
path="\$lib/posix"
path="\$lib/ecmas"
path="\$lib/extra"
@if release:
obj_checks:off
field_checks:off
range_checks:off
bound_checks:off
overflow_checks:off
assertions:off
stacktrace:off
debugger:off
line_dir:off
opt:speed
@end
# additional options always passed to the compiler:
--verbosity: "1"
hint[LineTooLong]=off
@if unix and not bsd:
passl= "-ldl"
@end
@if icc:
passl = "-cxxlib"
passc = "-cxxlib"
@end
# Configuration for the GNU C/C++ compiler:
#gcc.exe = "gcc-4.3"
#gcc.linkerExe = "gcc-4.3"
gcc.options.debug = "-g"
@if macosx:
gcc.options.always = "-w -fasm-blocks"
@else:
gcc.options.always = "-w"
@end
gcc.options.speed = "-O3 -fno-strict-aliasing"
gcc.options.size = "-Os"
EOF
echo "installation successful"
else
echo "Nimrod installation script"
echo "Usage: [sudo] sh install.h DIR"
echo "Where DIR may be:"
echo " /usr/bin"
echo " /usr/local/bin"
echo " /opt"
echo " <some other dir> (treated like '/opt')"
echo "To deinstall, use the command:"
echo "sh deinstall.sh DIR"
exit 1
fi

79
koch.py
View file

@ -18,7 +18,7 @@ False = 0 == 1
# --------------------- constants ----------------------------------------
NIMROD_VERSION = '0.7.2'
NIMROD_VERSION = '0.7.4'
# 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**
@ -35,8 +35,6 @@ force = False
GENERATE_DIFF = False
# if set, a diff.log file is generated when bootstrapping
# this uses quite a good amount of RAM (ca. 12 MB), so it should not be done
# on underpowered systems.
USE_FPC = True
@ -45,31 +43,6 @@ BOOTCMD = "%s cc --compile:build/platdef.c %s rod/nimrod.nim"
# --------------------------------------------------------------------------
DOC = split("""endb intern lib manual nimrodc steps overview""")
SRCDOC = split("""system os strutils base/regexprs math complex times
parseopt hashes strtabs lexbase parsecfg base/dialogs
posix/posix
streams base/odbcsql
base/zip/zipfiles base/zip/zlib base/zip/libzip
""")
ADD_SRCDOC = split("""
base/cairo/cairo base/cairo/cairoft
base/cairo/cairowin32 base/cairo/cairoxlib
base/gtk/atk base/gtk/gdk2 base/gtk/gdk2pixbuf
base/gtk/gdkglext base/gtk/glib2 base/gtk/gtk2
base/gtk/gtkglext base/gtk/gtkhtml base/gtk/libglade2
base/gtk/pango base/gtk/pangoutils
windows/windows windows/mmsystem windows/nb30
windows/ole2 windows/shellapi windows/shfolder
base/x11/*.nim
base/opengl/*.nim
base/sdl/*.nim
base/lua/*.nim
""")
# --------------------------------------------------------------------------
def Error(msg): sys.exit("[Koch] *** ERROR: " + msg)
def Warn(msg): print "[Koch] *** WARNING: " + msg
def Echo(msg): print "[Koch] " + msg
@ -140,7 +113,7 @@ BaseDir = _baseDir
def Path(a):
# Gets a UNIX like path and converts it to a path on this platform.
# With UNIX like, I mean: slashes, not backslashes, only relative
# paths ('../etc' can be used)
# paths ('../etc') can be used
result = a
if os.sep != "/": result = replace(result, "/", os.sep)
if os.pardir != "..": result = replace(result, "..", os.pardir)
@ -401,12 +374,12 @@ Options:
Possible Commands:
nim builds the Pascal version of Nimrod
rod [options] builds the Nimrod version of Nimrod (with options)
doc builds the documentation in HTML
clean cleans Nimrod project; removes generated files
boot [options] bootstraps with given command line options
rodsrc generates Nimrod version from Pascal version
web generates the website
profile profile the Nimrod compiler
csource build the C sources for installation
zip build the installation ZIP package
inno build the Inno Setup installer
""" % (NIMROD_VERSION + ' ' * (44-len(NIMROD_VERSION)), sys.version)
@ -436,7 +409,6 @@ def main(args):
cmd = args[i]
if cmd == "rod": cmd_rod(join(args[i+1:]))
elif cmd == "nim": cmd_nim()
elif cmd == "doc": cmd_doc()
elif cmd == "clean": cmd_clean()
elif cmd == "boot": cmd_boot(join(args[i+1:]))
elif cmd == "rodsrc": cmd_rodsrc()
@ -444,11 +416,14 @@ def main(args):
elif cmd == "profile": cmd_profile()
elif cmd == "zip": cmd_zip()
elif cmd == "inno": cmd_inno()
elif cmd == "csource": cmd_csource()
else: Error("illegal command: " + cmd)
def cmd_zip():
def cmd_csource():
Exec("nimrod cc -r tools/niminst --var:version=%s csource rod/nimrod" %
NIMROD_VERSION)
def cmd_zip():
Exec("nimrod cc -r tools/niminst --var:version=%s zip rod/nimrod" %
NIMROD_VERSION)
@ -586,50 +561,10 @@ def cmd_profile():
# ------------------ web ------------------------------------------------------
def buildDoc(destPath):
# call nim for the documentation:
for d in DOC:
Exec("nimrod rst2html --putenv:nimrodversion=%s -o:%s/%s.html "
"--index=%s/theindex doc/%s" %
(NIMROD_VERSION, destPath, d, destPath, d))
for d in SRCDOC:
Exec("nimrod doc --putenv:nimrodversion=%s -o:%s/%s.html "
"--index=%s/theindex lib/%s" %
(NIMROD_VERSION, destPath, FilenameNoExt(d), destPath, d))
Exec("nimrod rst2html -o:%s/theindex.html %s/theindex" % (destPath, destPath))
def buildAddDoc(destPath):
# build additional documentation (without the index):
def build(d):
c = Changed("web__"+d, ["lib/"+d+".nim"], EXPLAIN)
if c.check() or force:
Exec("nimrod doc --putenv:nimrodversion=%s -o:%s/%s.html "
" lib/%s" % (NIMROD_VERSION, destPath, FilenameNoExt(d), d))
c.success()
for a in ADD_SRCDOC:
if '*' in a:
for d in Glob("lib/" + a): build(d)
else:
build(a)
def cmd_web():
Exec("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=%s"
% NIMROD_VERSION)
# ------------------ doc ------------------------------------------------------
def cmd_doc():
c = Changed("doc", ["koch.py"] +
Glob("doc/*.txt") + Glob("lib/*.txt") + Glob("lib/*.nim")+
Glob("config/*.cfg"),
EXPLAIN)
if c.check() or force:
cmd_nim() # we need Nimrod for processing the documentation
buildDoc("doc")
if Exists("doc/overview.html"):
c.success()
# -----------------------------------------------------------------------------
def getVersion():

View file

@ -102,51 +102,16 @@ proc absInt(a: int): int {.compilerProc, inline.} =
raiseOverflow()
const
asmVersion = defined(I386) and (defined(vcc) or defined(wcc) or defined(dmc))
asmVersion = defined(I386) and (defined(vcc) or defined(wcc) or
defined(dmc) or defined(gcc) or defined(llvm_gcc))
# my Version of Borland C++Builder does not have
# tasm32, which is needed for assembler blocks
# this is why Borland is not included in the 'when'
useInline = not asmVersion
when asmVersion and defined(gcc):
proc addInt(a, b: int): int {.compilerProc, pure, inline.}
proc subInt(a, b: int): int {.compilerProc, pure, inline.}
proc mulInt(a, b: int): int {.compilerProc, pure, inline.}
proc divInt(a, b: int): int {.compilerProc, pure, inline.}
proc modInt(a, b: int): int {.compilerProc, pure, inline.}
proc negInt(a: int): int {.compilerProc, pure, inline.}
elif asmVersion:
proc addInt(a, b: int): int {.compilerProc, pure.}
proc subInt(a, b: int): int {.compilerProc, pure.}
proc mulInt(a, b: int): int {.compilerProc, pure.}
proc divInt(a, b: int): int {.compilerProc, pure.}
proc modInt(a, b: int): int {.compilerProc, pure.}
proc negInt(a: int): int {.compilerProc, pure.}
elif useInline:
proc addInt(a, b: int): int {.compilerProc, inline.}
proc subInt(a, b: int): int {.compilerProc, inline.}
proc mulInt(a, b: int): int {.compilerProc.}
# mulInt is to large for inlining?
proc divInt(a, b: int): int {.compilerProc, inline.}
proc modInt(a, b: int): int {.compilerProc, inline.}
proc negInt(a: int): int {.compilerProc, inline.}
else:
proc addInt(a, b: int): int {.compilerProc.}
proc subInt(a, b: int): int {.compilerProc.}
proc mulInt(a, b: int): int {.compilerProc.}
proc divInt(a, b: int): int {.compilerProc.}
proc modInt(a, b: int): int {.compilerProc.}
proc negInt(a: int): int {.compilerProc.}
# implementation:
when asmVersion and not defined(gcc):
when asmVersion and not defined(gcc) and not defined(llvm_gcc):
# assembler optimized versions for compilers that
# have an intel syntax assembler:
proc addInt(a, b: int): int =
proc addInt(a, b: int): int {.compilerProc, pure.} =
# a in eax, and b in edx
asm """
mov eax, `a`
@ -156,7 +121,7 @@ when asmVersion and not defined(gcc):
theEnd:
"""
proc subInt(a, b: int): int =
proc subInt(a, b: int): int {.compilerProc, pure.} =
asm """
mov eax, `a`
sub eax, `b`
@ -165,7 +130,7 @@ when asmVersion and not defined(gcc):
theEnd:
"""
proc negInt(a: int): int =
proc negInt(a: int): int {.compilerProc, pure.} =
asm """
mov eax, `a`
neg eax
@ -174,7 +139,7 @@ when asmVersion and not defined(gcc):
theEnd:
"""
proc divInt(a, b: int): int =
proc divInt(a, b: int): int {.compilerProc, pure.} =
asm """
mov eax, `a`
mov ecx, `b`
@ -185,7 +150,7 @@ when asmVersion and not defined(gcc):
theEnd:
"""
proc modInt(a, b: int): int =
proc modInt(a, b: int): int {.compilerProc, pure.} =
asm """
mov eax, `a`
mov ecx, `b`
@ -197,7 +162,7 @@ when asmVersion and not defined(gcc):
mov eax, edx
"""
proc mulInt(a, b: int): int =
proc mulInt(a, b: int): int {.compilerProc, pure.} =
asm """
mov eax, `a`
mov ecx, `b`
@ -208,99 +173,105 @@ when asmVersion and not defined(gcc):
theEnd:
"""
elif asmVersion and defined(gcc):
proc addInt(a, b: int): int =
asm """ "addl %1,%%eax\n"
elif false: # asmVersion and (defined(gcc) or defined(llvm_gcc)):
proc addInt(a, b: int): int {.compilerProc, inline.} =
# don't use a pure proc here!
asm """
"addl %%ecx, %%eax\n"
"jno 1\n"
"call _raiseOverflow\n"
"1: \n"
:"=a"(`result`)
:"a"(`a`), "c"(`b`)
"""
proc subInt(a, b: int): int {.compilerProc, inline.} =
asm """ "subl %%ecx,%%eax\n"
"jno 1\n"
"call _raiseOverflow\n"
"1: \n"
:"=a"(`result`)
:"a"(`a`), "c"(`b`)
"""
proc mulInt(a, b: int): int {.compilerProc, inline.} =
asm """ "xorl %%edx, %%edx\n"
"imull %%ecx\n"
"jno 1\n"
"call _raiseOverflow\n"
"1: \n"
:"=a"(`a`)
:"a"(`a`), "r"(`b`)
:"=a"(`result`)
:"a"(`a`), "c"(`b`)
:"%edx"
"""
proc subInt(a, b: int): int =
asm """ "subl %1,%%eax\n"
"jno 1\n"
"call _raiseOverflow\n"
"1: \n"
:"=a"(`a`)
:"a"(`a`), "r"(`b`)
proc negInt(a: int): int {.compilerProc, inline.} =
asm """ "negl %%eax\n"
"jno 1\n"
"call _raiseOverflow\n"
"1: \n"
:"=a"(`result`)
:"a"(`a`)
"""
proc negInt(a: int): int =
asm """ "negl %%eax\n"
"jno 1\n"
"call _raiseOverflow\n"
"1: \n"
:"=a"(`a`)
:"a"(`a`)
"""
proc divInt(a, b: int): int =
proc divInt(a, b: int): int {.compilerProc, inline.} =
asm """ "xorl %%edx, %%edx\n"
"idivl %%ecx\n"
"jno 1\n"
"call _raiseOverflow\n"
"1: \n"
:"=a"(`a`)
:"a"(`a`), "c"(`b`)
:"%edx"
:"=a"(`result`)
:"a"(`a`), "c"(`b`)
:"%edx"
"""
proc modInt(a, b: int): int =
proc modInt(a, b: int): int {.compilerProc, inline.} =
asm """ "xorl %%edx, %%edx\n"
"idivl %%ecx\n"
"jno 1\n"
"call _raiseOverflow\n"
"1: \n"
"movl %%edx, %%eax"
:"=a"(`a`)
:"a"(`a`), "c"(`b`)
:"%edx"
:"=a"(`result`)
:"a"(`a`), "c"(`b`)
:"%edx"
"""
proc mulInt(a, b: int): int =
asm """ "xorl %%edx, %%edx\n"
"imull %%ecx\n"
"jno 1\n"
"call _raiseOverflow\n"
"1: \n"
:"=a"(`a`)
:"a"(`a`), "c"(`b`)
:"%edx"
"""
else:
# Platform independant versions of the above (slower!)
proc addInt(a, b: int): int =
# Platform independant versions of the above (slower!)
when not defined(addInt):
proc addInt(a, b: int): int {.compilerProc, inline.} =
result = a +% b
if (result xor a) >= 0 or (result xor b) >= 0:
return result
raiseOverflow()
proc subInt(a, b: int): int =
when not defined(subInt):
proc subInt(a, b: int): int {.compilerProc, inline.} =
result = a -% b
if (result xor a) >= 0 or (result xor not b) >= 0:
return result
raiseOverflow()
proc negInt(a: int): int =
when not defined(negInt):
proc negInt(a: int): int {.compilerProc, inline.} =
if a != low(int): return -a
raiseOverflow()
proc divInt(a, b: int): int =
when not defined(divInt):
proc divInt(a, b: int): int {.compilerProc, inline.} =
if b == 0:
raiseDivByZero()
if a == low(int) and b == -1:
raiseOverflow()
return a div b
proc modInt(a, b: int): int =
when not defined(modInt):
proc modInt(a, b: int): int {.compilerProc, inline.} =
if b == 0:
raiseDivByZero()
return a mod b
when not defined(mulInt):
#
# This code has been inspired by Python's source code.
# The native int product x*y is either exactly right or *way* off, being
@ -321,7 +292,7 @@ else:
# the only one that can lose catastrophic amounts of information, it's the
# native int product that must have overflowed.
#
proc mulInt(a, b: int): int =
proc mulInt(a, b: int): int {.compilerProc.} =
var
resAsFloat, floatProd: float

View file

@ -281,7 +281,7 @@ proc getServerSoftware*(): string =
proc setTestData*(keysvalues: openarray[string]) =
## fills the appropriate environment variables to test your CGI application.
## This can only simulate the 'GET' 'REQUEST_METHOD'. `keysvalues` should
## This can only simulate the 'GET' request method. `keysvalues` should
## provide embedded (name, value)-pairs. Example:
##
## .. code-block:: Nimrod
@ -300,6 +300,7 @@ proc setTestData*(keysvalues: openarray[string]) =
proc writeContentType*() =
## call this before starting to send your HTML data to `stdout`. This
## is just a shorthand for:
##
## .. code-block:: Nimrod
## write(stdout, "Content-type: text/html\n\n")
write(stdout, "Content-type: text/html\n\n")

View file

@ -1,3 +1,5 @@
{.deadCodeElim: on.}
import
glib2

View file

@ -1,3 +1,5 @@
{.deadCodeElim: on.}
import
glib2, gdk2pixbuf, pango

View file

@ -1,3 +1,5 @@
{.deadCodeElim: on.}
import
glib2

View file

@ -1,3 +1,5 @@
{.deadCodeElim: on.}
import
Glib2, Gdk2

View file

@ -1,3 +1,5 @@
{.deadCodeElim: on.}
when defined(windows):
const
gliblib = "libglib-2.0-0.dll"

View file

@ -1,3 +1,5 @@
{.deadCodeElim: on.}
import
glib2, atk, pango, gdk2pixbuf, gdk2

View file

@ -1,3 +1,5 @@
{.deadCodeElim: on.}
import
Glib2, Gdk2, Gtk2, GdkGLExt

View file

@ -1,3 +1,5 @@
{.deadCodeElim: on.}
import
gtk2, glib2, atk, pango, gdk2pixbuf, gdk2

View file

@ -1,3 +1,5 @@
{.deadCodeElim: on.}
import
glib2, gtk2

View file

@ -1,3 +1,5 @@
{.deadCodeElim: on.}
import
glib2

View file

@ -1,3 +1,5 @@
{.deadCodeElim: on.}
import
glib2, pango

View file

@ -270,7 +270,8 @@
#
#
#
{.deadCodeElim: on.}
when defined(windows):
const SDLLibName = "SDL.dll"
elif defined(macosx):

View file

@ -318,11 +318,6 @@ proc Mix_LoadWAV_RW*(src: PSDL_RWops, freesrc: int): PMix_Chunk{.cdecl,
proc Mix_LoadWAV*(filename: cstring): PMix_Chunk
proc Mix_LoadMUS*(filename: cstring): PMix_Music{.cdecl,
importc, dynlib: SDL_MixerLibName.}
##if 0 { This hasn't been hooked into music.c yet }
#{ Load a music file from an SDL_RWop object (MikMod-specific currently)
# Matt Campbell (matt@campbellhome.dhs.org) April 2000 }
#function Mix_LoadMUS_RW(SDL_RWops *rw) : PMix_Music; cdecl;
##endif
# Load a wave file of the mixer format from a memory buffer
proc Mix_QuickLoad_WAV*(mem: PUint8): PMix_Chunk{.cdecl,
importc, dynlib: SDL_MixerLibName.}

View file

@ -126,5 +126,5 @@ elif defined(mac):
nss = NSLookupSymbolInModule(NSModule(lib), name)
result = TProcAddr(NSAddressOfSymbol(nss))
else: # workaround a newly introduced bug :-(
else:
{.error: "no implementation for dyncalls".}

View file

@ -326,6 +326,9 @@ proc echo*(x: cstring) = ewriteln(x)
proc echo[Ty](x: Ty) =
echo(x)
proc echo[Ty](x: openArray[Ty]) =
for a in items(x): echo(a)
# Arithmetic:
proc addInt(a, b: int): int {.pure, compilerproc.} =
asm """

View file

@ -61,9 +61,6 @@ type
filename: CString
len: int # length of slots (when not debugging always zero)
TTempFrame = tuple[ # used for recursion elimination in WriteStackTrace
procname: CString, line: int]
var
buf: string # cannot be allocated on the stack!
assertBuf: string # we need a different buffer for
@ -72,33 +69,45 @@ var
framePtr {.exportc.}: PFrame
tempFrames: array [0..255, TTempFrame] # cannot be allocated
# on the stack!
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
proc auxWriteStackTrace(f: PFrame, s: var string) =
const
firstCalls = 32
var
it = f
i = 0
total = 0
while it != nil and i <= high(tempFrames):
tempFrames[i] = (it.procname, it.line)
while it != nil and i <= high(tempFrames)-(firstCalls-1):
# the (-1) is for a nil entry that marks where the '...' should occur
tempFrames[i] = it
inc(i)
inc(total)
it = it.prev
var b = it
while it != nil:
inc(total)
it = it.prev
# if the buffer overflowed print '...':
for j in 1..total-i-(firstCalls-1):
if b != nil: b = b.prev
if total != i:
add(s, "(")
add(s, $(total-i))
add(s, " calls omitted) ...\n")
tempFrames[i] = nil
inc(i)
while b != nil and i <= high(tempFrames):
tempFrames[i] = b
inc(i)
b = b.prev
for j in countdown(i-1, 0):
add(s, $tempFrames[j].procname)
if tempFrames[j].line > 0:
add(s, ", line: ")
add(s, $tempFrames[j].line)
add(s, "\n")
if tempFrames[j] == nil:
add(s, "(")
add(s, $(total-i-1))
add(s, " calls omitted) ...\n")
else:
add(s, $tempFrames[j].procname)
if tempFrames[j].line > 0:
add(s, ", line: ")
add(s, $tempFrames[j].line)
add(s, "\n")
proc rawWriteStackTrace(s: var string) =
if framePtr == nil:
@ -156,8 +165,7 @@ proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
raise gAssertionFailed # newException(EAssertionFailed, assertBuf)
proc WriteStackTrace() =
var
s: string = ""
var s = ""
rawWriteStackTrace(s)
writeToStdErr(s)
@ -208,13 +216,13 @@ assertBuf = newString(2048)
setLen(buf, 0)
setLen(assertBuf, 0)
proc raiseRangeError(val: biggestInt) {.compilerproc, noreturn.} =
proc raiseRangeError(val: biggestInt) {.compilerproc, noreturn, noinline.} =
raise newException(EOutOfRange, "value " & $val & " out of range")
proc raiseIndexError() {.compilerproc, noreturn.} =
proc raiseIndexError() {.compilerproc, noreturn, noinline.} =
raise newException(EInvalidIndex, "index out of bounds")
proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
proc raiseFieldError(f: string) {.compilerproc, noreturn, noinline.} =
raise newException(EInvalidField, f & " is not accessible")
proc chckIndx(i, a, b: int): int =

View file

@ -13,11 +13,10 @@
# * incremental
# * non-recursive
# * generational
# * excellent performance
# Future Improvements:
# * Both dlmalloc and TLSF lack zero-overhead object allocation. Thus, for
# small objects we will should use our own allocator.
# small objects we should use our own allocator.
# * Support for multi-threading. However, locks for the reference counting
# might turn out to be too slow.
@ -332,7 +331,7 @@ proc CellSetPut(t: var TCellSet, key: TAddress): PPageDesc =
# ---------- slightly higher level procs --------------------------------------
proc in_Operator(s: TCellSet, cell: PCell): bool =
proc contains(s: TCellSet, cell: PCell): bool =
var u = cast[TAddress](cell)
var t = CellSetGet(s, u shr PageShift)
if t != nil:
@ -468,23 +467,6 @@ proc prepareDealloc(cell: PCell) =
proc setStackBottom(theStackBottom: pointer) {.compilerproc.} =
stackBottom = theStackBottom
proc initGC() =
when traceGC:
for i in low(TCellState)..high(TCellState): CellSetInit(states[i])
gch.stackScans = 0
gch.cycleCollections = 0
gch.maxThreshold = 0
gch.maxStackSize = 0
gch.maxStackPages = 0
gch.cycleTableSize = 0
# init the rt
init(gch.zct)
init(gch.tempStack)
CellSetInit(gch.cycleRoots)
CellSetInit(gch.stackCells)
gch.mask = 0
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
proc PossibleRoot(gch: var TGcHeap, c: PCell) {.inline.} =
if canbeCycleRoot(c): incl(gch.cycleRoots, c)
@ -535,6 +517,23 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) =
if dest^ != nil: decRef(usrToCell(dest^))
dest^ = src
proc initGC() =
when traceGC:
for i in low(TCellState)..high(TCellState): CellSetInit(states[i])
gch.stackScans = 0
gch.cycleCollections = 0
gch.maxThreshold = 0
gch.maxStackSize = 0
gch.maxStackPages = 0
gch.cycleTableSize = 0
# init the rt
init(gch.zct)
init(gch.tempStack)
CellSetInit(gch.cycleRoots)
CellSetInit(gch.stackCells)
gch.mask = 0
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
assert(n.kind == nkCase)
var d: int

View file

@ -151,8 +151,7 @@ proc open(L: var TBaseLexer, input: PStream, bufLen: int = 8192) =
skip_UTF_8_BOM(L)
proc getColNumber(L: TBaseLexer, pos: int): int =
result = pos - L.lineStart
assert(result >= 0)
result = abs(pos - L.lineStart)
proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
var i: int

View file

@ -31,6 +31,40 @@
# cog.out(toEnum(key, val))
#]]]
type
TNimrodNodeKind* = enum
nnkNone, nnkEmpty, nnkIdent, nnkSym,
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit,
nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit,
nnkTripleStrLit, nnkMetaNode, nnkNilLit, nnkDotCall,
nnkCommand, nnkCall, nnkGenericCall, nnkExplicitTypeListCall,
nnkExprEqExpr, nnkExprColonExpr, nnkIdentDefs, nnkVarTuple,
nnkInfix, nnkPrefix, nnkPostfix, nnkPar,
nnkCurly, nnkBracket, nnkBracketExpr, nnkPragmaExpr,
nnkRange, nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr,
nnkIfExpr, nnkElifExpr, nnkElseExpr, nnkLambda,
nnkAccQuoted, nnkHeaderQuoted, nnkTableConstr, nnkQualified,
nnkHiddenStdConv, nnkHiddenSubConv, nnkHiddenCallConv, nnkConv,
nnkCast, nnkAddr, nnkHiddenAddr, nnkHiddenDeref,
nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64,
nnkChckRange, nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray,
nnkAsgn, nnkFastAsgn, nnkDefaultTypeParam, nnkGenericParams,
nnkFormalParams, nnkOfInherit, nnkModule, nnkProcDef,
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, nnkImportAs, nnkIncludeStmt, nnkAccessStmt,
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkStmtListType,
nnkBlockType, nnkVm, nnkTypeOfExpr, nnkObjectTy,
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
nnkRefTy, nnkPtrTy, nnkVarTy, nnkProcTy,
nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
TNimNodeKinds* = set[TNimrodNodeKind]
TNimrodTypeKind* = enum
ntyNone, ntyBool, ntyChar, ntyEmpty,
ntyArrayConstr, ntyNil, ntyGeneric, ntyGenericInst,
@ -49,40 +83,6 @@ type
nskMacro, nskTemplate, nskField, nskEnumField,
nskForVar, nskModule, nskLabel, nskStub
TNimSymKinds* = set[TNimrodSymKind]
TNimrodNodeKind* = enum
nnkNone, nnkEmpty, nnkIdent, nnkSym,
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit,
nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit,
nnkTripleStrLit, nnkMetaNode, nnkNilLit, nnkDotCall,
nnkCommand, nnkCall, nnkGenericCall, nnkExplicitTypeListCall,
nnkExprEqExpr, nnkExprColonExpr, nnkIdentDefs, nnkInfix,
nnkPrefix, nnkPostfix, nnkPar, nnkCurly,
nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange,
nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr,
nnkElifExpr, nnkElseExpr, nnkLambda, nnkAccQuoted,
nnkHeaderQuoted, nnkTableConstr, nnkQualified, nnkHiddenStdConv,
nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast,
nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv,
nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange,
nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray, nnkAsgn,
nnkDefaultTypeParam, nnkGenericParams, nnkFormalParams, nnkOfInherit,
nnkModule, nnkProcDef, 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, nnkImportAs,
nnkIncludeStmt, nnkAccessStmt, nnkCommentStmt, nnkStmtListExpr,
nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkVm,
nnkTypeOfExpr, nnkObjectTy, nnkTupleTy, nnkRecList,
nnkRecCase, nnkRecWhen, nnkRefTy, nnkPtrTy,
nnkVarTy, nnkProcTy, nnkEnumTy, nnkEnumFieldDef,
nnkReturnToken
TNimNodeKinds* = set[TNimrodNodeKind]
#[[[end]]]
type

View file

@ -421,18 +421,4 @@ struct NimException {
};
#endif
#if 0
typedef struct TStringDesc {
NI len;
NI space;
NIM_CHAR data[1]; /* SEQ_DECL_SIZE]; */
} TStringDesc;
typedef struct {
NI len, space;
} TGenericSeq;
typedef TGenericSeq* PGenericSeq;
#endif
#endif

View file

@ -11,6 +11,7 @@
## retrieving environment variables, reading command line arguments,
## working with directories, running shell commands, etc.
## This module is -- like any other basic library -- platform independant.
{.deadCodeElim: on.}
{.push debugger: off.}
@ -863,9 +864,9 @@ var
when defined(windows):
# because we support Windows GUI applications, things get really
# messy here...
proc GetEnvironmentStringsA*(): cstring {.
proc GetEnvironmentStringsA(): cstring {.
stdcall, dynlib: "kernel32", importc.}
proc FreeEnvironmentStringsA*(para1: cstring): int32 {.
proc FreeEnvironmentStringsA(para1: cstring): int32 {.
stdcall, dynlib: "kernel32", importc.}
proc strEnd(cstr: CString, c = 0): CString {.importc: "strchr", nodecl.}
@ -1091,8 +1092,8 @@ proc expandFilename(filename: string): string =
proc parseCmdLine*(c: string): seq[string] =
## Splits a command line into several components; components are separated by
## whitespace or are quoted with the ``"`` or ``'`` characters. This proc is
## only occassionally useful, better use the `parseopt` module.
## whitespace unless the whitespace occurs within ``"`` or ``'`` quotes.
## This proc is only occassionally useful, better use the `parseopt` module.
result = @[]
var i = 0
while c[i] != '\0':

View file

@ -54,7 +54,7 @@ type
TTokKind = enum
tkInvalid, tkEof,
tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
TToken{.final.} = object # a token
TToken {.final.} = object # a token
kind: TTokKind # the type of the token
literal: string # the parsed (string) literal
@ -139,7 +139,7 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
inc(c.bufpos) # skip '\'
case c.buf[c.bufpos]
of 'n', 'N':
tok.literal = tok.literal & nl
add(tok.literal, nl)
Inc(c.bufpos)
of 'r', 'R', 'c', 'C':
add(tok.literal, '\c')

View file

@ -53,7 +53,7 @@ proc init(cmdline: string = ""): TOptParser =
else:
result.cmd = ""
for i in countup(1, ParamCount()):
result.cmd = result.cmd & quoteIfSpaceExists(paramStr(i)) & ' '
result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i)) & ' '
result.kind = cmdEnd
result.key = ""
result.val = ""

View file

@ -22,12 +22,12 @@ proc reprPointer(x: pointer): string {.compilerproc.} =
proc reprStrAux(result: var string, s: string) =
if cast[pointer](s) == nil:
add result "nil"
add result, "nil"
return
add result, reprPointer(cast[pointer](s)) & "\""
for c in items(s):
case c
of '"': add result "\\\""
of '"': add result, "\\\""
of '\\': add result, "\\\\" # BUGFIX: forgotten
of '\10': add result, "\\10\"\n\"" # " \n " # better readability
of '\128' .. '\255', '\0'..'\9', '\11'..'\31':

View file

@ -12,6 +12,8 @@
## All the routines here are avaiable for the EMCAScript target
## too!
{.deadCodeElim: on.}
{.push debugger:off .} # the user does not want to trace a part
# of the standard library!
@ -41,19 +43,19 @@ proc strip*(s: string): string {.noSideEffect.}
proc toLower*(s: string): string {.noSideEffect.}
## Converts `s` into lower case. This works only for the letters A-Z.
## See `charsets.nativeToLower` for a version that is locale-dependant.
## See `unicode.toLower` for a version that works for any Unicode character.
proc toLower*(c: Char): Char {.noSideEffect.}
## Converts `c` into lower case. This works only for the letters A-Z.
## See `charsets.nativeToLower()` for a version that is locale-dependant.
## See `unicode.toLower` for a version that works for any Unicode character.
proc toUpper*(s: string): string {.noSideEffect.}
## Converts `s` into upper case. This works only for the letters a-z.
## See `charsets.nativeToUpper()` for a version that is locale-dependant.
## See `unicode.toUpper` for a version that works for any Unicode character.
proc toUpper*(c: Char): Char {.noSideEffect.}
## Converts `c` into upper case. This works only for the letters a-z.
## See `charsets.nativeToUpper()` for a version that is locale-dependant.
## See `unicode.toUpper` for a version that works for any Unicode character.
proc capitalize*(s: string): string {.noSideEffect.}
## Converts the first character of `s` into upper case.
@ -71,6 +73,10 @@ proc findSubStr*(sub: char, s: 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.
proc findChars*(chars: set[char], s: string, start: int = 0): int {.noSideEffect.}
## Searches for `chars` in `s` starting at position `start`. If `s` contains
## none of the characters in `chars`, -1 is returned.
proc replaceStr*(s, sub, by: string): string {.noSideEffect.}
## Replaces `sub` in `s` by the string `by`.
@ -173,11 +179,14 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect.}
## | < 0 iff a < b
## | > 0 iff a > b
proc in_Operator*(s: string, c: char): bool {.noSideEffect.}
## Same as `findSubStr(c, s) >= 0`.
proc contains*(s: string, c: char): bool {.noSideEffect.}
## Same as ``findSubStr(c, s) >= 0``.
proc in_Operator*(s, sub: string): bool {.noSideEffect.}
## Same as `findSubStr(sub, s) >= 0`.
proc contains*(s, sub: string): bool {.noSideEffect.}
## Same as ``findSubStr(sub, s) >= 0``.
proc contains*(s: string, chars: set[char]): bool {.noSideEffect.}
## Same as ``findChars(s, chars) >= 0``.
proc toHex*(x: BiggestInt, len: int): string {.noSideEffect.}
## Converts `x` to its hexadecimal representation. The resulting string
@ -259,10 +268,10 @@ proc allCharsInSet*(s: string, theSet: TCharSet): bool =
if not (c in theSet): return false
return true
proc quoteIfSpaceExists*(s: string): string =
proc quoteIfContainsWhite*(s: string): string =
## returns ``'"' & s & '"'`` if `s` contains a space and does not
## start with a quote, else returns `s`
if findSubStr(' ', s) >= 0 and s[0] != '"':
if findChars({' ', '\t'}, s) >= 0 and s[0] != '"':
result = '"' & s & '"'
else:
result = s
@ -489,10 +498,18 @@ proc findSubStr(sub: char, s: string, start: int = 0): int =
if sub == s[i]: return i
return -1
proc in_Operator(s: string, c: char): bool =
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 findChars(chars, s) >= 0
proc contains(s: string, c: char): bool =
return findSubStr(c, s) >= 0
proc in_Operator(s, sub: string): bool =
proc contains(s, sub: string): bool =
return findSubStr(sub, s) >= 0
proc replaceStr(s, sub, by: string): string =

View file

@ -91,10 +91,13 @@ proc writeln[Ty](f: TFile, x: Ty) =
write(f, "\n")
proc writeln[Ty](f: TFile, x: openArray[Ty]) =
write(f, x)
for i in items(x): write(f, i)
write(f, "\n")
proc echo[Ty](x: Ty) = writeln(stdout, x)
proc echo[Ty](x: openArray[Ty]) =
for i in items(x): write(stdout, i)
write(stdout, "\n")
# interface to the C procs:
proc fopen(filename, mode: CString): pointer {.importc: "fopen", noDecl.}

View file

@ -15,13 +15,6 @@
# we don't use refcounts because that's a behaviour
# the programmer may not want
type
# len and space without counting the terminating zero:
NimStringDesc {.compilerproc, final.} = object of TGenericSeq
data: array[0..100_000_000, char] # for the '\0' character
NimString = ptr NimStringDesc
# implementation:
proc resize(old: int): int {.inline.} =

View file

@ -58,6 +58,17 @@ proc defined*[T] (x: T): bool {.magic: "Defined", noSideEffect.}
proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.}
## Boolean not; returns true iff ``x == false``.
proc `and`*(x, y: bool): bool {.magic: "And", noSideEffect.}
## Boolean ``and``; returns true iff ``x == y == true``.
## Evaluation is short-circuited: This means that if ``x`` is false,
## ``y`` will not even be evaluated.
proc `or`*(x, y: bool): bool {.magic: "Or", noSideEffect.}
## Boolean ``or``; returns true iff ``not (not x and not y)``.
## Evaluation is short-circuited: This means that if ``x`` is true,
## ``y`` will not even be evaluated.
proc `xor`*(x, y: bool): bool {.magic: "Xor", noSideEffect.}
## Boolean `exclusive or`; returns true iff ``x != y``.
proc new*[T](a: var ref T) {.magic: "New".}
## creates a new object of type ``T`` and returns a safe (traced)
## reference to it in ``a``.
@ -94,6 +105,19 @@ type
seq*{.magic: "Seq".}[T] ## Generic type to construct sequences.
set*{.magic: "Set".}[T] ## Generic type to construct bit sets.
when not defined(EcmaScript) and not defined(NimrodVM):
type
TGenericSeq {.compilerproc, pure.} = object
len, space: int
PGenericSeq {.exportc.} = ptr TGenericSeq
# len and space without counting the terminating zero:
NimStringDesc {.compilerproc, final.} = object of TGenericSeq
data: array[0..100_000_000, char]
NimString = ptr NimStringDesc
include hti
type
Byte* = Int8 ## this is an alias for ``int8``, that is a signed
## int 8 bits wide.
@ -403,8 +427,8 @@ proc abs*(x: int8): int8 {.magic: "AbsI", noSideEffect.}
proc abs*(x: int16): int16 {.magic: "AbsI", noSideEffect.}
proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.}
proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.}
## returns the absolute value of `x`. If `x` is ``low(x)`` (that is
## -MININT for its type), an overflow exception is thrown (if overflow
## returns the absolute value of `x`. If `x` is ``low(x)`` (that
## is -MININT for its type), an overflow exception is thrown (if overflow
## checking is turned on).
proc min*(x, y: int): int {.magic: "MinI", noSideEffect.}
@ -500,18 +524,6 @@ proc abs*(x: float): float {.magic: "AbsF64", noSideEffect.}
proc min*(x, y: float): float {.magic: "MinF64", noSideEffect.}
proc max*(x, y: float): float {.magic: "MaxF64", noSideEffect.}
# boolean operators:
proc `and`*(x, y: bool): bool {.magic: "And", noSideEffect.}
## Boolean ``and``; returns true iff ``x == y == true``.
## Evaluation is short-circuited: This means that if ``x`` is false,
## ``y`` will not even be evaluated.
proc `or`*(x, y: bool): bool {.magic: "Or", noSideEffect.}
## Boolean ``or``; returns true iff ``not (not x and not y)``.
## Evaluation is short-circuited: This means that if ``x`` is true,
## ``y`` will not even be evaluated.
proc `xor`*(x, y: bool): bool {.magic: "Xor", noSideEffect.}
## Boolean `exclusive or`; returns true iff ``x != y``.
# set operators
proc `*` *[T](x, y: set[T]): set[T] {.magic: "MulSet", noSideEffect.}
## This operator computes the intersection of two sets.
@ -563,11 +575,12 @@ template `>` * (x, y: expr): expr =
## "is greater" operator. This is the same as ``y < x``.
y < x
proc in_Operator*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.}
proc contains*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.}
## One should overload this proc if one wants to overload the ``in`` operator.
## The parameters are in reverse order! This is because the unification
## algorithm that Nimrod uses for overload resolution works from left to
## right.
## The parameters are in reverse order! ``a in b`` is a template for
## ``contains(b, a)``.
## This is because the unification algorithm that Nimrod uses for overload
## resolution works from left to right.
## But for the ``in`` operator that would be the wrong direction for this
## piece of code:
##
@ -578,11 +591,11 @@ proc in_Operator*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.}
## If ``in`` had been declared as ``[T](elem: T, s: set[T])`` then ``T`` would
## have been bound to ``char``. But ``s`` is not compatible to type
## ``set[char]``! The solution is to bind ``T`` to ``range['a'..'z']``. This
## is achieved by reversing the parameters for ``in_operator``; ``in`` then
## is achieved by reversing the parameters for ``contains``; ``in`` then
## passes its arguments in reverse order.
template `in` * (x, y: expr): expr = in_Operator(y, x)
template `not_in` * (x, y: expr): expr = not in_Operator(y, x)
template `in` * (x, y: expr): expr = contains(y, x)
template `not_in` * (x, y: expr): expr = not contains(y, x)
proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
template `is_not` *(x, y: expr): expr = not (x is y)
@ -616,15 +629,17 @@ proc `&` * (x: char, y: string): string {.
magic: "ConStrStr", noSideEffect, merge.}
## is the `concatenation operator`. It concatenates `x` and `y`.
proc add * (x: var string, y: char) {.magic: "AppendStrCh".}
proc add * (x: var string, y: string) {.magic: "AppendStrStr".}
proc add*(x: var string, y: char) {.magic: "AppendStrCh".}
proc add*(x: var string, y: string) {.magic: "AppendStrStr".}
when not defined(ECMAScript):
{.push overflow_checks:off}
proc add* (x: var string, y: cstring) =
var i = 0
while y[i] != '\0':
add(x, y[i])
inc(i)
{.pop.}
else:
proc add* (x: var string, y: cstring) {.pure.} =
asm """
@ -646,7 +661,7 @@ proc add *[T](x: var seq[T], y: seq[T]) {.magic: "AppendSeqSeq".}
proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.}
## takes any Nimrod variable and returns its string representation. It
## works even for complex data graphs with cycles. This is an invaluable
## works even for complex data graphs with cycles. This is a great
## debugging tool.
type
@ -730,6 +745,15 @@ const
## is the endianness of the target CPU. This is a valuable piece of
## information for low-level code only. This works thanks to compiler magic.
hostOS* {.magic: "HostOS"}: string = ""
## a string that describes the host operating system. Possible values:
## "windows", "macosx", "linux", "netbsd", "freebsd", "openbsd", "solaris",
## "aix"
hostCPU* {.magic: "HostCPU"}: string = ""
## a string that describes the host CPU. Possible values:
## "i386", "alpha", "powerpc", "sparc", "amd64", "mips", "arm"
proc toFloat*(i: int): float {.
magic: "ToFloat", noSideEffect, importc: "toFloat".}
## converts an integer `i` into a ``float``. If the conversion
@ -896,6 +920,12 @@ proc `$` *(x: string): string {.magic: "StrToStr", noSideEffect.}
## as it is. This operator is useful for generic code, so
## that ``$expr`` also works if ``expr`` is already a string.
proc `$` *(x: TAnyEnum): string {.magic: "EnumToStr", noSideEffect.}
## The stingify operator for an enumeration argument. This works for
## any enumeration type thanks to compiler magic. If a
## a ``$`` operator for a concrete enumeration is provided, this is
## used instead. (In other words: *Overwriting* is possible.)
# undocumented:
proc getRefcount*[T](x: ref T): int {.importc: "getRefcount".}
## retrieves the reference count of an heap-allocated object. The
@ -1136,6 +1166,10 @@ proc echo*[Ty](x: Ty) {.inline.}
## equivalent to ``writeln(stdout, x); flush(stdout)``. BUT: This is
## available for the ECMAScript target too!
proc echo*[Ty](x: openarray[Ty]) {.inline.}
## equivalent to ``writeln(stdout, x); flush(stdout)``. BUT: This is
## available for the ECMAScript target too!
template newException(exceptn, message: expr): expr =
block: # open a new scope
@ -1169,8 +1203,6 @@ when not defined(EcmaScript) and not defined(NimrodVM):
when not defined(EcmaScript) and not defined(NimrodVM):
include hti
proc initGC()
var
@ -1370,13 +1402,6 @@ when not defined(EcmaScript) and not defined(NimrodVM):
include arithm
{.pop.} # stack trace
# sequence type declarations here because the GC needs them too:
type
TGenericSeq {.compilerproc, pure.} = object
len, space: int
PGenericSeq {.exportc.} = ptr TGenericSeq
const
GenericSeqSize = (2 * sizeof(int))
@ -1417,13 +1442,12 @@ elif defined(NimrodVM):
proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1
proc echo[Ty](x: Ty) = nil
proc echo[Ty](x: openarray[Ty]) = nil
proc cmp(x, y: string): int =
if x == y: return 0
if x < y: return -1
return 1
include macros
{.pop.} # checks
{.pop.} # hints

View file

@ -276,8 +276,8 @@ else:
proc getDateStr(): string =
var ti = getLocalTime(getTime())
result = $ti.year & "-" & intToStr(ord(ti.month)+1, 2) &
"-" & intToStr(ti.monthDay, 2)
result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) &
'-' & intToStr(ti.monthDay, 2)
proc getClockStr(): string =
var ti = getLocalTime(getTime())

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,8 @@
## Define ``winUnicode`` before importing this module for the
## unicode version.
{.deadCodeElim: on.}
type
ATOM* = int16
TAtom* = ATOM

View file

@ -7,7 +7,7 @@ all:
.PHONY : install
install:
sh install.sh /usr/bin
sh build.sh
.PHONY : clean
clean:

View file

@ -66,12 +66,93 @@ for key, val in enums.items():
cog.out(b)
]]]*)
type
TTypeFlag = (
tfVarargs, tfFinal, tfAcyclic, tfEnumHasWholes);
TTypeFlags = set of TTypeFlag;
TNodeKind = (
nkNone, nkEmpty, nkIdent, nkSym,
nkType, nkCharLit, nkIntLit, nkInt8Lit,
nkInt16Lit, nkInt32Lit, nkInt64Lit, nkFloatLit,
nkFloat32Lit, nkFloat64Lit, nkStrLit, nkRStrLit,
nkTripleStrLit, nkMetaNode, nkNilLit, nkDotCall,
nkCommand, nkCall, nkGenericCall, nkExplicitTypeListCall,
nkExprEqExpr, nkExprColonExpr, nkIdentDefs, nkVarTuple,
nkInfix, nkPrefix, nkPostfix, nkPar,
nkCurly, nkBracket, nkBracketExpr, nkPragmaExpr,
nkRange, nkDotExpr, nkCheckedFieldExpr, nkDerefExpr,
nkIfExpr, nkElifExpr, nkElseExpr, nkLambda,
nkAccQuoted, nkHeaderQuoted, nkTableConstr, nkQualified,
nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkConv,
nkCast, nkAddr, nkHiddenAddr, nkHiddenDeref,
nkObjDownConv, nkObjUpConv, nkChckRangeF, nkChckRange64,
nkChckRange, nkStringToCString, nkCStringToString, nkPassAsOpenArray,
nkAsgn, nkFastAsgn, nkDefaultTypeParam, nkGenericParams,
nkFormalParams, nkOfInherit, nkModule, nkProcDef,
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, nkImportAs, nkIncludeStmt, nkAccessStmt,
nkCommentStmt, nkStmtListExpr, nkBlockExpr, nkStmtListType,
nkBlockType, nkVm, nkTypeOfExpr, nkObjectTy,
nkTupleTy, nkRecList, nkRecCase, nkRecWhen,
nkRefTy, nkPtrTy, nkVarTy, nkProcTy,
nkEnumTy, nkEnumFieldDef, nkReturnToken);
TNodeKinds = set of TNodeKind;
const
TypeFlagToStr: array [TTypeFlag] of string = (
'tfVarargs', 'tfFinal', 'tfAcyclic', 'tfEnumHasWholes');
NodeKindToStr: array [TNodeKind] of string = (
'nkNone', 'nkEmpty', 'nkIdent', 'nkSym',
'nkType', 'nkCharLit', 'nkIntLit', 'nkInt8Lit',
'nkInt16Lit', 'nkInt32Lit', 'nkInt64Lit', 'nkFloatLit',
'nkFloat32Lit', 'nkFloat64Lit', 'nkStrLit', 'nkRStrLit',
'nkTripleStrLit', 'nkMetaNode', 'nkNilLit', 'nkDotCall',
'nkCommand', 'nkCall', 'nkGenericCall', 'nkExplicitTypeListCall',
'nkExprEqExpr', 'nkExprColonExpr', 'nkIdentDefs', 'nkVarTuple',
'nkInfix', 'nkPrefix', 'nkPostfix', 'nkPar',
'nkCurly', 'nkBracket', 'nkBracketExpr', 'nkPragmaExpr',
'nkRange', 'nkDotExpr', 'nkCheckedFieldExpr', 'nkDerefExpr',
'nkIfExpr', 'nkElifExpr', 'nkElseExpr', 'nkLambda',
'nkAccQuoted', 'nkHeaderQuoted', 'nkTableConstr', 'nkQualified',
'nkHiddenStdConv', 'nkHiddenSubConv', 'nkHiddenCallConv', 'nkConv',
'nkCast', 'nkAddr', 'nkHiddenAddr', 'nkHiddenDeref',
'nkObjDownConv', 'nkObjUpConv', 'nkChckRangeF', 'nkChckRange64',
'nkChckRange', 'nkStringToCString', 'nkCStringToString', 'nkPassAsOpenArray',
'nkAsgn', 'nkFastAsgn', 'nkDefaultTypeParam', 'nkGenericParams',
'nkFormalParams', 'nkOfInherit', 'nkModule', 'nkProcDef',
'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', 'nkImportAs', 'nkIncludeStmt', 'nkAccessStmt',
'nkCommentStmt', 'nkStmtListExpr', 'nkBlockExpr', 'nkStmtListType',
'nkBlockType', 'nkVm', 'nkTypeOfExpr', 'nkObjectTy',
'nkTupleTy', 'nkRecList', 'nkRecCase', 'nkRecWhen',
'nkRefTy', 'nkPtrTy', 'nkVarTy', 'nkProcTy',
'nkEnumTy', 'nkEnumFieldDef', 'nkReturnToken');
type
TSymFlag = (
sfUsed, sfStar, sfMinus, sfInInterface,
sfFromGeneric, sfGlobal, sfForward, sfImportc,
sfExportc, sfVolatile, sfRegister, sfPure,
sfResult, sfNoSideEffect, sfMainModule, sfSystemModule,
sfNoReturn, sfAddrTaken, sfCompilerProc, sfCppMethod,
sfDiscriminant, sfDeprecated, sfInClosure, sfTypeCheck,
sfCompileTime, sfThreadVar, sfMerge, sfDeadCodeElim);
TSymFlags = set of TSymFlag;
const
SymFlagToStr: array [TSymFlag] of string = (
'sfUsed', 'sfStar', 'sfMinus', 'sfInInterface',
'sfFromGeneric', 'sfGlobal', 'sfForward', 'sfImportc',
'sfExportc', 'sfVolatile', 'sfRegister', 'sfPure',
'sfResult', 'sfNoSideEffect', 'sfMainModule', 'sfSystemModule',
'sfNoReturn', 'sfAddrTaken', 'sfCompilerProc', 'sfCppMethod',
'sfDiscriminant', 'sfDeprecated', 'sfInClosure', 'sfTypeCheck',
'sfCompileTime', 'sfThreadVar', 'sfMerge', 'sfDeadCodeElim');
type
TTypeKind = (
tyNone, tyBool, tyChar, tyEmpty,
@ -95,25 +176,6 @@ const
'tyCString', 'tyForward', 'tyInt', 'tyInt8',
'tyInt16', 'tyInt32', 'tyInt64', 'tyFloat',
'tyFloat32', 'tyFloat64', 'tyFloat128');
type
TSymFlag = (
sfUsed, sfStar, sfMinus, sfInInterface,
sfFromGeneric, sfGlobal, sfForward, sfImportc,
sfExportc, sfVolatile, sfRegister, sfPure,
sfResult, sfNoSideEffect, sfMainModule, sfSystemModule,
sfNoReturn, sfAddrTaken, sfCompilerProc, sfCppMethod,
sfDiscriminant, sfDeprecated, sfInClosure, sfTypeCheck,
sfCompileTime, sfThreadVar, sfMerge);
TSymFlags = set of TSymFlag;
const
SymFlagToStr: array [TSymFlag] of string = (
'sfUsed', 'sfStar', 'sfMinus', 'sfInInterface',
'sfFromGeneric', 'sfGlobal', 'sfForward', 'sfImportc',
'sfExportc', 'sfVolatile', 'sfRegister', 'sfPure',
'sfResult', 'sfNoSideEffect', 'sfMainModule', 'sfSystemModule',
'sfNoReturn', 'sfAddrTaken', 'sfCompilerProc', 'sfCppMethod',
'sfDiscriminant', 'sfDeprecated', 'sfInClosure', 'sfTypeCheck',
'sfCompileTime', 'sfThreadVar', 'sfMerge');
type
TNodeFlag = (
nfNone, nfBase2, nfBase8, nfBase16,
@ -123,6 +185,13 @@ const
NodeFlagToStr: array [TNodeFlag] of string = (
'nfNone', 'nfBase2', 'nfBase8', 'nfBase16',
'nfAllConst', 'nfTransf', 'nfSem');
type
TTypeFlag = (
tfVarargs, tfFinal, tfAcyclic, tfEnumHasWholes);
TTypeFlags = set of TTypeFlag;
const
TypeFlagToStr: array [TTypeFlag] of string = (
'tfVarargs', 'tfFinal', 'tfAcyclic', 'tfEnumHasWholes');
type
TSymKind = (
skUnknownSym, skConditional, skDynLib, skParam,
@ -138,75 +207,6 @@ const
'skVar', 'skProc', 'skIterator', 'skConverter',
'skMacro', 'skTemplate', 'skField', 'skEnumField',
'skForVar', 'skModule', 'skLabel', 'skStub');
type
TNodeKind = (
nkNone, nkEmpty, nkIdent, nkSym,
nkType, nkCharLit, nkIntLit, nkInt8Lit,
nkInt16Lit, nkInt32Lit, nkInt64Lit, nkFloatLit,
nkFloat32Lit, nkFloat64Lit, nkStrLit, nkRStrLit,
nkTripleStrLit, nkMetaNode, nkNilLit, nkDotCall,
nkCommand, nkCall, nkGenericCall, nkExplicitTypeListCall,
nkExprEqExpr, nkExprColonExpr, nkIdentDefs, nkInfix,
nkPrefix, nkPostfix, nkPar, nkCurly,
nkBracket, nkBracketExpr, nkPragmaExpr, nkRange,
nkDotExpr, nkCheckedFieldExpr, nkDerefExpr, nkIfExpr,
nkElifExpr, nkElseExpr, nkLambda, nkAccQuoted,
nkHeaderQuoted, nkTableConstr, nkQualified, nkHiddenStdConv,
nkHiddenSubConv, nkHiddenCallConv, nkConv, nkCast,
nkAddr, nkHiddenAddr, nkHiddenDeref, nkObjDownConv,
nkObjUpConv, nkChckRangeF, nkChckRange64, nkChckRange,
nkStringToCString, nkCStringToString, nkPassAsOpenArray, nkAsgn,
nkDefaultTypeParam, nkGenericParams, nkFormalParams, nkOfInherit,
nkModule, nkProcDef, 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, nkImportAs,
nkIncludeStmt, nkAccessStmt, nkCommentStmt, nkStmtListExpr,
nkBlockExpr, nkStmtListType, nkBlockType, nkVm,
nkTypeOfExpr, nkObjectTy, nkTupleTy, nkRecList,
nkRecCase, nkRecWhen, nkRefTy, nkPtrTy,
nkVarTy, nkProcTy, nkEnumTy, nkEnumFieldDef,
nkReturnToken);
TNodeKinds = set of TNodeKind;
const
NodeKindToStr: array [TNodeKind] of string = (
'nkNone', 'nkEmpty', 'nkIdent', 'nkSym',
'nkType', 'nkCharLit', 'nkIntLit', 'nkInt8Lit',
'nkInt16Lit', 'nkInt32Lit', 'nkInt64Lit', 'nkFloatLit',
'nkFloat32Lit', 'nkFloat64Lit', 'nkStrLit', 'nkRStrLit',
'nkTripleStrLit', 'nkMetaNode', 'nkNilLit', 'nkDotCall',
'nkCommand', 'nkCall', 'nkGenericCall', 'nkExplicitTypeListCall',
'nkExprEqExpr', 'nkExprColonExpr', 'nkIdentDefs', 'nkInfix',
'nkPrefix', 'nkPostfix', 'nkPar', 'nkCurly',
'nkBracket', 'nkBracketExpr', 'nkPragmaExpr', 'nkRange',
'nkDotExpr', 'nkCheckedFieldExpr', 'nkDerefExpr', 'nkIfExpr',
'nkElifExpr', 'nkElseExpr', 'nkLambda', 'nkAccQuoted',
'nkHeaderQuoted', 'nkTableConstr', 'nkQualified', 'nkHiddenStdConv',
'nkHiddenSubConv', 'nkHiddenCallConv', 'nkConv', 'nkCast',
'nkAddr', 'nkHiddenAddr', 'nkHiddenDeref', 'nkObjDownConv',
'nkObjUpConv', 'nkChckRangeF', 'nkChckRange64', 'nkChckRange',
'nkStringToCString', 'nkCStringToString', 'nkPassAsOpenArray', 'nkAsgn',
'nkDefaultTypeParam', 'nkGenericParams', 'nkFormalParams', 'nkOfInherit',
'nkModule', 'nkProcDef', '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', 'nkImportAs',
'nkIncludeStmt', 'nkAccessStmt', 'nkCommentStmt', 'nkStmtListExpr',
'nkBlockExpr', 'nkStmtListType', 'nkBlockType', 'nkVm',
'nkTypeOfExpr', 'nkObjectTy', 'nkTupleTy', 'nkRecList',
'nkRecCase', 'nkRecWhen', 'nkRefTy', 'nkPtrTy',
'nkVarTy', 'nkProcTy', 'nkEnumTy', 'nkEnumFieldDef',
'nkReturnToken');
{[[[end]]]}
type
@ -240,22 +240,23 @@ type
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16,
mToU32, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr,
mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr,
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet,
mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet,
mConStrStr, mConArrArr, mConArrT, mConTArr, mConTT, mSlice,
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq, mInRange, mInSet,
mAsgn, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mAssert,
mSwap, mIsNil, mArrToSeq, mArray, mOpenArray, mRange,
mSet, mSeq, mInt, mInt8, mInt16, mInt32,
mInt64, mFloat, mFloat32, mFloat64, mBool, mChar,
mString, mCstring, mPointer, mAnyEnum, mEmptySet, mIntSetBaseType,
mNil, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor,
mNimrodMinor, mNimrodPatch, mCpuEndian, mNaN, mInf, mNegInf,
mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel,
mNKind, mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType,
mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType,
mNSetStrVal, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
mEqIdent, mNHint, mNWarning, mNError
mEnumToStr, mAnd, mOr, mEqStr, mLeStr, mLtStr,
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mConTArr, mConTT,
mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq, mInRange,
mInSet, mAsgn, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
mAssert, mSwap, mIsNil, mArrToSeq, mArray, mOpenArray,
mRange, mSet, mSeq, mInt, mInt8, mInt16,
mInt32, mInt64, mFloat, mFloat32, mFloat64, mBool,
mChar, mString, mCstring, mPointer, mAnyEnum, mEmptySet,
mIntSetBaseType, mNil, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion,
mNimrodMajor, mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU,
mNaN, mInf, mNegInf, mNLen, mNChild, mNSetChild,
mNAdd, mNAddMultiple, mNDel, mNKind, mNIntVal, mNFloatVal,
mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal,
mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNNewNimNode, mNCopyNimNode,
mNCopyNimTree, mStrToIdent, mIdentToStr, mEqIdent, mNHint, mNWarning,
mNError
//[[[end]]]
);
@ -322,7 +323,6 @@ type
locProc, // location is a proc (an address of a procedure)
locData, // location is a constant
locCall, // location is a call expression
locImmediate, // location is an immediate value
locOther // location is something other
);
@ -498,22 +498,23 @@ const // "MagicToStr" array:
'Ze16ToI', 'Ze16ToI64', 'Ze32ToI64', 'ZeIToI64', 'ToU8', 'ToU16',
'ToU32', 'ToFloat', 'ToBiggestFloat', 'ToInt', 'ToBiggestInt', 'CharToStr',
'BoolToStr', 'IntToStr', 'Int64ToStr', 'FloatToStr', 'CStrToStr', 'StrToStr',
'And', 'Or', 'EqStr', 'LeStr', 'LtStr', 'EqSet',
'LeSet', 'LtSet', 'MulSet', 'PlusSet', 'MinusSet', 'SymDiffSet',
'ConStrStr', 'ConArrArr', 'ConArrT', 'ConTArr', 'ConTT', 'Slice',
'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'AppendSeqSeq', 'InRange', 'InSet',
'Asgn', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq', 'Assert',
'Swap', 'IsNil', 'ArrToSeq', 'Array', 'OpenArray', 'Range',
'Set', 'Seq', 'Int', 'Int8', 'Int16', 'Int32',
'Int64', 'Float', 'Float32', 'Float64', 'Bool', 'Char',
'String', 'Cstring', 'Pointer', 'AnyEnum', 'EmptySet', 'IntSetBaseType',
'Nil', 'IsMainModule', 'CompileDate', 'CompileTime', 'NimrodVersion', 'NimrodMajor',
'NimrodMinor', 'NimrodPatch', 'CpuEndian', 'NaN', 'Inf', 'NegInf',
'NLen', 'NChild', 'NSetChild', 'NAdd', 'NAddMultiple', 'NDel',
'NKind', 'NIntVal', 'NFloatVal', 'NSymbol', 'NIdent', 'NGetType',
'NStrVal', 'NSetIntVal', 'NSetFloatVal', 'NSetSymbol', 'NSetIdent', 'NSetType',
'NSetStrVal', 'NNewNimNode', 'NCopyNimNode', 'NCopyNimTree', 'StrToIdent', 'IdentToStr',
'EqIdent', 'NHint', 'NWarning', 'NError'
'EnumToStr', 'And', 'Or', 'EqStr', 'LeStr', 'LtStr',
'EqSet', 'LeSet', 'LtSet', 'MulSet', 'PlusSet', 'MinusSet',
'SymDiffSet', 'ConStrStr', 'ConArrArr', 'ConArrT', 'ConTArr', 'ConTT',
'Slice', 'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'AppendSeqSeq', 'InRange',
'InSet', 'Asgn', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq',
'Assert', 'Swap', 'IsNil', 'ArrToSeq', 'Array', 'OpenArray',
'Range', 'Set', 'Seq', 'Int', 'Int8', 'Int16',
'Int32', 'Int64', 'Float', 'Float32', 'Float64', 'Bool',
'Char', 'String', 'Cstring', 'Pointer', 'AnyEnum', 'EmptySet',
'IntSetBaseType', 'Nil', 'IsMainModule', 'CompileDate', 'CompileTime', 'NimrodVersion',
'NimrodMajor', 'NimrodMinor', 'NimrodPatch', 'CpuEndian', 'HostOS', 'HostCPU',
'NaN', 'Inf', 'NegInf', 'NLen', 'NChild', 'NSetChild',
'NAdd', 'NAddMultiple', 'NDel', 'NKind', 'NIntVal', 'NFloatVal',
'NSymbol', 'NIdent', 'NGetType', 'NStrVal', 'NSetIntVal', 'NSetFloatVal',
'NSetSymbol', 'NSetIdent', 'NSetType', 'NSetStrVal', 'NNewNimNode', 'NCopyNimNode',
'NCopyNimTree', 'StrToIdent', 'IdentToStr', 'EqIdent', 'NHint', 'NWarning',
'NError'
//[[[end]]]
);
@ -1342,13 +1343,13 @@ end;
function IntSetContains(const s: TIntSet; key: int): bool;
var
u: int;
u: TBitScalar;
t: PTrunk;
begin
t := IntSetGet(s, key shr TrunkShift);
t := IntSetGet(s, shru(key, TrunkShift));
if t <> nil then begin
u := key and TrunkMask;
result := (t.bits[u shr IntShift] and (1 shl (u and IntMask))) <> 0
result := (t.bits[shru(u, IntShift)] and shlu(1, u and IntMask)) <> 0
end
else
result := false
@ -1356,27 +1357,27 @@ end;
procedure IntSetIncl(var s: TIntSet; key: int);
var
u: int;
u: TBitScalar;
t: PTrunk;
begin
t := IntSetPut(s, key shr TrunkShift);
t := IntSetPut(s, shru(key, TrunkShift));
u := key and TrunkMask;
t.bits[u shr IntShift] := t.bits[u shr IntShift]
or (1 shl (u and IntMask));
t.bits[shru(u, IntShift)] := t.bits[shru(u, IntShift)]
or shlu(1, u and IntMask);
end;
function IntSetContainsOrIncl(var s: TIntSet; key: int): bool;
var
u: int;
u: TBitScalar;
t: PTrunk;
begin
t := IntSetGet(s, key shr TrunkShift);
t := IntSetGet(s, shru(key, TrunkShift));
if t <> nil then begin
u := key and TrunkMask;
result := (t.bits[u shr IntShift] and (1 shl (u and IntMask))) <> 0;
result := (t.bits[shru(u, IntShift)] and shlu(1, u and IntMask)) <> 0;
if not result then
t.bits[u shr IntShift] := t.bits[u shr IntShift]
or (1 shl (u and IntMask));
t.bits[shru(u, IntShift)] := t.bits[shru(u, IntShift)]
or shlu(1, u and IntMask);
end
else begin
IntSetIncl(s, key);

View file

@ -450,29 +450,27 @@ begin
else begin
istr := spaces(indent+2);
result := ropef('{$n$1"kind": $2',
[istr, makeYamlString(nodeKindToStr[n.kind])]);
[istr, makeYamlString(nodeKindToStr[n.kind])]);
if maxRecDepth <> 0 then begin
appf(result, ',$n$1"info": $2',
[istr, lineInfoToStr(n.info)]);
case n.kind of
nkCharLit..nkInt64Lit:
appf(result, '$n$1"intVal": $2', [istr, toRope(n.intVal)]);
appf(result, ',$n$1"intVal": $2', [istr, toRope(n.intVal)]);
nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, '$n$1"floatVal": $2',
[istr, toRopeF(n.floatVal)]);
appf(result, ',$n$1"floatVal": $2', [istr, toRopeF(n.floatVal)]);
nkStrLit..nkTripleStrLit:
appf(result, '$n$1"strVal": $2',
[istr, makeYamlString(n.strVal)]);
appf(result, ',$n$1"strVal": $2', [istr, makeYamlString(n.strVal)]);
nkSym:
appf(result, ',$n$1"sym": $2',
[istr, symToYamlAux(n.sym, marker, indent+2, maxRecDepth)]);
nkIdent: begin
if n.ident <> nil then
appf(result, '$n$1"ident": $2',
appf(result, ',$n$1"ident": $2',
[istr, makeYamlString(n.ident.s)])
else
appf(result, '$n$1"ident": null', [istr])
appf(result, ',$n$1"ident": null', [istr])
end
else begin
if sonsLen(n) > 0 then begin
@ -552,12 +550,12 @@ begin
if maxRecDepth <> 0 then begin
case n.kind of
nkCharLit..nkInt64Lit:
appf(result, '$n$1"intVal": $2', [istr, toRope(n.intVal)]);
appf(result, ',$n$1"intVal": $2', [istr, toRope(n.intVal)]);
nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, '$n$1"floatVal": $2',
appf(result, ',$n$1"floatVal": $2',
[istr, toRopeF(n.floatVal)]);
nkStrLit..nkTripleStrLit:
appf(result, '$n$1"strVal": $2',
appf(result, ',$n$1"strVal": $2',
[istr, makeYamlString(n.strVal)]);
nkSym:
appf(result, ',$n$1"sym": $2_$3',
@ -565,10 +563,10 @@ begin
nkIdent: begin
if n.ident <> nil then
appf(result, '$n$1"ident": $2',
appf(result, ',$n$1"ident": $2',
[istr, makeYamlString(n.ident.s)])
else
appf(result, '$n$1"ident": null', [istr])
appf(result, ',$n$1"ident": null', [istr])
end
else begin
if sonsLen(n) > 0 then begin

View file

@ -126,7 +126,7 @@ var
j: int;
begin
result := 0;
if CPU[hostCPU].endian = CPU[targetCPU].endian then begin
if CPU[platform.hostCPU].endian = CPU[targetCPU].endian then begin
for j := 0 to size-1 do
if j < length(s) then
result := result or shlu(Ze64(s[j]), j * 8)
@ -773,6 +773,34 @@ begin
putIntoDest(p, d, field.typ, r);
end;
procedure genTupleElem(p: BProc; e: PNode; var d: TLoc);
var
a: TLoc;
field: PSym;
ty: PType;
r: PRope;
i: int;
begin
initLocExpr(p, e.sons[0], a);
if d.k = locNone then d.s := a.s;
{@discard} getTypeDesc(p.module, a.t); // fill the record's fields.loc
ty := getUniqueType(a.t);
r := rdLoc(a);
case e.sons[1].kind of
nkIntLit..nkInt64Lit: i := int(e.sons[1].intVal);
else internalError(e.info, 'genTupleElem');
end;
if ty.n <> nil then begin
field := ty.n.sons[i].sym;
if field = nil then InternalError(e.info, 'genTupleElem');
if field.loc.r = nil then InternalError(e.info, 'genTupleElem');
appf(r, '.$1', [field.loc.r]);
end
else
appf(r, '.Field$1', [toRope(i)]);
putIntoDest(p, d, ty.sons[i], r);
end;
procedure genInExprAux(p: BProc; e: PNode; var a, b, d: TLoc); forward;
procedure genCheckedRecordField(p: BProc; e: PNode; var d: TLoc);
@ -848,10 +876,11 @@ begin
first := intLiteral(firstOrd(ty));
// emit range check:
if (optBoundsCheck in p.options) then begin
if b.k <> locImmediate then begin // semantic pass has already checked:
if not isConstExpr(e.sons[1]) then begin
// semantic pass has already checked for const index expressions
useMagic(p.module, 'raiseIndexError');
if firstOrd(ty) = 0 then begin
if lastOrd(b.t) > lastOrd(ty) then
if (firstOrd(b.t) < firstOrd(ty)) or (lastOrd(b.t) > lastOrd(ty)) then
appf(p.s[cpsStmts],
'if ((NU)($1) > (NU)($2)) raiseIndexError();$n',
[rdCharLoc(b), intLiteral(lastOrd(ty))])
@ -1289,14 +1318,22 @@ var
a, b, f: TLoc;
refType, bt: PType;
ti: PRope;
oldModule: BModule;
begin
useMagic(p.module, 'newObj');
refType := skipVarGenericRange(e.sons[1].typ);
InitLocExpr(p, e.sons[1], a);
// This is a little hack:
oldModule := p.module;
p.module := gmti;
InitLocExpr(p, e.sons[2], f);
p.module := oldModule;
initLoc(b, locExpr, a.t, OnHeap);
ti := genTypeInfo(p.module, refType);
appf(p.module.s[cfsTypeInit3], '$1->finalizer = (void*)$2;$n', [
appf(gmti.s[cfsTypeInit3], '$1->finalizer = (void*)$2;$n', [
ti, rdLoc(f)]);
b.r := ropef('($1) newObj($2, sizeof($3))',
[getTypeDesc(p.module, refType), ti,
@ -1331,7 +1368,7 @@ begin
UseMagic(p.module, 'reprChar');
putIntoDest(p, d, e.typ, ropef('reprChar($1)', [rdLoc(a)]))
end;
tyEnum: begin
tyEnum, tyAnyEnum: begin
UseMagic(p.module, 'reprEnum');
putIntoDest(p, d, e.typ,
ropef('reprEnum($1, $2)', [rdLoc(a), genTypeInfo(p.module, t)]))
@ -1853,6 +1890,7 @@ begin
mFloatToStr: genDollar(p, e, d, 'nimFloatToStr', 'nimFloatToStr($1)');
mCStrToStr: genDollar(p, e, d, 'cstrToNimstr', 'cstrToNimstr($1)');
mStrToStr: expr(p, e.sons[1], d);
mEnumToStr: genRepr(p, e, d);
mAssert: begin
if (optAssert in p.Options) then begin
useMagic(p.module, 'internalAssert');
@ -1996,23 +2034,31 @@ var
it: PNode;
t: PType;
begin
// the code generator assumes that there are only tuple constructors with
// field names!
if not handleConstExpr(p, n, d) then begin
t := getUniqueType(n.typ);
{@discard} getTypeDesc(p.module, t); // so that any fields are initialized
if d.k = locNone then getTemp(p, t, d);
if t.n = nil then InternalError(n.info, 'genTupleConstr');
if sonsLen(t.n) <> sonsLen(n) then
InternalError(n.info, 'genTupleConstr');
for i := 0 to sonsLen(n)-1 do begin
it := n.sons[i];
if it.kind <> nkExprColonExpr then InternalError(n.info, 'genTupleConstr');
initLoc(rec, locExpr, it.sons[1].typ, d.s);
if (t.n.sons[i].kind <> nkSym) then
InternalError(n.info, 'genTupleConstr');
rec.r := ropef('$1.$2', [rdLoc(d), mangleRecFieldName(t.n.sons[i].sym, t)]);
expr(p, it.sons[1], rec);
if it.kind = nkExprColonExpr then begin
initLoc(rec, locExpr, it.sons[1].typ, d.s);
if (t.n.sons[i].kind <> nkSym) then
InternalError(n.info, 'genTupleConstr');
rec.r := ropef('$1.$2', [rdLoc(d), mangleRecFieldName(t.n.sons[i].sym, t)]);
expr(p, it.sons[1], rec);
end
else if t.n = nil then begin
initLoc(rec, locExpr, it.typ, d.s);
rec.r := ropef('$1.Field$2', [rdLoc(d), toRope(i)]);
expr(p, it, rec);
end
else begin
initLoc(rec, locExpr, it.typ, d.s);
if (t.n.sons[i].kind <> nkSym) then
InternalError(n.info, 'genTupleConstr: 2');
rec.r := ropef('$1.$2', [rdLoc(d), mangleRecFieldName(t.n.sons[i].sym, t)]);
expr(p, it, rec);
end
end
end
end;
@ -2123,11 +2169,10 @@ begin
sym := e.sym;
case sym.Kind of
skProc, skConverter: begin
// generate prototype if not already declared in this translation unit
genProcPrototype(p.module, sym);
genProc(p.module, sym);
if ((sym.loc.r = nil) or (sym.loc.t = nil)) then
InternalError(e.info, 'expr: proc not init ' + sym.name.s);
putLocIntoDest(p, d, sym.loc)
putLocIntoDest(p, d, sym.loc);
end;
skConst:
if isSimpleConst(sym.typ) then
@ -2160,8 +2205,6 @@ begin
nkStrLit..nkTripleStrLit, nkIntLit..nkInt64Lit,
nkFloatLit..nkFloat64Lit, nkNilLit, nkCharLit: begin
putIntoDest(p, d, e.typ, genLiteral(p, e));
if d.k in [locNone, locExpr] then
d.k := locImmediate // for removal of index checks
end;
nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand: begin
if (e.sons[0].kind = nkSym) and
@ -2189,6 +2232,7 @@ begin
tyOpenArray: genOpenArrayElem(p, e, d);
tySequence, tyString: genSeqElem(p, e, d);
tyCString: genCStringElem(p, e, d);
tyTuple: genTupleElem(p, e, d);
else InternalError(e.info,
'expr(nkBracketExpr, ' + typeKindToStr[ty.kind] + ')');
end

View file

@ -801,9 +801,16 @@ begin
assert(key.kind = nkIdent);
case whichKeyword(key.ident) of
wBreakpoint: genBreakPoint(p, it);
wDeadCodeElim: begin
if not (optDeadCodeElim in gGlobalOptions) then begin
// we need to keep track of ``deadCodeElim`` pragma
if (sfDeadCodeElim in p.module.module.flags) then
addPendingModule(p.module)
end
end
else begin end
end
end
end;
end;
procedure genAsgn(p: BProc; e: PNode);
@ -816,6 +823,17 @@ begin
expr(p, e.sons[1], a);
end;
procedure genFastAsgn(p: BProc; e: PNode);
var
a: TLoc;
begin
genLineDir(p, e); // BUGFIX
InitLocExpr(p, e.sons[0], a);
include(a.flags, lfNoDeepCopy);
assert(a.t <> nil);
expr(p, e.sons[1], a);
end;
procedure genStmts(p: BProc; t: PNode);
var
a: TLoc;
@ -844,6 +862,7 @@ begin
initLocExpr(p, t, a);
end;
nkAsgn: genAsgn(p, t);
nkFastAsgn: genFastAsgn(p, t);
nkDiscardStmt: begin
genLineDir(p, t);
initLocExpr(p, t.sons[0], a);
@ -865,17 +884,13 @@ begin
nkProcDef, nkConverterDef: begin
if (t.sons[genericParamsPos] = nil) then begin
prc := t.sons[namePos].sym;
if (t.sons[codePos] <> nil)
or (lfDynamicLib in prc.loc.flags) then begin // BUGFIX
if IntSetContainsOrIncl(p.module.debugDeclared, prc.id) then begin
internalError(t.info, 'genStmts(): ' + toString(prc.id));
// XXX: remove this check!
end;
//if IntSetContains(p.module.debugDeclared, 2642) then
// InternalError(t.info, 'this sucks ' + toString(prc.id));
genProc(p.module, prc)
if not (optDeadCodeElim in gGlobalOptions) and
not (sfDeadCodeElim in getModule(prc).flags)
or ([sfExportc, sfCompilerProc] * prc.flags = [sfExportc]) then begin
if (t.sons[codePos] <> nil) or (lfDynamicLib in prc.loc.flags) then begin
genProc(p.module, prc)
end
end
//else if sfCompilerProc in prc.flags then genProcPrototype(prc);
end
end;
else

View file

@ -8,7 +8,7 @@
//
//var
// newDummyVar: int; // just to check the rodgen mechanism
// newDummyVar: int; // just to check the symbol file mechanism
// ------------------------- Name Mangling --------------------------------
@ -461,6 +461,22 @@ begin
app(result, '};' + tnl);
end;
function getTupleDesc(m: BModule; typ: PType; name: PRope;
var check: TIntSet): PRope;
var
desc: PRope;
i: int;
begin
result := ropef('struct $1 {$n', [name]);
desc := nil;
for i := 0 to sonsLen(typ)-1 do
appf(desc, '$1 Field$2;$n',
[getTypeDescAux(m, typ.sons[i], check), toRope(i)]);
if (desc = nil) then app(result, 'char dummy;' + tnl)
else app(result, desc);
app(result, '};' + tnl);
end;
procedure pushType(m: BModule; typ: PType);
var
L: int;
@ -571,13 +587,15 @@ begin
if result = nil then begin
result := getTypeName(t);
if not isImportedType(t) then
appf(m.s[cfsForwardTypes],
getForwardStructFormat(), [result]);
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]);
IdTablePut(m.forwTypeCache, t, result)
end;
IdTablePut(m.typeCache, t, result);
// always call for sideeffects:
recdesc := getRecordDesc(m, t, result, check);
if t.n <> nil then
recdesc := getRecordDesc(m, t, result, check)
else
recdesc := getTupleDesc(m, t, result, check);
if not isImportedType(t) then app(m.s[cfsTypes], recdesc);
end;
tySet: begin
@ -735,9 +753,11 @@ begin
assert(n.sons[0].kind = nkSym);
field := n.sons[0].sym;
tmp := getTempName();
useMagic(m, 'chckNil');
appf(m.s[cfsTypeInit3], '$1.kind = 3;$n' +
'$1.offset = offsetof($2, $3);$n' +
'$1.typ = $4;$n' +
'chckNil($1.typ);$n' +
'$1.name = $5;$n' +
'$1.sons = &$6[0];$n' +
'$1.len = $7;$n',
@ -746,7 +766,7 @@ begin
makeCString(field.name.s), tmp,
toRope(lengthOrd(field.typ))]);
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n',
[tmp, toRope(lengthOrd(field.typ)+1)]);
[tmp, toRope(lengthOrd(field.typ)+1)]);
for i := 1 to len-1 do begin
b := n.sons[i]; // branch
tmp2 := getNimNode(m);
@ -761,18 +781,18 @@ begin
y := int(getOrdValue(b.sons[j].sons[1]));
while x <= y do begin
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n',
[tmp, toRope(x), tmp2]);
[tmp, toRope(x), tmp2]);
inc(x);
end;
end
else
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n',
[tmp, toRope(getOrdValue(b.sons[j])), tmp2])
[tmp, toRope(getOrdValue(b.sons[j])), tmp2])
end
end;
nkElse: begin
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n',
[tmp, toRope(lengthOrd(field.typ)), tmp2]);
[tmp, toRope(lengthOrd(field.typ)), tmp2]);
end
else
internalError(n.info, 'genObjectFields(nkRecCase)');
@ -781,9 +801,11 @@ begin
end;
nkSym: begin
field := n.sym;
useMagic(m, 'chckNil');
appf(m.s[cfsTypeInit3], '$1.kind = 1;$n' +
'$1.offset = offsetof($2, $3);$n' +
'$1.typ = $4;$n' +
'chckNil($1.typ);$n' +
'$1.name = $5;$n',
[expr, getTypeDesc(m, typ), field.loc.r,
genTypeInfo(m, field.typ),
@ -804,6 +826,41 @@ begin
appf(m.s[cfsTypeInit3], '$1->node = &$2;$n', [name, tmp]);
end;
procedure genTupleInfo(m: BModule; typ: PType; name: PRope);
var
tmp, expr, tmp2: PRope;
i, len: int;
a: PType;
begin
genTypeInfoAuxBase(m, typ, name, toRope('0'+''));
expr := getNimNode(m);
len := sonsLen(typ);
if len > 0 then begin
tmp := getTempName();
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n', [tmp, toRope(len)]);
for i := 0 to len-1 do begin
a := typ.sons[i];
tmp2 := getNimNode(m);
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n', [tmp, toRope(i), tmp2]);
useMagic(m, 'chckNil');
appf(m.s[cfsTypeInit3], '$1.kind = 1;$n' +
'$1.offset = offsetof($2, Field$3);$n' +
'$1.typ = $4;$n' +
'chckNil($1.typ);$n' +
'$1.name = "Field$3";$n',
[tmp2, getTypeDesc(m, typ), toRope(i),
genTypeInfo(m, a)]);
end;
appf(m.s[cfsTypeInit3],
'$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n', [
expr, toRope(len), tmp]);
end
else
appf(m.s[cfsTypeInit3],
'$1.len = $2; $1.kind = 2;$n', [expr, toRope(len)]);
appf(m.s[cfsTypeInit3], '$1->node = &$2;$n', [name, tmp]);
end;
procedure genEnumInfo(m: BModule; typ: PType; name: PRope);
var
nodePtrs, elemNode, enumNames, enumArray, counter, specialCases: PRope;
@ -817,7 +874,8 @@ begin
genTypeInfoAux(m, typ, name);
nodePtrs := getTempName();
len := sonsLen(typ.n);
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n', [nodePtrs, toRope(len)]);
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n',
[nodePtrs, toRope(len)]);
enumNames := nil;
specialCases := nil;
firstNimNode := m.typeNodes;
@ -879,83 +937,79 @@ function genTypeInfo(m: BModule; typ: PType): PRope;
var
t: PType;
id: int;
dataGen: bool;
dataGenerated: bool;
begin
t := getUniqueType(typ);
id := IiTableGet(gToTypeInfoId, t.id);
if id = invalidKey then begin
dataGen := false;
case t.kind of
tyEnum, tyBool: begin
id := t.id;
dataGen := true
end;
tyObject: begin
if isPureObject(t) then
id := getID()
else begin
id := t.id;
dataGen := true
end
end
else
id := getID();
end;
dataGenerated := false;
id := t.id; // getID();
IiTablePut(gToTypeInfoId, t.id, id);
end
else
dataGen := true;
dataGenerated := true;
result := ropef('NTI$1', [toRope(id)]);
if not IntSetContainsOrIncl(m.typeInfoMarker, t.id) then begin
if not IntSetContainsOrIncl(m.typeInfoMarker, id) then begin
// declare type information structures:
useMagic(m, 'TNimType');
useMagic(m, 'TNimNode');
if dataGen then
appf(m.s[cfsVars], 'extern TNimType* $1; /* $2 */$n',
[result, toRope(typeToString(t))]);
appf(m.s[cfsVars], 'extern TNimType* $1; /* $2 */$n',
[result, toRope(typeToString(t))]);
end;
if dataGen then exit;
if dataGenerated then exit;
case t.kind of
tyPointer, tyProc, tyBool, tyChar, tyCString, tyString,
tyInt..tyFloat128, tyVar:
genTypeInfoAuxBase(m, t, result, toRope('0'+''));
tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(m, t, result);
tyArrayConstr, tyArray: genArrayInfo(m, t, result);
tySet: genSetInfo(m, t, result);
tyEnum: genEnumInfo(m, t, result);
tyObject, tyTuple: genObjectInfo(m, t, result);
genTypeInfoAuxBase(gmti, t, result, toRope('0'+''));
tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(gmti, t, result);
tyArrayConstr, tyArray: genArrayInfo(gmti, t, result);
tySet: genSetInfo(gmti, t, result);
tyEnum: genEnumInfo(gmti, t, result);
tyObject: genObjectInfo(gmti, t, result);
tyTuple: begin
if t.n <> nil then genObjectInfo(gmti, t, result)
else genTupleInfo(gmti, t, result);
end;
else InternalError('genTypeInfo(' + typekindToStr[t.kind] + ')');
end
end;
procedure genTypeSection(m: BModule; n: PNode);
begin
end;
(*
procedure genTypeSection(m: BModule; n: PNode);
var
i: int;
a: PNode;
t: PType;
begin
for i := 0 to sonsLen(n)-1 do begin
a := n.sons[i];
if a.kind = nkCommentStmt then continue;
if (a.sons[0].kind <> nkSym) then InternalError(a.info, 'genTypeSection');
t := a.sons[0].sym.typ;
if (a.sons[2] = nil)
or not (a.sons[2].kind in [nkSym, nkIdent, nkAccQuoted]) then
if t <> nil then
case t.kind of
tyEnum, tyBool: begin
useMagic(m, 'TNimType');
useMagic(m, 'TNimNode');
genEnumInfo(m, t, ropef('NTI$1', [toRope(t.id)]));
end;
tyObject: begin
if not isPureObject(t) then begin
if not (optDeadCodeElim in gGlobalOptions) then begin
for i := 0 to sonsLen(n)-1 do begin
a := n.sons[i];
if a.kind = nkCommentStmt then continue;
if (a.sons[0].kind <> nkSym) then InternalError(a.info, 'genTypeSection');
t := a.sons[0].sym.typ;
if (a.sons[2] = nil)
or not (a.sons[2].kind in [nkSym, nkIdent, nkAccQuoted]) then
if t <> nil then
case t.kind of
tyEnum, tyBool: begin
useMagic(m, 'TNimType');
useMagic(m, 'TNimNode');
genObjectInfo(m, t, ropef('NTI$1', [toRope(t.id)]));
genEnumInfo(m, t, ropef('NTI$1', [toRope(t.id)]));
end;
tyObject: begin
if not isPureObject(t) then begin
useMagic(m, 'TNimType');
useMagic(m, 'TNimNode');
genObjectInfo(m, t, ropef('NTI$1', [toRope(t.id)]));
end
end
else begin end
end
else begin end
end
end
end
end;
*)

View file

@ -27,16 +27,48 @@ function GetUniqueType(key: PType): PType;
implementation
var
gTypeTable: TIdTable;
gTypeTable: array [TTypeKind] of TIdTable;
procedure initTypeTables();
var
i: TTypeKind;
begin
for i := low(TTypeKind) to high(TTypeKind) do
InitIdTable(gTypeTable[i]);
end;
function GetUniqueType(key: PType): PType;
var
t: PType;
h: THash;
k: TTypeKind;
begin
// this was a hotspot in the compiler!
// this is a hotspot in the compiler!
result := key;
if key = nil then exit;
k := key.kind;
case k of
tyObject, tyEnum: begin
result := PType(IdTableGet(gTypeTable[k], key));
if result = nil then begin
IdTablePut(gTypeTable[k], key, key);
result := key;
end
end;
tyGenericInst: result := GetUniqueType(lastSon(key));
tyProc: begin end;
else begin
// we have to do a slow linear search because types may need
// to be compared by their structure:
if IdTableHasObjectAsKey(gTypeTable[k], key) then exit;
for h := 0 to high(gTypeTable[k].data) do begin
t := PType(gTypeTable[k].data[h].key);
if (t <> nil) and sameType(t, key) then begin result := t; exit end
end;
IdTablePut(gTypeTable[k], key, key);
end;
end;
(*
case key.Kind of
tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
tyInt..tyFloat128, tyProc, tyAnyEnum: begin end;
@ -62,7 +94,7 @@ begin
end
end;
tyGenericInst: result := GetUniqueType(lastSon(key));
end;
end; *)
end;
function TableGetType(const tab: TIdTable; key: PType): PObject;
@ -122,6 +154,6 @@ begin
app(result, toRope(res));
end;
initialization
InitIdTable(gTypeTable);
begin
InitTypeTables();
end.

View file

@ -73,7 +73,6 @@ type
cpsStmts // section of local statements for C proc
);
TCProcSections = array [TCProcSection] of PRope;
// TCProcSections represents a generated C proc
@ -112,21 +111,64 @@ type
typeCache: TIdTable; // cache the generated types
forwTypeCache: TIdTable; // cache for forward declarations of types
declaredThings: TIntSet; // things we have declared in this .c file
debugDeclared: TIntSet; // for debugging purposes
declaredProtos: TIntSet; // prototypes we have declared in this .c file
headerFiles: TLinkedList; // needed headers to include
typeInfoMarker: TIntSet; // needed for generating type information
initProc: BProc; // code for init procedure
typeStack: TTypeSeq; // used for type generation
dataCache: TNodeTable;
forwardedProcs: TSymSeq; // keep forwarded procs here
typeNodes, nimTypes: int;// used for type info generation
typeNodesName, nimTypesName: PRope; // used for type info generation
typeNodesName, nimTypesName: PRope; // used for type info generation
end;
var
mainModProcs, mainModInit: PRope; // parts of the main module
gMapping: PRope; // the generated mapping file (if requested)
gProcProfile: Natural; // proc profile counter
gGeneratedSyms: TIntSet; // set of ID's of generated symbols
gPendingModules: array of BModule = {@ignore} nil {@emit @[]};
// list of modules that are not finished with code generation
gForwardedProcsCounter: int = 0;
gmti: BModule; // generated type info: no need to initialize: defaults fit
procedure addForwardedProc(m: BModule; prc: PSym);
var
L: int;
begin
L := length(m.forwardedProcs);
setLength(m.forwardedProcs, L+1);
m.forwardedProcs[L] := prc;
inc(gForwardedProcsCounter);
end;
procedure addPendingModule(m: BModule);
var
L, i: int;
begin
for i := 0 to high(gPendingModules) do
if gPendingModules[i] = m then
InternalError('module already pending: ' + m.module.name.s);
L := length(gPendingModules);
setLength(gPendingModules, L+1);
gPendingModules[L] := m;
end;
function findPendingModule(m: BModule; s: PSym): BModule;
var
ms: PSym;
i: int;
begin
ms := getModule(s);
if ms.id = m.module.id then begin
result := m; exit
end;
for i := 0 to high(gPendingModules) do begin
result := gPendingModules[i];
if result.module.id = ms.id then exit;
end;
InternalError(s.info, 'no pending module found for: ' + s.name.s);
end;
procedure initLoc(var result: TLoc; k: TLocKind; typ: PType; s: TStorageLoc);
begin
@ -209,26 +251,13 @@ end;
// -------------------------- Variable manager ----------------------------
procedure declareGlobalVar(m: BModule; s: PSym);
begin
if not IntSetContainsOrIncl(m.declaredThings, s.id) then begin
app(m.s[cfsVars], getTypeDesc(m, s.loc.t));
if sfRegister in s.flags then
app(m.s[cfsVars], ' register');
if sfVolatile in s.flags then
app(m.s[cfsVars], ' volatile');
if sfThreadVar in s.flags then
app(m.s[cfsVars], ' NIM_THREADVAR');
appf(m.s[cfsVars], ' $1;$n', [s.loc.r])
end
end;
procedure assignLocalVar(p: BProc; s: PSym);
begin
//assert(s.loc.k == locNone) // not yet assigned
// this need not be fullfilled for inline procs; they are regenerated
// for each module that uses them!
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack);
if s.loc.k = locNone then
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack);
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t));
if sfRegister in s.flags then
app(p.s[cpsLocals], ' register');
@ -248,11 +277,19 @@ end;
procedure assignGlobalVar(m: BModule; s: PSym);
begin
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap);
if s.loc.k = locNone then
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap);
useHeader(m, s);
if lfNoDecl in s.loc.flags then exit;
if sfImportc in s.flags then app(m.s[cfsVars], 'extern ');
declareGlobalVar(m, s);
app(m.s[cfsVars], getTypeDesc(m, s.loc.t));
if sfRegister in s.flags then
app(m.s[cfsVars], ' register');
if sfVolatile in s.flags then
app(m.s[cfsVars], ' volatile');
if sfThreadVar in s.flags then
app(m.s[cfsVars], ' NIM_THREADVAR');
appf(m.s[cfsVars], ' $1;$n', [s.loc.r]);
if [optStackTrace, optEndb] * m.module.options =
[optStackTrace, optEndb] then begin
useMagic(m, 'dbgRegisterGlobal');
@ -282,6 +319,12 @@ begin
end
end;
procedure fillProcLoc(sym: PSym);
begin
if sym.loc.k = locNone then
fillLoc(sym.loc, locProc, sym.typ, mangleName(sym), OnStack);
end;
// -------------------------- label manager -------------------------------
// note that a label is a location too
@ -296,11 +339,11 @@ begin
appf(p.s[cpsStmts], '$1: ;$n', [labl])
end;
procedure genProcPrototype(m: BModule; sym: PSym); forward;
procedure genVarPrototype(m: BModule; sym: PSym); forward;
procedure genConstPrototype(m: BModule; sym: PSym); forward;
procedure genProc(m: BModule; prc: PSym); forward;
procedure genStmts(p: BProc; t: PNode); forward;
procedure genProcPrototype(m: BModule; sym: PSym); forward;
{$include 'ccgexprs.pas'}
{$include 'ccgstmts.pas'}
@ -343,11 +386,11 @@ begin
tmp := ropef('Dl_$1', [toRope(sym.id)]);
sym.loc.r := tmp; // from now on we only need the internal name
sym.typ.sym := nil; // generate a new name
appf(m.s[cfsDynLibInit],
'$1 = ($2) nimGetProcAddr($3, $4);$n',
[tmp, getTypeDesc(m, sym.typ), lib.name,
makeCString(ropeToStr(extname))]);
declareGlobalVar(m, sym)
appf(m.s[cfsDynLibInit], '$1 = ($2) nimGetProcAddr($3, $4);$n',
[tmp, getTypeDesc(m, sym.typ), lib.name, makeCString(ropeToStr(extname))]);
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t));
appf(m.s[cfsVars], ' $1;$n', [sym.loc.r]);
end;
// ----------------------------- sections ---------------------------------
@ -356,16 +399,16 @@ procedure UseMagic(m: BModule; const name: string);
var
sym: PSym;
begin
if (sfSystemModule in m.module.flags) then exit;
// we don't know the magic symbols in the system module, but they will be
// there anyway, because that is the way the code generator works
sym := magicsys.getCompilerProc(name);
case sym.kind of
skProc, skConverter: genProcPrototype(m, sym);
skVar: genVarPrototype(m, sym);
skType: {@discard} getTypeDesc(m, sym.typ);
else InternalError('useMagic: ' + name)
end
if sym <> nil then
case sym.kind of
skProc, skConverter: genProc(m, sym);
skVar: genVarPrototype(m, sym);
skType: {@discard} getTypeDesc(m, sym.typ);
else InternalError('useMagic: ' + name)
end
else if not (sfSystemModule in m.module.flags) then
rawMessage(errSystemNeeds, name); // don't be too picky here
end;
procedure generateHeaders(m: BModule);
@ -405,95 +448,131 @@ begin
result := (s.typ.sons[0] <> nil) and not isInvalidReturnType(s.typ.sons[0])
end;
procedure genProc(m: BModule; prc: PSym);
procedure genProcAux(m: BModule; prc: PSym);
var
p: BProc;
generatedProc, header, returnStmt: PRope;
i, profileId: int;
i: int;
res, param: PSym;
begin
useHeader(m, prc);
fillLoc(prc.loc, locProc, prc.typ, mangleName(prc), OnStack);
if (lfNoDecl in prc.loc.Flags) then exit;
if lfDynamicLib in prc.loc.flags then
SymInDynamicLib(m, prc)
else if not (sfImportc in prc.flags) then begin
// we have a real proc here:
p := newProc(prc, m);
header := genProcHeader(m, prc);
if (sfCompilerProc in prc.flags)
and (sfSystemModule in m.module.flags)
and not IntSetContains(m.declaredThings, prc.id) then
appf(m.s[cfsProcHeaders], '$1;$n', [header]);
intSetIncl(m.declaredThings, prc.id);
returnStmt := nil;
assert(prc.ast <> nil);
p := newProc(prc, m);
header := genProcHeader(m, prc);
returnStmt := nil;
assert(prc.ast <> nil);
if not (sfPure in prc.flags) and (prc.typ.sons[0] <> nil) then begin
res := prc.ast.sons[resultPos].sym; // get result symbol
if not isInvalidReturnType(prc.typ.sons[0]) then begin
// declare the result symbol:
assignLocalVar(p, res);
assert(res.loc.r <> nil);
returnStmt := ropef('return $1;$n', [rdLoc(res.loc)]);
end
else begin
fillResult(res);
assignParam(p, res);
end;
initVariable(p, res);
genObjectInit(p, res.typ, res.loc, true);
end;
for i := 1 to sonsLen(prc.typ.n)-1 do begin
param := prc.typ.n.sons[i].sym;
assignParam(p, param)
end;
genStmts(p, prc.ast.sons[codePos]); // modifies p.locals, p.init, etc.
if sfPure in prc.flags then
generatedProc := ropef('$1 {$n$2$3$4}$n',
[header, p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]])
if not (sfPure in prc.flags) and (prc.typ.sons[0] <> nil) then begin
res := prc.ast.sons[resultPos].sym; // get result symbol
if not isInvalidReturnType(prc.typ.sons[0]) then begin
// declare the result symbol:
assignLocalVar(p, res);
assert(res.loc.r <> nil);
returnStmt := ropef('return $1;$n', [rdLoc(res.loc)]);
end
else begin
generatedProc := con(header, '{' + tnl);
if optStackTrace in prc.options then begin
getFrameDecl(p);
prepend(p.s[cpsInit], ropef(
'F.procname = $1;$n' +
'F.prev = framePtr;$n' +
'F.filename = $2;$n' +
'F.line = 0;$n' +
'framePtr = (TFrame*)&F;$n',
[makeCString(prc.owner.name.s +{&} '.' +{&} prc.name.s),
makeCString(toFilename(prc.info))]));
end;
if optProfiler in prc.options then begin
if gProcProfile >= 64*1024 then // XXX: hard coded value!
InternalError(prc.info, 'too many procedures for profiling');
useMagic(m, 'profileData');
app(p.s[cpsLocals], 'ticks NIM_profilingStart;'+tnl);
if prc.loc.a < 0 then begin
appf(m.s[cfsDebugInit], 'profileData[$1].procname = $2;$n',
[toRope(gProcProfile),
makeCString(prc.owner.name.s +{&} '.' +{&} prc.name.s)]);
prc.loc.a := gProcProfile;
inc(gProcProfile);
end;
prepend(p.s[cpsInit], toRope('NIM_profilingStart = getticks();' + tnl));
end;
app(generatedProc, con(p.s));
if p.beforeRetNeeded then
app(generatedProc, 'BeforeRet: ;' + tnl);
if optStackTrace in prc.options then
app(generatedProc, 'framePtr = framePtr->prev;' + tnl);
if optProfiler in prc.options then
appf(generatedProc,
'profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n',
[toRope(prc.loc.a)]);
app(generatedProc, returnStmt);
app(generatedProc, '}' + tnl);
fillResult(res);
assignParam(p, res);
end;
app(m.s[cfsProcs], generatedProc);
initVariable(p, res);
genObjectInit(p, res.typ, res.loc, true);
end;
for i := 1 to sonsLen(prc.typ.n)-1 do begin
param := prc.typ.n.sons[i].sym;
assignParam(p, param)
end;
genStmts(p, prc.ast.sons[codePos]); // modifies p.locals, p.init, etc.
if sfPure in prc.flags then
generatedProc := ropef('$1 {$n$2$3$4}$n',
[header, p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]])
else begin
generatedProc := con(header, '{' + tnl);
if optStackTrace in prc.options then begin
getFrameDecl(p);
prepend(p.s[cpsInit], ropef(
'F.procname = $1;$n' +
'F.prev = framePtr;$n' +
'F.filename = $2;$n' +
'F.line = 0;$n' +
'framePtr = (TFrame*)&F;$n',
[makeCString(prc.owner.name.s +{&} '.' +{&} prc.name.s),
makeCString(toFilename(prc.info))]));
end;
if optProfiler in prc.options then begin
if gProcProfile >= 64*1024 then // XXX: hard coded value!
InternalError(prc.info, 'too many procedures for profiling');
useMagic(m, 'profileData');
app(p.s[cpsLocals], 'ticks NIM_profilingStart;'+tnl);
if prc.loc.a < 0 then begin
appf(m.s[cfsDebugInit], 'profileData[$1].procname = $2;$n',
[toRope(gProcProfile),
makeCString(prc.owner.name.s +{&} '.' +{&} prc.name.s)]);
prc.loc.a := gProcProfile;
inc(gProcProfile);
end;
prepend(p.s[cpsInit], toRope('NIM_profilingStart = getticks();' + tnl));
end;
app(generatedProc, con(p.s));
if p.beforeRetNeeded then
app(generatedProc, 'BeforeRet: ;' + tnl);
if optStackTrace in prc.options then
app(generatedProc, 'framePtr = framePtr->prev;' + tnl);
if optProfiler in prc.options then
appf(generatedProc,
'profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n',
[toRope(prc.loc.a)]);
app(generatedProc, returnStmt);
app(generatedProc, '}' + tnl);
end;
app(m.s[cfsProcs], generatedProc);
end;
procedure genProcPrototype(m: BModule; sym: PSym);
begin
useHeader(m, sym);
if (lfNoDecl in sym.loc.Flags) then exit;
if lfDynamicLib in sym.loc.Flags then begin
if (sym.owner.id <> m.module.id) and
not intSetContainsOrIncl(m.declaredThings, sym.id) then begin
appf(m.s[cfsVars], 'extern $1 Dl_$2;$n',
[getTypeDesc(m, sym.loc.t), toRope(sym.id)])
end
end
else begin
if not IntSetContainsOrIncl(m.declaredProtos, sym.id) then
appf(m.s[cfsProcHeaders], '$1;$n', [genProcHeader(m, sym)]);
end
end;
procedure genProcNoForward(m: BModule; prc: PSym);
begin
fillProcLoc(prc);
useHeader(m, prc);
genProcPrototype(m, prc);
if (lfNoDecl in prc.loc.Flags) then exit;
if prc.typ.callConv = ccInline then begin
// We add inline procs to the calling module to enable C based inlining.
// This also means that a check with ``gGeneratedSyms`` is wrong, we need
// a check for ``m.declaredThings``.
if not intSetContainsOrIncl(m.declaredThings, prc.id) then
genProcAux(m, prc);
end
else if lfDynamicLib in prc.loc.flags then begin
if not IntSetContainsOrIncl(gGeneratedSyms, prc.id) then
SymInDynamicLib(findPendingModule(m, prc), prc);
end
else if not (sfImportc in prc.flags) then begin
if not IntSetContainsOrIncl(gGeneratedSyms, prc.id) then
genProcAux(findPendingModule(m, prc), prc);
end
end;
procedure genProc(m: BModule; prc: PSym);
begin
fillProcLoc(prc);
if [sfForward, sfFromGeneric] * prc.flags <> [] then
addForwardedProc(m, prc)
else
genProcNoForward(m, prc)
end;
procedure genVarPrototype(m: BModule; sym: PSym);
@ -522,7 +601,8 @@ end;
procedure genConstPrototype(m: BModule; sym: PSym);
begin
useHeader(m, sym);
fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnUnknown);
if sym.loc.k = locNone then
fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnUnknown);
if (lfNoDecl in sym.loc.Flags) or
intSetContainsOrIncl(m.declaredThings, sym.id) then
exit;
@ -535,32 +615,6 @@ begin
end
end;
procedure genProcPrototype(m: BModule; sym: PSym);
begin
useHeader(m, sym);
fillLoc(sym.loc, locProc, sym.typ, mangleName(sym), OnStack);
if lfDynamicLib in sym.loc.Flags then begin
// it is a proc variable!
if (sym.owner.id <> m.module.id) and
not intSetContainsOrIncl(m.declaredThings, sym.id) then begin
app(m.s[cfsVars], 'extern ');
// BUGFIX: declareGlobalVar() inlined, because of intSetContainsOrIncl
// check
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t));
appf(m.s[cfsVars], ' $1;$n', [sym.loc.r])
end
end
else begin
// it is a proc:
if (lfNoDecl in sym.loc.Flags) then exit;
if intSetContainsOrIncl(m.declaredThings, sym.id) then exit;
appf(m.s[cfsProcHeaders], '$1;$n', [genProcHeader(m, sym)]);
if (sym.typ.callConv = ccInline)
and (sym.owner.id <> m.module.id) then
genProc(m, sym) // generate the code again!
end
end;
function getFileHeader(const cfilenoext: string): PRope;
begin
if optCompileOnly in gGlobalOptions then
@ -593,6 +647,7 @@ procedure genMainProc(m: BModule);
const
CommonMainBody =
' setStackBottom(dummy);$n' +
' nim__datInit();$n' +
' systemInit();$n' +
'$1' +
'$2';
@ -652,8 +707,7 @@ var
initname: PRope;
begin
initname := getInitName(m);
appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n',
[initname]);
appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', [initname]);
if not (sfSystemModule in m.flags) then
appf(mainModInit, '$1();$n', [initname]);
end;
@ -669,14 +723,18 @@ begin
{@discard} lists.IncludeStr(m.headerFiles, '<cycle.h>');
end;
initname := getInitName(m.module);
registerModuleToMain(m.module);
prc := ropef('N_NOINLINE(void, $1)(void) {$n', [initname]);
if m.typeNodes > 0 then
if m.typeNodes > 0 then begin
useMagic(m, 'TNimNode');
appf(m.s[cfsTypeInit1], 'static TNimNode $1[$2];$n',
[m.typeNodesName, toRope(m.typeNodes)]);
if m.nimTypes > 0 then
end;
if m.nimTypes > 0 then begin
useMagic(m, 'TNimType');
appf(m.s[cfsTypeInit1], 'static TNimType $1[$2];$n',
[m.nimTypesName, toRope(m.nimTypes)]);
end;
if optStackTrace in m.initProc.options then begin
getFrameDecl(m.initProc);
app(prc, m.initProc.s[cpsLocals]);
@ -716,7 +774,7 @@ begin
for i := low(TCFileSection) to cfsProcs do app(result, m.s[i])
end;
function newModule(module: PSym; const filename: string): BModule;
function rawNewModule(module: PSym; const filename: string): BModule;
begin
new(result);
{@ignore}
@ -724,7 +782,7 @@ begin
{@emit}
InitLinkedList(result.headerFiles);
intSetInit(result.declaredThings);
intSetInit(result.debugDeclared);
intSetInit(result.declaredProtos);
result.cfilename := filename;
result.filename := filename;
initIdTable(result.typeCache);
@ -735,12 +793,36 @@ begin
result.initProc.options := gOptions;
initNodeTable(result.dataCache);
{@emit result.typeStack := @[];}
{@emit result.forwardedProcs := @[];}
result.typeNodesName := getTempName();
result.nimTypesName := getTempName();
end;
function newModule(module: PSym; const filename: string): BModule;
begin
result := rawNewModule(module, filename);
if (optDeadCodeElim in gGlobalOptions) then begin
if (sfDeadCodeElim in module.flags) then
InternalError('added pending module twice: ' + filename);
addPendingModule(result)
end;
end;
procedure registerTypeInfoModule();
const
moduleName = 'nim__dat';
var
s: PSym;
begin
s := NewSym(skModule, getIdent(moduleName), nil);
gmti := rawNewModule(s, joinPath(options.projectPath, moduleName)+'.nim');
addPendingModule(gmti);
appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', [getInitName(s)]);
end;
function myOpen(module: PSym; const filename: string): PPassContext;
begin
if gmti = nil then registerTypeInfoModule();
result := newModule(module, filename);
end;
@ -749,6 +831,7 @@ function myOpenCached(module: PSym; const filename: string;
var
cfile, cfilenoext, objFile: string;
begin
if gmti = nil then registerTypeInfoModule();
//MessageOut('cgen.myOpenCached has been called ' + filename);
cfile := changeFileExt(completeCFilePath(filename), cExt);
cfilenoext := changeFileExt(cfile, '');
@ -761,6 +844,8 @@ begin
end; *)
addFileToLink(cfilenoext);
registerModuleToMain(module);
// XXX: this cannot be right here, initalization has to be appended during
// the ``myClose`` call
result := nil;
end;
@ -790,11 +875,52 @@ begin
genStmts(m.initProc, n);
end;
function myClose(b: PPassContext; n: PNode): PNode;
procedure finishModule(m: BModule);
var
i: int;
prc: PSym;
begin
i := 0;
while i <= high(m.forwardedProcs) do begin
// Note: ``genProc`` may add to ``m.forwardedProcs``, so we cannot use
// a for loop here
prc := m.forwardedProcs[i];
if sfForward in prc.flags then InternalError(prc.info, 'still forwarded');
genProcNoForward(m, prc);
inc(i);
end;
assert(gForwardedProcsCounter >= i);
dec(gForwardedProcsCounter, i);
setLength(m.forwardedProcs, 0);
end;
procedure writeModule(m: BModule);
var
cfile, cfilenoext: string;
m: BModule;
code: PRope;
begin
// generate code for the init statements of the module:
genInitCode(m);
finishTypeDescriptions(m);
cfile := completeCFilePath(m.cfilename);
cfilenoext := changeFileExt(cfile, '');
if sfMainModule in m.module.flags then begin
// generate main file:
app(m.s[cfsProcHeaders], mainModProcs);
end;
code := genModule(m, cfilenoext);
if shouldRecompile(code, changeFileExt(cfile, cExt), cfilenoext) then begin
addFileToCompile(cfilenoext);
end;
addFileToLink(cfilenoext);
if sfMainModule in m.module.flags then writeMapping(cfile, gMapping);
end;
function myClose(b: PPassContext; n: PNode): PNode;
var
m: BModule;
i: int;
begin
result := n;
if b = nil then exit;
@ -803,22 +929,24 @@ begin
m.initProc.options := gOptions;
genStmts(m.initProc, n);
end;
// generate code for the init statements of the module:
genInitCode(m);
finishTypeDescriptions(m);
cfile := completeCFilePath(m.cfilename);
cfilenoext := changeFileExt(cfile, '');
registerModuleToMain(m.module);
if not (optDeadCodeElim in gGlobalOptions) and
not (sfDeadCodeElim in m.module.flags) then
finishModule(m);
if sfMainModule in m.module.flags then begin
// generate main file:
app(m.s[cfsProcHeaders], mainModProcs);
genMainProc(m);
// we need to process the transitive closure because recursive module
// deps are allowed (and the system module is processed in the wrong
// order anyway)
while gForwardedProcsCounter > 0 do
for i := 0 to high(gPendingModules) do
finishModule(gPendingModules[i]);
for i := 0 to high(gPendingModules) do writeModule(gPendingModules[i]);
setLength(gPendingModules, 0);
end;
code := genModule(m, cfilenoext);
if shouldRecompile(code, changeFileExt(cfile, cExt), cfilenoext) then begin
addFileToCompile(cfilenoext); // is to compile
end;
addFileToLink(cfilenoext);
if sfMainModule in m.module.flags then writeMapping(cfile, gMapping);
if not (optDeadCodeElim in gGlobalOptions) and
not (sfDeadCodeElim in m.module.flags) then
writeModule(m);
end;
function cgenPass(): TPass;
@ -832,4 +960,5 @@ end;
initialization
InitIiTable(gToTypeInfoId);
IntSetInit(gGeneratedSyms);
end.

View file

@ -39,14 +39,14 @@ const
{$ifdef fpc}
compileDate = {$I %date%};
{$else}
compileDate = '2008-0-0';
compileDate = '2009-0-0';
{$endif}
{@emit}
const
HelpMessage = 'Nimrod Compiler Version $1 (' +{&}
compileDate +{&} ') [$2: $3]' +{&} nl +{&}
'Copyright (c) 2004-2008 by Andreas Rumpf' +{&} nl;
'Copyright (c) 2004-2009 by Andreas Rumpf' +{&} nl;
const
Usage = ''
@ -82,6 +82,7 @@ const
+{&} ' --bound_checks:on|off code generation for bound checks ON|OFF' +{&} nl
+{&} ' --overflow_checks:on|off code generation for over-/underflow checks ON|OFF' +{&} nl
+{&} ' -a, --assertions:on|off code generation for assertions ON|OFF' +{&} nl
+{&} ' --dead_code_elim:on|off whole program dead code elimination ON|OFF' +{&} nl
+{&} ' --opt:none|speed|size optimize not at all or for speed|size' +{&} nl
+{&} ' --app:console|gui|lib generate a console|GUI application or a shared lib' +{&} nl
+{&} ' -r, --run run the compiled program with given arguments' +{&} nl
@ -96,7 +97,7 @@ const
// cog.outl(f(line))
//]]]
+{&} 'Advanced commands::' +{&} nl
+{&} ' pas convert a Pascal file to Nimrod standard syntax' +{&} nl
+{&} ' pas convert a Pascal file to Nimrod syntax' +{&} nl
+{&} ' pretty pretty print the inputfile' +{&} nl
+{&} ' gen_depend generate a DOT file containing the' +{&} nl
+{&} ' module dependency graph' +{&} nl
@ -135,8 +136,8 @@ const
function getCommandLineDesc: string;
begin
result := format(HelpMessage, [VersionAsString, platform.os[hostOS].name,
cpu[hostCPU].name]) +{&} Usage
result := format(HelpMessage, [VersionAsString,
platform.os[platform.hostOS].name, cpu[platform.hostCPU].name]) +{&} Usage
end;
var
@ -157,8 +158,10 @@ procedure writeAdvancedUsage(pass: TCmdLinePass);
begin
if (pass = passCmd1) and not advHelpWritten then begin
// BUGFIX 19
MessageOut(format(HelpMessage, [VersionAsString, platform.os[hostOS].name,
cpu[hostCPU].name]) +{&} AdvancedUsage);
MessageOut(format(HelpMessage, [VersionAsString,
platform.os[platform.hostOS].name,
cpu[platform.hostCPU].name]) +{&}
AdvancedUsage);
advHelpWritten := true;
helpWritten := true;
halt(0);
@ -170,8 +173,9 @@ begin
if (pass = passCmd1) and not versionWritten then begin
versionWritten := true;
helpWritten := true;
messageOut(format(HelpMessage, [VersionAsString, platform.os[hostOS].name,
cpu[hostCPU].name]))
messageOut(format(HelpMessage, [VersionAsString,
platform.os[platform.hostOS].name,
cpu[platform.hostCPU].name]))
end
end;
@ -388,6 +392,7 @@ begin
wOverflowChecks: ProcessOnOffSwitch({@set}[optOverflowCheck], arg, pass, info);
wLineDir: ProcessOnOffSwitch({@set}[optLineDir], arg, pass, info);
wAssertions, wA: ProcessOnOffSwitch({@set}[optAssert], arg, pass, info);
wDeadCodeElim: ProcessOnOffSwitchG({@set}[optDeadCodeElim], arg, pass, info);
wOpt: begin
case whichKeyword(arg) of
wSpeed: begin
@ -453,7 +458,7 @@ begin
theOS := platform.NameToOS(arg);
if theOS = osNone then
liMessage(info, errUnknownOS, arg);
if theOS <> hostOS then begin
if theOS <> platform.hostOS then begin
setTarget(theOS, targetCPU);
include(gGlobalOptions, optCompileOnly);
condsyms.InitDefines()
@ -465,7 +470,7 @@ begin
cpu := platform.NameToCPU(arg);
if cpu = cpuNone then
liMessage(info, errUnknownCPU, arg);
if cpu <> hostCPU then begin
if cpu <> platform.hostCPU then begin
setTarget(targetOS, cpu);
include(gGlobalOptions, optCompileOnly);
condsyms.InitDefines()

View file

@ -218,8 +218,6 @@ begin
end;
function nextSplitPoint(const s: string; start: int): int;
var
i: int;
begin
result := start;
while result < length(s)+strStart do begin
@ -393,7 +391,8 @@ begin
fillChar(r, sizeof(r), 0);
{@emit}
comm := genRecComment(d, n); // call this here for the side-effect!
initTokRender(r, n, {@set}[renderNoPragmas, renderNoBody]);
initTokRender(r, n, {@set}[renderNoPragmas, renderNoBody, renderNoComments,
renderDocComments]);
while true do begin
getNextTok(r, kind, literal);
case kind of
@ -579,7 +578,7 @@ begin
end;
rnHyperlink: begin
result := ropef('`$1 <$2>`_', [renderRstToRst(d, n.sons[0]),
renderRstToRst(d, n.sons[1])]);
renderRstToRst(d, n.sons[1])]);
end;
rnGeneralRole: begin
result := renderRstToRst(d, n.sons[0]);
@ -661,21 +660,26 @@ begin
result := ropef('<ul class="simple">$1</ul>', [result]);
end;
function fieldAux(const s: string): PRope;
begin
result := toRope(strip(s))
end;
function renderImage(d: PDoc; n: PRstNode): PRope;
var
s: string;
begin
result := ropef('<img src="$1"', [toRope(getArgument(n))]);
s := getFieldValue(n, 'height');
if s <> '' then appf(result, ' height="$1"', [toRope(s)]);
if s <> '' then appf(result, ' height="$1"', [fieldAux(s)]);
s := getFieldValue(n, 'width');
if s <> '' then appf(result, ' width="$1"', [toRope(s)]);
if s <> '' then appf(result, ' width="$1"', [fieldAux(s)]);
s := getFieldValue(n, 'scale');
if s <> '' then appf(result, ' scale="$1"', [toRope(s)]);
if s <> '' then appf(result, ' scale="$1"', [fieldAux(s)]);
s := getFieldValue(n, 'alt');
if s <> '' then appf(result, ' alt="$1"', [toRope(s)]);
if s <> '' then appf(result, ' alt="$1"', [fieldAux(s)]);
s := getFieldValue(n, 'align');
if s <> '' then appf(result, ' align="$1"', [toRope(s)]);
if s <> '' then appf(result, ' align="$1"', [fieldAux(s)]);
app(result, ' />');
if rsonsLen(n) >= 3 then app(result, renderRstToHtml(d, n.sons[2]))
end;
@ -863,15 +867,18 @@ begin
nkConverterDef: genItem(d, n, n.sons[namePos], skConverter);
nkVarSection: begin
for i := 0 to sonsLen(n)-1 do
genItem(d, n.sons[i], n.sons[i].sons[0], skVar);
if n.sons[i].kind <> nkCommentStmt then
genItem(d, n.sons[i], n.sons[i].sons[0], skVar);
end;
nkConstSection: begin
for i := 0 to sonsLen(n)-1 do
genItem(d, n.sons[i], n.sons[i].sons[0], skConst);
if n.sons[i].kind <> nkCommentStmt then
genItem(d, n.sons[i], n.sons[i].sons[0], skConst);
end;
nkTypeSection: begin
for i := 0 to sonsLen(n)-1 do
genItem(d, n.sons[i], n.sons[i].sons[0], skType);
if n.sons[i].kind <> nkCommentStmt then
genItem(d, n.sons[i], n.sons[i].sons[0], skType);
end;
nkStmtList: begin
for i := 0 to sonsLen(n)-1 do generateDoc(d, n.sons[i]);

View file

@ -896,7 +896,8 @@ begin
or (skipGeneric(y.typ).kind in [tyRef, tyPtr, tyVar])
end;
procedure genAsgnAux(var p: TProc; x, y: PNode; var r: TCompRes);
procedure genAsgnAux(var p: TProc; x, y: PNode; var r: TCompRes;
noCopyNeeded: bool);
var
a, b: TCompRes;
begin
@ -906,7 +907,7 @@ begin
etyObject: begin
if a.com <> nil then appf(r.com, '$1;$n', [a.com]);
if b.com <> nil then appf(r.com, '$1;$n', [b.com]);
if needsNoCopy(y) then
if needsNoCopy(y) or noCopyNeeded then
appf(r.com, '$1 = $2;$n', [a.res, b.res])
else begin
useMagic(p, 'NimCopy');
@ -930,7 +931,13 @@ end;
procedure genAsgn(var p: TProc; n: PNode; var r: TCompRes);
begin
genLineDir(p, n, r);
genAsgnAux(p, n.sons[0], n.sons[1], r);
genAsgnAux(p, n.sons[0], n.sons[1], r, false);
end;
procedure genFastAsgn(var p: TProc; n: PNode; var r: TCompRes);
begin
genLineDir(p, n, r);
genAsgnAux(p, n.sons[0], n.sons[1], r, true);
end;
procedure genSwap(var p: TProc; n: PNode; var r: TCompRes);
@ -1692,6 +1699,7 @@ begin
nkReturnStmt: genReturnStmt(p, n, r);
nkBreakStmt: genBreakStmt(p, n, r);
nkAsgn: genAsgn(p, n, r);
nkFastAsgn: genFastAsgn(p, n, r);
nkDiscardStmt: begin
genLineDir(p, n, r);
gen(p, n.sons[0], r);

View file

@ -28,7 +28,7 @@ type
TStackFrame = record
mapping: TIdNodeTable; // mapping from symbols to nodes
prc: PSym; // current prc; proc that is evaluated
call: PNode; // current for stmt
call: PNode;
next: PStackFrame; // for stacking
params: TNodeSeq; // parameters passed to the proc
end;
@ -127,7 +127,7 @@ begin
inc(i)
end;
if (i < len) and (sonsLen(n.sons[i]) < 2) then // eval else-part
result := evalAux(c, n.sons[0])
result := evalAux(c, n.sons[i].sons[0])
else
result := emptyNode
end;
@ -1227,7 +1227,7 @@ begin
nkDerefExpr, nkHiddenDeref: result := evalDeref(c, n);
nkAddr, nkHiddenAddr: result := evalAddr(c, n);
nkHiddenStdConv, nkHiddenSubConv, nkConv: result := evalConv(c, n);
nkAsgn: result := evalAsgn(c, n);
nkAsgn, nkFastAsgn: result := evalAsgn(c, n);
nkWhenStmt, nkIfStmt, nkIfExpr: result := evalIf(c, n);
nkWhileStmt: result := evalWhile(c, n);
nkCaseStmt: result := evalCase(c, n);
@ -1259,7 +1259,7 @@ begin
nkTemplateDef, nkConstSection, nkIteratorDef, nkConverterDef,
nkIncludeStmt, nkImportStmt, nkFromStmt: begin end;
nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
nkQualified, nkLambda, nkContinueStmt:
nkQualified, nkLambda, nkContinueStmt, nkIdent:
stackTrace(c, n, errCannotInterpretNodeX, nodeKindToStr[n.kind]);
else InternalError(n.info, 'evalAux: ' + nodekindToStr[n.kind]);
end;

View file

@ -343,17 +343,11 @@ begin
result := ccNone
end;
procedure addStr(var dest: string; const src: string);
begin
dest := dest +{&} src;
end;
procedure addOpt(var dest: string; const src: string);
begin
if (length(dest) = 0) or (dest[length(dest)-1+strStart] <> ' ') then
addStr(dest, ' '+'');
addStr(dest, src);
add(dest, ' '+'');
add(dest, src);
end;
procedure addCompileOption(const option: string);
@ -473,40 +467,52 @@ begin
key := cc[c].name + '.exe';
if existsConfigVar(key) then
exe := getConfigVar(key);
if targetOS = osWindows then exe := appendFileExt(exe, 'exe');
if (optGenDynLib in gGlobalOptions)
and (ospNeedsPIC in platform.OS[targetOS].props) then
addStr(options, ' ' + cc[c].pic);
add(options, ' ' + cc[c].pic);
if targetOS = hostOS then begin
if targetOS = platform.hostOS then begin
// compute include paths:
includeCmd := cc[c].includeCmd; // this is more complex than needed, but
// a workaround of a FPC bug...
addStr(includeCmd, libpath);
compilePattern := quoteIfSpaceExists(JoinPath(ccompilerpath, exe));
add(includeCmd, quoteIfContainsWhite(libpath));
compilePattern := JoinPath(ccompilerpath, exe);
end
else begin
includeCmd := '';
compilePattern := cc[c].compilerExe
end;
if targetOS = hostOS then
if targetOS = platform.hostOS then
cfile := cfilename
else
cfile := extractFileName(cfilename);
if not isExternal or (targetOS <> hostOS) then
if not isExternal or (targetOS <> platform.hostOS) then
objfile := toObjFile(cfile)
else
objfile := completeCFilePath(toObjFile(cfile));
cfile := quoteIfContainsWhite(AppendFileExt(cfile, cExt));
objfile := quoteIfContainsWhite(objfile);
result := format(compilePattern +{&} ' ' +{&} cc[c].compileTmpl,
['file', AppendFileExt(cfile, cExt),
result := quoteIfContainsWhite(format(compilePattern,
['file', cfile,
'objfile', objfile,
'options', options,
'include', includeCmd,
'nimrod', getPrefixDir(),
'lib', libpath
]);
]));
add(result, ' ');
add(result, format(cc[c].compileTmpl,
['file', cfile,
'objfile', objfile,
'options', options,
'include', includeCmd,
'nimrod', quoteIfContainsWhite(getPrefixDir()),
'lib', quoteIfContainsWhite(libpath)
]));
end;
procedure CompileCFile(const list: TLinkedList;
@ -551,11 +557,12 @@ 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 (hostOS <> targetOS) then
linkCmd := linkerExe
if (platform.hostOS <> targetOS) then
linkCmd := quoteIfContainsWhite(linkerExe)
else
linkCmd := quoteIfSpaceExists(JoinPath(ccompilerpath, linkerExe));
linkCmd := quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe));
if optGenDynLib in gGlobalOptions then
buildDll := cc[c].buildDll
@ -570,27 +577,29 @@ begin
exefile := platform.os[targetOS].dllPrefix
else
exefile := '';
if targetOS = hostOS then
addStr(exefile, projectFile)
if targetOS = platform.hostOS then
add(exefile, projectFile)
else
addStr(exefile, extractFileName(projectFile));
add(exefile, extractFileName(projectFile));
if optGenDynLib in gGlobalOptions then
addStr(exefile, platform.os[targetOS].dllExt)
add(exefile, platform.os[targetOS].dllExt)
else
addStr(exefile, platform.os[targetOS].exeExt);
add(exefile, platform.os[targetOS].exeExt);
exefile := quoteIfContainsWhite(exefile);
it := PStrEntry(toLink.head);
objfiles := '';
while it <> nil do begin
addStr(objfiles, ' '+'');
if targetOS = hostOS then
addStr(objfiles, toObjfile(it.data))
add(objfiles, ' '+'');
if targetOS = platform.hostOS then
add(objfiles, quoteIfContainsWhite(toObjfile(it.data)))
else
addStr(objfiles, toObjfile(extractFileName(it.data)));
add(objfiles, quoteIfContainsWhite(
toObjfile(extractFileName(it.data))));
it := PStrEntry(it.next);
end;
linkCmd := format(linkCmd +{&} ' ' +{&} cc[c].linkTmpl, [
linkCmd := quoteIfContainsWhite(format(linkCmd, [
'builddll', builddll,
'buildgui', buildgui,
'options', linkOptions,
@ -598,7 +607,18 @@ begin
'exefile', exefile,
'nimrod', getPrefixDir(),
'lib', libpath
]);
]));
add(linkCmd, ' ');
add(linkCmd, format(cc[c].linkTmpl, [
'builddll', builddll,
'buildgui', buildgui,
'options', linkOptions,
'objfiles', objfiles,
'exefile', exefile,
'nimrod', quoteIfContainsWhite(getPrefixDir()),
'lib', quoteIfContainsWhite(libpath)
]));
if not (optCompileOnly in gGlobalOptions) then
execExternalProgram(linkCmd);
end // end if not noLinking

View file

@ -61,7 +61,7 @@ begin
result := x -{%} 1;
// complicated, to make it a nop if sizeof(int) == 4,
// because shifting more than 31 bits is undefined in C
result := result or (result shr ((sizeof(int)-4)* 32));
result := result or (result shr ((sizeof(int)-4)* 8));
result := result or (result shr 16);
result := result or (result shr 8);
result := result or (result shr 4);

View file

@ -64,7 +64,7 @@ function getCurrentLine(const L: TBaseLexer; marker: boolean = true): string;
function getColNumber(const L: TBaseLexer; pos: int): int;
function HandleCR(var L: TBaseLexer; pos: int): int;
// Call this if you scanned over CR in the buffer; it returns the the
// Call this if you scanned over CR in the buffer; it returns the
// position to continue the scanning from. `pos` must be the position
// of the CR.
@ -211,8 +211,7 @@ end;
function getColNumber(const L: TBaseLexer; pos: int): int;
begin
result := pos - L.lineStart;
assert(result >= 0);
result := abs(pos - L.lineStart);
end;
function getCurrentLine(const L: TBaseLexer; marker: boolean = true): string;

View file

@ -96,9 +96,10 @@ begin
result := StrTableGet(compilerprocs, ident);
if result = nil then begin
result := StrTableGet(rodCompilerProcs, ident);
if result = nil then rawMessage(errSystemNeeds, name);
strTableAdd(compilerprocs, result);
if result.kind = skStub then loadStub(result);
if result <> nil then begin
strTableAdd(compilerprocs, result);
if result.kind = skStub then loadStub(result);
end;
// A bit hacky that this code is needed here, but it is the easiest
// solution in order to avoid special cases for sfCompilerProc in the
// rodgen module. Another solution would be to always recompile the system

View file

@ -173,7 +173,7 @@ type
errSelectorMustBeOrdinal,
errOrdXMustNotBeNegative,
errLenXinvalid,
errWrongNumberOfLoopVariables,
errWrongNumberOfVariables,
errExprCannotBeRaised,
errBreakOnlyInLoop,
errTypeXhasUnknownSize,
@ -431,7 +431,7 @@ const
'selector must be of an ordinal type',
'ord($1) must not be negative',
'len($1) must be less than 32768',
'wrong number of loop variables',
'wrong number of variables',
'only objects can be raised',
'''break'' only allowed in loop construct',
'type ''$1'' has unknown size',

View file

@ -93,7 +93,8 @@ begin
}
end;
if optRun in gGlobalOptions then
execExternalProgram(changeFileExt(filename, '') +{&} ' ' +{&} arguments)
execExternalProgram(quoteIfContainsWhite(changeFileExt(filename, '')) +{&}
' ' +{&} arguments)
end
end;

View file

@ -35,9 +35,11 @@ const
{$ifdef mswindows}
dirsep = '\'; // seperator within paths
altsep = '/';
exeExt = 'exe';
{$else}
dirsep = '/';
altsep = #0; // work around fpc bug
exeExt = '';
{$endif}
pathSep = ';'; // seperator between paths
sep = dirsep; // alternative name

View file

@ -149,8 +149,12 @@ function subU(a, b: biggestInt): biggestInt;
function mulU(a, b: biggestInt): biggestInt;
function divU(a, b: biggestInt): biggestInt;
function modU(a, b: biggestInt): biggestInt;
function shlU(a, b: biggestInt): biggestInt;
function shrU(a, b: biggestInt): biggestInt;
function shlU(a, b: biggestInt): biggestInt; overload;
function shrU(a, b: biggestInt): biggestInt; overload;
function shlU(a, b: Int32): Int32;overload;
function shrU(a, b: int32): int32;overload;
function ltU(a, b: biggestInt): bool;
function leU(a, b: biggestInt): bool;
@ -281,6 +285,16 @@ begin
result := biggestInt(biggestUInt(a) shr biggestUInt(b));
end;
function shlU(a, b: Int32): Int32;
begin
result := Int32(UInt32(a) shl UInt32(b));
end;
function shrU(a, b: int32): int32;
begin
result := Int32(UInt32(a) shr UInt32(b));
end;
function ltU(a, b: biggestInt): bool;
begin
result := biggestUInt(a) < biggestUInt(b);

View file

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

View file

@ -1,34 +0,0 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit optast;
// Optimizations that can be done by AST transformations. The code generators
// should work without the optimizer. The optimizer does the following:
// - cross-module constant merging
// - cross-module generic merging
// - lowers set operations to bit operations
// - inlining of procs
// - ``s == ""`` --> ``len(s) == 0``
// - optimization of ``&`` string operator
interface
{$include 'config.inc'}
uses
nsystem, ast, astalgo, strutils, hashes, trees, treetab, platform, magicsys,
options, msgs, crc, idents, lists, types, ropes, nmath, wordrecg, rnimsyn;
implementation
end.

View file

@ -201,6 +201,12 @@ begin
if startsWith(dir, prefix) then begin
result := ncopy(dir, length(prefix) + strStart); exit
end;
prefix := projectPath +{&} dirSep;
//writeln(output, prefix);
//writeln(output, dir);
if startsWith(dir, prefix) then begin
result := ncopy(dir, length(prefix) + strStart); exit
end;
result := dir
end;
@ -209,7 +215,7 @@ var
head, tail: string;
begin
splitPath(path, head, tail);
result := joinPath([projectPath, genSubDir, shortenDir(head),
result := joinPath([projectPath, genSubDir, shortenDir(head +{&} dirSep),
changeFileExt(tail, ext)])
end;
@ -219,9 +225,11 @@ var
head, tail, subdir: string;
begin
splitPath(f, head, tail);
subdir := joinPath([projectPath, genSubDir, shortenDir(head)]);
if createSubDir then
subdir := joinPath([projectPath, genSubDir, shortenDir(head +{&} dirSep)]);
if createSubDir then begin
//Writeln(output, subdir);
createDir(subdir);
end;
result := joinPath(subdir, tail)
end;

View file

@ -51,7 +51,7 @@ begin
else begin
result.cmd := '';
for i := 1 to ParamCount() do
result.cmd := result.cmd +{&} quoteIfSpaceExists(paramStr(i)) +{&} ' ';
result.cmd := result.cmd +{&} quoteIfContainsWhite(paramStr(i)) +{&} ' ';
{@ignore}
result.cmd := result.cmd + #0;
{@emit}

View file

@ -14,7 +14,7 @@ unit passaux;
interface
uses
nsystem, strutils, ast, passes, msgs, options;
nsystem, strutils, ast, astalgo, passes, msgs, options;
function verbosePass: TPass;
function cleanupPass: TPass;
@ -50,6 +50,8 @@ var
s: PSym;
begin
result := n;
// we cannot clean up if dead code elimination is activated
if (optDeadCodeElim in gGlobalOptions) then exit;
case n.kind of
nkStmtList: begin
for i := 0 to sonsLen(n)-1 do {@discard} cleanup(c, n.sons[i]);
@ -57,7 +59,8 @@ begin
nkProcDef: begin
if (n.sons[namePos].kind = nkSym) then begin
s := n.sons[namePos].sym;
if not astNeeded(s) then s.ast.sons[codePos] := nil; // free the memory
if not (sfDeadCodeElim in getModule(s).flags) and
not astNeeded(s) then s.ast.sons[codePos] := nil; // free the memory
end
end
else begin end;

View file

@ -23,7 +23,7 @@ uses
type
TSystemOS = (
// Also add OS for in initialization section and alias conditionals to
// Also add OS in initialization section and alias conditionals to
// condsyms (end of module).
osNone,
osDos,

View file

@ -9,7 +9,7 @@
unit pnimsyn;
// This module implements the parser of the standard Nimrod representation.
// The parser is strictly reflects the grammar ("doc/grammar.txt"); however
// The parser strictly reflects the grammar ("doc/grammar.txt"); however
// it uses several helper routines to keep the parser small. A special
// efficient algorithm is used for the precedence levels. The parser here can
// be seen as a refinement of the grammar, as it specifies how the AST is build
@ -98,15 +98,21 @@ end;
// ---------------- parser helpers --------------------------------------------
procedure parMessage(const p: TParser; const msg: TMsgKind;
const arg: string = '');
begin
lexMessage(p.lex^, msg, arg);
end;
procedure skipComment(var p: TParser; node: PNode);
begin
if p.tok.tokType = tkComment then begin
if node <> nil then begin
if node.comment = snil then node.comment := '';
node.comment := node.comment +{&} p.tok.literal;
add(node.comment, p.tok.literal);
end
else
assert(false);
parMessage(p, errInternal, 'skipComment');
getTok(p);
end
end;
@ -116,6 +122,11 @@ begin
if p.tok.tokType = tkInd then getTok(p)
end;
procedure optSad(var p: TParser);
begin
if p.tok.tokType = tkSad then getTok(p)
end;
procedure optInd(var p: TParser; n: PNode);
begin
skipComment(p, n);
@ -146,12 +157,6 @@ begin
else lexMessage(p.lex^, errTokenExpected, TokTypeToStr[tokType])
end;
procedure parMessage(const p: TParser; const msg: TMsgKind;
const arg: string = '');
begin
lexMessage(p.lex^, msg, arg);
end;
function parLineInfo(const p: TParser): TLineInfo;
begin
result := getLineInfo(p.lex^)
@ -385,7 +390,7 @@ begin
result := newNodeP(nkExprEqExpr, p);
addSon(result, a);
getTok(p);
optInd(p, result);
//optInd(p, result);
case p.tok.tokType of
tkVar, tkRef, tkPtr, tkProc:
addSon(result, parseTypeDescK(p));
@ -413,21 +418,16 @@ begin
addSon(result, first);
getTok(p);
optInd(p, result);
while true do begin
if p.tok.tokType = tkBracketRi then begin
getTok(p); break
end;
if p.tok.tokType = tkEof then begin
parMessage(p, errTokenExpected, TokTypeToStr[tkBracketRi]); break
end;
while (p.tok.tokType <> tkBracketRi) and (p.tok.tokType <> tkEof)
and (p.tok.tokType <> tkSad) do begin
a := namedTypeOrExpr(p);
optInd(p, a);
if p.tok.tokType = tkComma then begin
getTok(p);
optInd(p, a)
end;
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, tkBracketRi);
end;
function exprColonEqExpr(var p: TParser; kind: TNodeKind;
@ -439,7 +439,7 @@ begin
if p.tok.tokType = tok then begin
result := newNodeP(kind, p);
getTok(p);
optInd(p, result);
//optInd(p, result);
addSon(result, a);
addSon(result, parseExpr(p));
end
@ -454,21 +454,14 @@ var
begin
getTok(p);
optInd(p, result);
while true do begin
if p.tok.tokType = endTok then begin
getTok(p); break
end;
if p.tok.tokType = tkEof then begin
parMessage(p, errTokenExpected, TokTypeToStr[endtok]); break
end;
while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof) do begin
a := exprColonEqExpr(p, elemKind, sepTok);
optInd(p, a);
if p.tok.tokType = tkComma then begin
getTok(p);
optInd(p, a)
end;
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
eat(p, endTok);
end;
function qualifiedIdent(var p: TParser): PNode;
@ -476,7 +469,7 @@ var
a: PNode;
begin
result := parseSymbol(p);
optInd(p, result);
//optInd(p, result);
if p.tok.tokType = tkDot then begin
getTok(p);
optInd(p, result);
@ -494,28 +487,41 @@ var
begin
getTok(p);
optInd(p, result);
while true do begin
if p.tok.tokType = endTok then begin
getTok(p); break
end;
if p.tok.tokType = tkEof then begin
parMessage(p, errTokenExpected, TokTypeToStr[endtok]); break
end;
while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof) do begin
a := qualifiedIdent(p);
optInd(p, a);
if p.tok.tokType = tkComma then begin
getTok(p);
optInd(p, a)
end;
addSon(result, a);
//optInd(p, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
eat(p, endTok);
end;
procedure exprColonEqExprListAux(var p: TParser; elemKind: TNodeKind;
endTok, sepTok: TTokType; result: PNode);
var
a: PNode;
begin
getTok(p);
optInd(p, result);
while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof)
and (p.tok.tokType <> tkSad) do begin
a := exprColonEqExpr(p, elemKind, sepTok);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, endTok);
end;
function exprColonEqExprList(var p: TParser; kind, elemKind: TNodeKind;
endTok, sepTok: TTokType): PNode;
begin
result := newNodeP(kind, p);
exprListAux(p, elemKind, endTok, sepTok, result);
exprColonEqExprListAux(p, elemKind, endTok, sepTok, result);
end;
function parseCast(var p: TParser): PNode;
@ -525,12 +531,12 @@ begin
eat(p, tkBracketLe);
optInd(p, result);
addSon(result, parseTypeDesc(p));
optInd(p, result);
optSad(p);
eat(p, tkBracketRi);
eat(p, tkParLe);
optInd(p, result);
addSon(result, parseExpr(p));
optInd(p, result);
optSad(p);
eat(p, tkParRi);
end;
@ -541,7 +547,7 @@ begin
eat(p, tkParLe);
optInd(p, result);
addSon(result, parseExpr(p));
optInd(p, result);
optSad(p);
eat(p, tkParRi);
end;
@ -667,7 +673,7 @@ begin
a := result;
result := newNodeP(nkCall, p);
addSon(result, a);
exprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result);
exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result);
end;
tkDot: begin
a := result;
@ -709,8 +715,9 @@ begin
opNode := newIdentNodeP(op.ident, p);
// skip operator:
getTok(p);
skipComment(p, opNode);
skipInd(p);
//skipComment(p, opNode);
//skipInd(p);
optInd(p, opNode);
// read sub-expression with higher priority
nextop := lowestExprAux(p, v2, opPred);
@ -739,12 +746,12 @@ begin
while true do begin
getTok(p); // skip `if`, `elif`
branch := newNodeP(nkElifExpr, p);
optInd(p, branch);
//optInd(p, branch);
addSon(branch, parseExpr(p));
eat(p, tkColon);
optInd(p, branch);
//optInd(p, branch);
addSon(branch, parseExpr(p));
optInd(p, branch);
//optInd(p, branch);
addSon(result, branch);
if p.tok.tokType <> tkElif then break
end;
@ -770,31 +777,33 @@ var
begin
result := newNodeP(nkPragma, p);
getTok(p);
while true do begin
skipComment(p, result);
skipInd(p);
case p.tok.TokType of
tkCurlyDotRi, tkCurlyRi: begin
getTok(p); // skip } or .}
break
end;
tkEof: begin
parMessage(p, errTokenExpected, '.}');
break
end
else begin
a := exprColonEqExpr(p, nkExprColonExpr, tkColon);
addSon(result, a);
if p.tok.tokType = tkComma then begin
getTok(p);
skipComment(p, a)
end
end
optInd(p, result);
while (p.tok.tokType <> tkCurlyDotRi) and (p.tok.tokType <> tkCurlyRi)
and (p.tok.tokType <> tkEof) and (p.tok.tokType <> tkSad) do begin
a := exprColonEqExpr(p, nkExprColonExpr, tkColon);
addSon(result, a);
if p.tok.tokType = tkComma then begin
getTok(p);
optInd(p, a)
end
end
end;
optSad(p);
if (p.tok.tokType = tkCurlyDotRi) or (p.tok.tokType = tkCurlyRi) then
getTok(p)
else
parMessage(p, errTokenExpected, '.}');
end;
// ---------------------- statement parser ------------------------------------
function isExprStart(const p: TParser): bool;
begin
case p.tok.tokType of
tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkLambda,
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit: result := true;
else result := false;
end;
end;
function parseExprStmt(var p: TParser): PNode;
var
a, b, e: PNode;
@ -813,16 +822,16 @@ begin
result.info := a.info;
addSon(result, a);
while true do begin
case p.tok.tokType of
(*case p.tok.tokType of
tkColon, tkInd, tkSad, tkDed, tkEof, tkComment: break;
else begin end
end;
end;*)
if not isExprStart(p) then break;
e := parseExpr(p);
if p.tok.tokType = tkComma then begin
getTok(p);
skipComment(p, e)
end;
addSon(result, e);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a);
end;
if sonsLen(result) <= 1 then result := a
else a := result;
@ -897,7 +906,7 @@ begin
break
end;
end;
optInd(p, a);
//optInd(p, a);
if p.tok.tokType = tkAs then begin
getTok(p);
optInd(p, a);
@ -906,11 +915,10 @@ begin
addSon(a, b);
addSon(a, parseSymbol(p));
end;
if p.tok.tokType = tkComma then begin
getTok(p);
optInd(p, a)
end;
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
end;
@ -942,12 +950,11 @@ begin
break
end;
end;
optInd(p, a);
if p.tok.tokType = tkComma then begin
getTok(p);
optInd(p, a)
end;
addSon(result, a);
//optInd(p, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
end;
@ -977,7 +984,7 @@ begin
end
end;
addSon(result, a);
optInd(p, a);
//optInd(p, a);
eat(p, tkImport);
optInd(p, result);
while true do begin
@ -989,12 +996,11 @@ begin
break
end;
end;
optInd(p, a);
if p.tok.tokType = tkComma then begin
getTok(p);
optInd(p, a)
end;
//optInd(p, a);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
end;
@ -1074,6 +1080,7 @@ begin
result := newNodeP(nkCaseStmt, p);
getTok(p);
addSon(result, parseExpr(p));
if p.tok.tokType = tkColon then getTok(p);
skipComment(p, result);
inElif := false;
while true do begin
@ -1145,22 +1152,15 @@ begin
result := newNodeP(nkForStmt, p);
getTok(p);
optInd(p, result);
while true do begin
if p.tok.tokType = tkIn then begin
getTok(p); break
end;
if p.tok.tokType = tkEof then begin
parMessage(p, errTokenExpected, TokTypeToStr[tkIn]); break
end;
a := parseSymbol(p);
if a = nil then break;
a := parseSymbol(p);
addSon(result, a);
while p.tok.tokType = tkComma do begin
getTok(p);
optInd(p, a);
if p.tok.tokType = tkComma then begin
getTok(p); optInd(p, a)
end;
a := parseSymbol(p);
addSon(result, a);
end;
eat(p, tkIn);
addSon(result, exprColonEqExpr(p, nkRange, tkDotDot));
eat(p, tkColon);
skipComment(p, result);
@ -1246,12 +1246,11 @@ begin
if a = nil then exit;
end
end;
optInd(p, a);
if p.tok.tokType = tkComma then begin
getTok(p);
optInd(p, a)
end;
addSon(result, a);
//optInd(p, 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);
@ -1282,15 +1281,17 @@ begin
while true do begin
case p.tok.tokType of
tkSymbol, tkAccent: a := parseIdentColonEquals(p, false);
tkParRi: begin getTok(p); break end;
tkParRi: break;
else begin parMessage(p, errTokenExpected, ')'+''); break; end;
end;
optInd(p, a);
if p.tok.tokType = tkComma then begin
getTok(p); optInd(p, a)
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);
@ -1336,18 +1337,15 @@ begin
getTok(p);
eat(p, tkBracketLe);
optInd(p, result);
while true do begin
case p.tok.tokType of
tkSymbol, tkAccent: a := parseIdentColonEquals(p, false);
tkBracketRi: begin getTok(p); break end;
else begin parMessage(p, errTokenExpected, ']'+''); break; end;
end;
optInd(p, a);
if p.tok.tokType = tkComma then begin
getTok(p); optInd(p, a)
end;
while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
a := parseIdentColonEquals(p, false);
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');
@ -1389,18 +1387,15 @@ begin
result := newNodeP(nkGenericParams, p);
getTok(p);
optInd(p, result);
while true do begin
case p.tok.tokType of
tkSymbol, tkAccent: a := parseGenericParam(p);
tkBracketRi: begin getTok(p); break end;
else begin parMessage(p, errTokenExpected, ']'+''); break; end;
end;
optInd(p, a);
if p.tok.tokType = tkComma then begin
getTok(p); optInd(p, a)
end;
while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
a := parseGenericParam(p);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
optSad(p);
eat(p, tkBracketRi);
end;
function parseRoutine(var p: TParser; kind: TNodeKind): PNode;
@ -1484,7 +1479,8 @@ begin
break
end
end
end
end;
popInd(p.lex^);
end;
tkSymbol, tkAccent: addSon(result, defparser(p));
else parMessage(p, errIdentifierExpected, tokToStr(p.tok));
@ -1628,7 +1624,8 @@ begin
break
end
end
end
end;
popInd(p.lex^);
end;
tkWhen: result := parseRecordWhen(p);
tkCase: result := parseRecordCase(p);
@ -1688,9 +1685,34 @@ begin
indAndComment(p, result); // special extension!
end;
function parseVarTuple(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkVarTuple, p);
getTok(p); // skip '('
optInd(p, result);
while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
a := identWithPragma(p);
addSon(result, a);
if p.tok.tokType <> tkComma then break;
getTok(p);
optInd(p, a)
end;
addSon(result, nil); // no type desc
optSad(p);
eat(p, tkParRi);
eat(p, tkEquals);
optInd(p, result);
addSon(result, parseExpr(p));
end;
function parseVariable(var p: TParser): PNode;
begin
result := parseIdentColonEquals(p, true);
if p.tok.tokType = tkParLe then
result := parseVarTuple(p)
else
result := parseIdentColonEquals(p, true);
indAndComment(p, result); // special extension!
end;
@ -1708,10 +1730,15 @@ begin
tkFrom: result := parseFromStmt(p);
tkInclude: result := parseIncludeStmt(p);
tkComment: result := newCommentStmt(p);
//tkSad, tkInd, tkDed: assert(false);
else result := parseExprStmt(p)
else begin
if isExprStart(p) then
result := parseExprStmt(p)
else
result := nil;
end
end;
skipComment(p, result);
if result <> nil then
skipComment(p, result);
end;
function complexOrSimpleStmt(var p: TParser): PNode;
@ -1738,6 +1765,8 @@ begin
end;
function parseStmt(var p: TParser): PNode;
var
a: PNode;
begin
if p.tok.tokType = tkInd then begin
result := newNodeP(nkStmtList, p);
@ -1748,9 +1777,14 @@ begin
tkSad: getTok(p);
tkEof: break;
tkDed: begin getTok(p); break end;
else addSon(result, complexOrSimpleStmt(p));
end;
end
else begin
a := complexOrSimpleStmt(p);
if a = nil then break;
addSon(result, a);
end
end
end;
popInd(p.lex^);
end
else begin
// the case statement is only needed for better error messages:
@ -1762,7 +1796,7 @@ begin
end
else begin
result := simpleStmt(p);
skipComment(p, result);
if result = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
if p.tok.tokType = tkSad then getTok(p);
end
end
@ -1770,6 +1804,8 @@ begin
end;
function parseModule(var p: TParser): PNode;
var
a: PNode;
begin
result := newNodeP(nkStmtList, p);
while true do begin
@ -1777,7 +1813,11 @@ begin
tkSad: getTok(p);
tkDed, tkInd: parMessage(p, errInvalidIndentation);
tkEof: break;
else addSon(result, complexOrSimpleStmt(p));
else begin
a := complexOrSimpleStmt(p);
if a = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
addSon(result, a);
end
end
end
end;
@ -1795,6 +1835,7 @@ begin
tkEof: break;
else begin
result := complexOrSimpleStmt(p);
if result = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
break
end
end

View file

@ -135,8 +135,8 @@ var
v: string;
m: TMagic;
begin
if not (sfSystemModule in c.module.flags) then
liMessage(n.info, errMagicOnlyInSystem);
//if not (sfSystemModule in c.module.flags) then
// liMessage(n.info, errMagicOnlyInSystem);
if n.kind <> nkExprColonExpr then
liMessage(n.info, errStringLiteralExpected);
if n.sons[1].kind = nkIdent then v := n.sons[1].ident.s
@ -172,6 +172,19 @@ begin
liMessage(n.info, errOnOrOffExpected)
end;
procedure pragmaDeadCodeElim(c: PContext; n: PNode);
begin
if (n.kind = nkExprColonExpr) and (n.sons[1].kind = nkIdent) then begin
case whichKeyword(n.sons[1].ident) of
wOn: include(c.module.flags, sfDeadCodeElim);
wOff: exclude(c.module.flags, sfDeadCodeElim);
else liMessage(n.info, errOnOrOffExpected)
end
end
else
liMessage(n.info, errOnOrOffExpected)
end;
procedure processCallConv(c: PContext; n: PNode);
var
sw: TSpecialWord;
@ -466,6 +479,7 @@ begin
wVolatile: begin noVal(it); Include(sym.flags, sfVolatile); end;
wRegister: begin noVal(it); include(sym.flags, sfRegister); end;
wThreadVar: begin noVal(it); include(sym.flags, sfThreadVar); end;
wDeadCodeElim: pragmaDeadCodeElim(c, it);
wMagic: processMagic(c, it, sym);
wCompileTime: begin
noVal(it);
@ -612,7 +626,7 @@ begin
wHint, wWarning, wError, wFatal, wDefine, wUndef,
wCompile, wLink, wLinkSys, wPure,
wPush, wPop, wFixupSystem, wBreakpoint, wCheckpoint,
wPassL, wPassC]);
wPassL, wPassC, wDeadCodeElim]);
end;
procedure pragmaLambda(c: PContext; s: PSym; n: PNode);

View file

@ -21,7 +21,7 @@ uses
type
TRenderFlag = (renderNone, renderNoBody, renderNoComments,
renderNoPragmas, renderIds);
renderDocComments, renderNoPragmas, renderIds);
TRenderFlags = set of TRenderFlag;
TRenderTok = record
@ -354,12 +354,20 @@ end;
const
Space = ' '+'';
function shouldRenderComment(var g: TSrcGen; n: PNode): bool;
begin
result := false;
if n.comment <> snil then
result := not (renderNoComments in g.flags) or
(renderDocComments in g.flags) and startsWith(n.comment, '##');
end;
procedure gcom(var g: TSrcGen; n: PNode);
var
ml: int;
begin
assert(n <> nil);
if (n.comment <> snil) and not (renderNoComments in g.flags) then begin
if shouldRenderComment(g, n) then begin
if (g.pendingNL < 0) and (length(g.buf) > 0)
and (g.buf[length(g.buf)] <> ' ') then
put(g, tkSpaces, Space);
@ -488,7 +496,7 @@ begin
nkAddr: result := lsub(n.sons[0])+length('addr()');
nkHiddenAddr, nkHiddenDeref: result := lsub(n.sons[0]);
nkCommand: result := lsub(n.sons[0])+lcomma(n, 1)+1;
nkExprEqExpr, nkDefaultTypeParam, nkAsgn: result := lsons(n)+3;
nkExprEqExpr, nkDefaultTypeParam, nkAsgn, nkFastAsgn: result := lsons(n)+3;
nkPar, nkCurly, nkBracket: result := lcomma(n)+2;
nkTupleTy: result := lcomma(n)+length('tuple[]');
nkQualified, nkDotExpr: result := lsons(n)+1;
@ -502,6 +510,7 @@ begin
if n.sons[L-1] <> nil then
result := result + lsub(n.sons[L-1]) + 3;
end;
nkVarTuple: result := lcomma(n, 0, -3) + length('() = ') + lsub(lastSon(n));
nkChckRangeF: result := length('chckRangeF') + 2 + lcomma(n);
nkChckRange64: result := length('chckRange64') + 2 + lcomma(n);
nkChckRange: result := length('chckRange') + 2 + lcomma(n);
@ -981,7 +990,7 @@ begin
put(g, tkSpaces, space);
gcomma(g, n, 1);
end;
nkExprEqExpr, nkDefaultTypeParam, nkAsgn: begin
nkExprEqExpr, nkDefaultTypeParam, nkAsgn, nkFastAsgn: begin
gsub(g, n.sons[0]);
put(g, tkSpaces, Space);
putWithSpace(g, tkEquals, '='+'');
@ -1056,6 +1065,14 @@ begin
gsub(g, n.sons[L-1], c)
end;
end;
nkVarTuple: begin
put(g, tkParLe, '('+'');
gcomma(g, n, 0, -3);
put(g, tkParRi, ')'+'');
put(g, tkSpaces, Space);
putWithSpace(g, tkEquals, '='+'');
gsub(g, lastSon(n), c);
end;
nkExprColonExpr: begin
gsub(g, n.sons[0]);
putWithSpace(g, tkColon, ':'+'');
@ -1362,11 +1379,11 @@ begin
nkTupleTy: begin
put(g, tkTuple, 'tuple');
put(g, tkBracketLe, '['+'');
assert(n.sons[0].kind = nkIdentDefs);
gcomma(g, n);
put(g, tkBracketRi, ']'+'');
end;
else begin
//nkNone, nkMetaNode, nkTableConstr, nkExplicitTypeListCall: begin
InternalError(n.info, 'rnimsyn.gsub(' +{&} nodeKindToStr[n.kind] +{&} ')')
end
end

View file

@ -503,7 +503,7 @@ end;
function process(c: PPassContext; n: PNode): PNode;
var
i, j: int;
i: int;
w: PRodWriter;
a: PNode;
s: PSym;

View file

@ -14,7 +14,7 @@ unit ropes;
efficiently; especially concatenation is done in O(1) instead of O(N).
Ropes make use a lazy evaluation: They are essentially concatenation
trees that are only flattened when converting to a native Nimrod
string or when written to disk. The empty string is represented with a
string or when written to disk. The empty string is represented by a
nil pointer.
A little picture makes everything clear:
@ -541,7 +541,6 @@ begin
if (r.data <> snil) then begin
if r.len > bufSize then
// A token bigger than 1 KB? - This cannot happen in reality.
// Well, at least I hope so. 1 KB did happen!
internalError('ropes: token too long');
readBytes := readBuffer(bin, buf, r.len);
result := (readBytes = r.len) // BUGFIX

View file

@ -494,7 +494,13 @@ begin
if n.kind = rnLeaf then begin
for i := strStart to length(n.text)+strStart-1 do begin
case n.text[i] of
'a'..'z', '0'..'9': begin
'0'..'9': begin
if b then begin addChar(r, '-'); b := false; end;
// BUGFIX: HTML id's cannot start with a digit
if length(r) = 0 then addChar(r, 'Z');
addChar(r, n.text[i])
end;
'a'..'z': begin
if b then begin addChar(r, '-'); b := false; end;
addChar(r, n.text[i])
end;
@ -1235,7 +1241,7 @@ begin
result := nil;
if (p.tok[p.idx].kind = tkIndent)
and (p.tok[p.idx+1].symbol = ':'+'') then begin
col := p.tok[p.idx].col;
col := p.tok[p.idx].ival; // BUGFIX!
result := newRstNode(rnFieldList);
inc(p.idx);
while true do begin
@ -1252,10 +1258,10 @@ var
i: int;
f: PRstNode;
begin
assert(n.kind = rnDirective);
result := '';
if n.sons[1] = nil then exit;
assert(n.sons[1].kind = rnFieldList);
if (n.sons[1].kind <> rnFieldList) then
InternalError('getFieldValue (2): ' + rstnodeKindToStr[n.sons[1].kind]);
for i := 0 to rsonsLen(n.sons[1])-1 do begin
f := n.sons[1].sons[i];
if cmpIgnoreStyle(addNodes(f.sons[0]), fieldname) = 0 then begin

View file

@ -21,9 +21,8 @@ interface
{$include 'config.inc'}
uses
charsets, nsystem, sysutils,
hashes, options, msgs, strutils, platform, idents,
lexbase, llstream, wordrecg;
charsets, nsystem, sysutils, hashes, options, msgs, strutils, platform,
idents, lexbase, llstream, wordrecg;
const
MaxLineLength = 80; // lines longer than this lead to a warning
@ -31,7 +30,7 @@ const
numChars: TCharSet = ['0'..'9','a'..'z','A'..'Z'];
SymChars: TCharSet = ['a'..'z', 'A'..'Z', '0'..'9', #128..#255];
SymStartChars: TCharSet = ['a'..'z', 'A'..'Z', #128..#255];
OpChars: TCharSet = ['+', '-', '*', '/', '<', '>', '!', '?', '^', '.',
OpChars: TCharSet = ['+', '-', '*', '/', '\', '<', '>', '!', '?', '^', '.',
'|', '=', '%', '&', '$', '@', '~', #128..#255];
type
@ -162,6 +161,8 @@ var
gLinesCompiled: int; // all lines that have been compiled
procedure pushInd(var L: TLexer; indent: int);
procedure popInd(var L: TLexer);
function isKeyword(kind: TTokType): boolean;
procedure openLexer(out lex: TLexer; const filename: string;
@ -206,6 +207,14 @@ begin
//writeln('push indent ', indent);
end;
procedure popInd(var L: TLexer);
var
len: int;
begin
len := length(L.indentStack);
setLength(L.indentStack, len-1);
end;
function findIdent(const L: TLexer; indent: int): boolean;
var
i: int;
@ -809,13 +818,11 @@ begin
end;
dec(L.dedent);
tok.tokType := tkDed;
if i >= 0 then
setLength(L.indentStack, i+1) // pop indentations
else begin
if i < 0 then begin
tok.tokType := tkSad; // for the parser it is better as SAD
lexMessage(L, errInvalidIndentation);
end
end;
end
end;
procedure scanComment(var L: TLexer; var tok: TToken);

View file

@ -76,6 +76,22 @@ function semStmt(c: PContext; n: PNode): PNode; forward;
function semConstExpr(c: PContext; n: PNode): PNode;
var
e: PNode;
begin
e := semExprWithType(c, n);
if e = nil then begin
liMessage(n.info, errConstExprExpected);
result := nil; exit
end;
result := getConstExpr(c.module, e);
if result = nil then
liMessage(n.info, errConstExprExpected);
end;
function semAndEvalConstExpr(c: PContext; n: PNode): PNode;
var
e: PNode;
p: PEvalContext;
s: PStackFrame;
begin
e := semExprWithType(c, n);
if e = nil then begin
@ -85,7 +101,14 @@ begin
result := getConstExpr(c.module, e);
if result = nil then begin
//writeln(output, renderTree(n));
liMessage(n.info, errConstExprExpected);
p := newEvalContext(c.module, '');
s := newStackFrame();
s.call := e;
pushStackFrame(p, s);
result := eval(p, e);
popStackFrame(p);
if (result = nil) or (result.kind = nkEmpty) then
liMessage(n.info, errConstExprExpected);
end
end;

View file

@ -43,6 +43,7 @@ type
PContext = ^TContext;
TContext = object(TPassContext) // a context represents a module
module: PSym; // the module sym belonging to the context
filename: string; // the module's filename
tab: TSymTab; // each module has its own symbol table
AmbigiousSymbols: TIntSet; // contains ids of all ambigious symbols (cannot
// store this info in the syms themselves!)
@ -160,6 +161,7 @@ begin
result.module := module;
result.generics := newNode(nkStmtList);
{@emit result.converters := @[];}
result.filename := nimfile;
end;
procedure addConverter(c: PContext; conv: PSym);

View file

@ -36,9 +36,9 @@ var
diff: int;
begin
diff := inheritanceDiff(castDest, src);
if diff = 0 then
liMessage(info, hintConvToBaseNotNeeded)
else if diff = high(int) then
//if diff = 0 then
// liMessage(info, hintConvToBaseNotNeeded)
if diff = high(int) then
liMessage(info, errGenerated,
format(MsgKindToString(errIllegalConvFromXtoY),
[typeToString(src), typeToString(castDest)]));
@ -466,11 +466,11 @@ begin
if m.state <> csMatch then begin
msg := msgKindToString(errTypeMismatch);
for i := 1 to sonsLen(n)-1 do begin
msg := msg +{&} typeToString(n.sons[i].typ);
if i <> sonsLen(n)-1 then msg := msg + ', ';
add(msg, typeToString(n.sons[i].typ));
if i <> sonsLen(n)-1 then add(msg, ', ');
end;
msg := msg +{&} ')' +{&} nl +{&} msgKindToString(errButExpected) +{&}
nl +{&} typeToString(n.sons[0].typ);
add(msg, ')' +{&} nl +{&} msgKindToString(errButExpected) +{&}
nl +{&} typeToString(n.sons[0].typ));
liMessage(n.Info, errGenerated, msg);
result := nil
end
@ -1117,7 +1117,7 @@ begin
if s <> nil then
result := semSym(c, n, s, flags)
else
// test!
// this is a test comment; please don't touch it
result := semFieldAccess(c, n, flags);
end;

View file

@ -94,6 +94,25 @@ begin
end
end;
function enumValToString(a: PNode): string;
var
n: PNode;
field: PSym;
x: biggestInt;
i: int;
begin
x := getInt(a);
n := a.typ.n;
for i := 0 to sonsLen(n)-1 do begin
if n.sons[i].kind <> nkSym then InternalError(a.info, 'enumValToString');
field := n.sons[i].sym;
if field.position = x then begin
result := field.name.s; exit
end;
end;
InternalError(a.info, 'no symbol for ordinal value: ' + toString(x));
end;
function evalOp(m: TMagic; n, a, b: PNode): PNode;
// if this is an unary operation, b is nil
begin
@ -246,11 +265,18 @@ begin
// available for interpretation. I don't know how to fix this.
//result := newStrNodeT(renderTree(a, {@set}[renderNoComments]), n);
end;
mIntToStr, mInt64ToStr, mBoolToStr, mCharToStr:
mIntToStr, mInt64ToStr:
result := newStrNodeT(toString(getOrdValue(a)), n);
mBoolToStr: begin
if getOrdValue(a) = 0 then
result := newStrNodeT('false', n)
else
result := newStrNodeT('true', n)
end;
mFloatToStr: result := newStrNodeT(toStringF(getFloat(a)), n);
mCStrToStr: result := newStrNodeT(getStrOrChar(a), n);
mCStrToStr, mCharToStr: result := newStrNodeT(getStrOrChar(a), n);
mStrToStr: result := a;
mEnumToStr: result := newStrNodeT(enumValToString(a), n);
mArrToSeq: begin
result := copyTree(a);
result.typ := n.typ;
@ -370,6 +396,10 @@ begin
mNimrodMinor: result := newIntNodeT(VersionMinor, n);
mNimrodPatch: result := newIntNodeT(VersionPatch, n);
mCpuEndian: result := newIntNodeT(ord(CPU[targetCPU].endian), n);
mHostOS:
result := newStrNodeT(toLower(platform.OS[targetOS].name), n);
mHostCPU:
result := newStrNodeT(toLower(platform.CPU[targetCPU].name),n);
mNaN: result := newFloatNodeT(NaN, n);
mInf: result := newFloatNodeT(Inf, n);
mNegInf: result := newFloatNodeT(NegInf, n);

View file

@ -324,7 +324,7 @@ begin
a := newNodeI(nkAsgn, n.sons[0].info);
n.sons[0] := fitNode(c, restype, n.sons[0]);
// optimize away ``return result``, because it would be transferred
// optimize away ``return result``, because it would be transformed
// to ``result = result; return``:
if (n.sons[0].kind = nkSym) and (sfResult in n.sons[0].sym.flags) then
begin
@ -378,14 +378,14 @@ function semVar(c: PContext; n: PNode): PNode;
var
i, j, len: int;
a, b, def: PNode;
typ: PType;
typ, tup: PType;
v: PSym;
begin
result := copyNode(n);
for i := 0 to sonsLen(n)-1 do begin
a := n.sons[i];
if a.kind = nkCommentStmt then continue;
if (a.kind <> nkIdentDefs) then IllFormedAst(a);
if (a.kind <> nkIdentDefs) and (a.kind <> nkVarTuple) then IllFormedAst(a);
checkMinSonsLen(a, 3);
len := sonsLen(a);
if a.sons[len-2] <> nil then
@ -401,14 +401,21 @@ begin
end
else
def := nil;
tup := skipGeneric(typ);
if a.kind = nkVarTuple then begin
if tup.kind <> tyTuple then liMessage(a.info, errXExpected, 'tuple');
if len-2 <> sonsLen(tup) then
liMessage(a.info, errWrongNumberOfVariables);
end;
for j := 0 to len-3 do begin
if (c.p.owner = nil) then begin
if c.p.owner = nil then begin
v := semIdentWithPragma(c, skVar, a.sons[j], {@set}[sfStar, sfMinus]);
include(v.flags, sfGlobal);
end
else
v := semIdentWithPragma(c, skVar, a.sons[j], {@set}[]);
v.typ := typ;
if a.kind <> nkVarTuple then v.typ := typ
else v.typ := tup.sons[j];
if v.flags * [sfStar, sfMinus] <> {@set}[] then
include(v.flags, sfInInterface);
addInterfaceDecl(c, v);
@ -443,7 +450,7 @@ begin
if a.sons[1] <> nil then typ := semTypeNode(c, a.sons[1], nil)
else typ := nil;
def := semConstExpr(c, a.sons[2]);
def := semAndEvalConstExpr(c, a.sons[2]);
// check type compability between def.typ and typ:
if (typ <> nil) then begin
def := fitRemoveHiddenConv(c, typ, def);
@ -495,7 +502,7 @@ begin
iter := skipGeneric(n.sons[len-2].typ);
openScope(c.tab);
if iter.kind <> tyTuple then begin
if len <> 3 then liMessage(n.info, errWrongNumberOfLoopVariables);
if len <> 3 then liMessage(n.info, errWrongNumberOfVariables);
v := newSymS(skForVar, n.sons[0], c);
v.typ := iter;
n.sons[0] := newSymNode(v);
@ -503,7 +510,7 @@ begin
end
else begin
if len-2 <> sonsLen(iter) then
liMessage(n.info, errWrongNumberOfLoopVariables);
liMessage(n.info, errWrongNumberOfVariables);
for i := 0 to len-3 do begin
v := newSymS(skForVar, n.sons[i], c);
v.typ := iter.sons[i];
@ -840,6 +847,7 @@ begin
closeScope(c.tab); // close scope for parameters
popOwner();
c.p := oldP; // restore
result.typ := s.typ;
end;
function semProcAux(c: PContext; n: PNode; kind: TSymKind): PNode;

View file

@ -256,8 +256,8 @@ begin
if templ = nil then begin result := nil; exit end;
case templ.kind of
nkSym: begin
if (templ.sym.kind = skTypeParam)
and (templ.sym.owner.id = sym.id) then
if (templ.sym.kind = skTypeParam) then
//and (templ.sym.owner.id = sym.id) then
result := copyTree(actual.sons[templ.sym.position+1])
else
result := copyNode(templ)
@ -285,15 +285,18 @@ begin
result.containerID := s.typ.containerID; // ... but the same containerID
result.sym := s;
if (s.typ.containerID = 0) then
InternalError(n.info, 'semGeneric');
InternalError(n.info, 'semtypes.semGeneric');
for i := 1 to sonsLen(n)-1 do begin
elem := semTypeNode(c, n.sons[i], nil);
if elem.kind = tyGenericParam then result.kind := tyGeneric;
if elem.kind = tyGenericParam then
result.kind := tyGeneric; // prevend type from instantiation
addSon(result, elem);
end;
if s.ast <> nil then begin
inst := instGenericAux(c, s.ast.sons[2], n, s);
if result.kind = tyGenericInst then begin
if (result.kind = tyGenericInst) then begin
inst := instGenericAux(c, s.ast.sons[2], n, s);
internalError(n.info, 'Generic containers not implemented');
// XXX: implementation does not work this way
// does checking of instantiated type for us:
elem := semTypeNode(c, inst, nil);
elem.id := result.containerID;
@ -304,6 +307,8 @@ begin
end
else
liMessage(n.info, errCannotInstantiateX, s.name.s);
(*if computeSize(result) < 0 then
liMessage(s.info, errIllegalRecursionInTypeX, s.name.s);*)
end;
function semIdentVis(c: PContext; kind: TSymKind; n: PNode;

View file

@ -80,20 +80,23 @@ var
begin
result := msgKindToString(errTypeMismatch);
for i := 1 to sonsLen(n)-1 do begin
result := result +{&} typeToString(n.sons[i].typ);
if i <> sonsLen(n)-1 then result := result + ', ';
debug(n.sons[i].typ);
add(result, typeToString(n.sons[i].typ));
if i <> sonsLen(n)-1 then add(result, ', ');
end;
addChar(result, ')');
candidates := '';
sym := initOverloadIter(o, c, n.sons[0]);
while sym <> nil do begin
if sym.kind in [skProc, skIterator, skConverter] then
candidates := candidates +{&} getProcHeader(sym) +{&} nl;
if sym.kind in [skProc, skIterator, skConverter] then begin
add(candidates, getProcHeader(sym));
add(candidates, nl)
end;
sym := nextOverloadIter(o, c, n.sons[0]);
end;
if candidates <> '' then
result := result +{&} nl +{&} msgKindToString(errButExpected) +{&} nl
+{&} candidates;
add(result, nl +{&} msgKindToString(errButExpected) +{&} nl
+{&} candidates);
end;
function typeRel(var mapping: TIdTable; f, a: PType): TTypeRelation; overload;
@ -431,7 +434,7 @@ begin // is a subtype of f?
tyAnyEnum: begin
case a.kind of
tyRange: result := typeRel(mapping, f, base(a));
tyEnum: result := isEqual;
tyEnum: result := isSubtype;
else begin end
end
end;

View file

@ -8,8 +8,7 @@
//
unit strtabs;
// A configuration file parser; the Nimrod version of this file
// will become part of the standard library.
// String tables.
interface

View file

@ -75,13 +75,14 @@ const
function strip(const s: string; const chars: TCharSet = WhiteSpace): string;
function allCharsInSet(const s: string; const theSet: TCharSet): bool;
function quoteIfSpaceExists(const s: string): string;
function quoteIfContainsWhite(const s: string): string;
implementation
function quoteIfSpaceExists(const s: string): string;
function quoteIfContainsWhite(const s: string): string;
begin
if (findSubStr(' ', s) >= strStart) and (s[strStart] <> '"') then
if ((findSubStr(' ', s) >= strStart)
or (findSubStr(#9, s) >= strStart)) and (s[strStart] <> '"') then
result := '"' +{&} s +{&} '"'
else
result := s

47
nim/tigen.pas Normal file
View file

@ -0,0 +1,47 @@
//
//
// The Nimrod Compiler
// (c) Copyright 2008 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit tigen;
// Type information generator. It transforms types into the AST of walker
// procs. This is used by the code generators.
interface
{$include 'config.inc'}
uses
nsystem, ast, astalgo, strutils, hashes, trees, treetab, platform, magicsys,
options, msgs, crc, idents, lists, types, rnimsyn;
function gcWalker(t: PType): PNode;
function initWalker(t: PType): PNode;
function asgnWalker(t: PType): PNode;
function reprWalker(t: PType): PNode;
implementation
function gcWalker(t: PType): PNode;
begin
end;
function initWalker(t: PType): PNode;
begin
end;
function asgnWalker(t: PType): PNode;
begin
end;
function reprWalker(t: PType): PNode;
begin
end;
end.

View file

@ -22,7 +22,7 @@ interface
uses
sysutils, nsystem, charsets, strutils,
lists, options, ast, astalgo, trees, treetab,
msgs, nos, idents, rnimsyn, types, passes, semfold;
msgs, nos, idents, rnimsyn, types, passes, semfold, magicsys;
const
genPrefix = ':tmp'; // prefix for generated names
@ -139,7 +139,7 @@ More efficient, but not implementable:
function newAsgnStmt(c: PTransf; le, ri: PNode): PNode;
begin
result := newNodeI(nkAsgn, ri.info);
result := newNodeI(nkFastAsgn, ri.info);
addSon(result, le);
addSon(result, ri);
end;
@ -224,6 +224,28 @@ begin
end
end;
function newTupleAccess(tup: PNode; i: int): PNode;
var
lit: PNode;
begin
result := newNodeIT(nkBracketExpr, tup.info, tup.typ.sons[i]);
addSon(result, copyTree(tup));
lit := newNodeIT(nkIntLit, tup.info, getSysType(tyInt));
lit.intVal := i;
addSon(result, lit);
end;
procedure unpackTuple(c: PTransf; n, father: PNode);
var
i: int;
begin
// XXX: BUG: what if `n` is an expression with side-effects?
for i := 0 to sonsLen(n)-1 do begin
addSon(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
transform(c, newTupleAccess(n, i))));
end
end;
function transformYield(c: PTransf; n: PNode): PNode;
var
e: PNode;
@ -239,10 +261,8 @@ begin
transform(c, copyTree(e.sons[i]))));
end
end
else begin
// XXX: tuple unpacking:
internalError(n.info, 'tuple unpacking is not implemented');
end
else
unpackTuple(c, e, result);
end
else begin
e := transform(c, copyTree(e));
@ -523,7 +543,7 @@ end;
(*
# example:
proc map(f: proc (x: int): int {.closure}, a: seq[int]): seq[int] =
result = []
result = @[]
for elem in a:
add result, f(a)
@ -534,12 +554,12 @@ end;
proc map(f: proc(x: int): int, closure: pointer,
a: seq[int]): seq[int] =
result = []
result = @[]
for elem in a:
add result, f(a, closure)
type
PMyClosure = ref record
PMyClosure = ref object
y: var int
proc myLambda(x: int, closure: pointer) =

View file

@ -638,15 +638,15 @@ begin
assert(sonsLen(t.n) = sonsLen(t));
for i := 0 to sonsLen(t.n)-1 do begin
assert(t.n.sons[i].kind = nkSym);
result := result +{&} t.n.sons[i].sym.name.s +{&} ': '
+{&} typeToString(t.sons[i]);
if i < sonsLen(t.n)-1 then result := result +{&} ', ';
add(result, t.n.sons[i].sym.name.s +{&} ': '
+{&} typeToString(t.sons[i]));
if i < sonsLen(t.n)-1 then add(result, ', ');
end
end
else begin
for i := 0 to sonsLen(t)-1 do begin
result := result +{&} typeToString(t.sons[i]);
if i < sonsLen(t)-1 then result := result +{&} ', ';
add(result, typeToString(t.sons[i]));
if i < sonsLen(t)-1 then add(result, ', ');
end
end;
addChar(result, ']')
@ -659,14 +659,14 @@ begin
tyProc: begin
result := 'proc (';
for i := 1 to sonsLen(t)-1 do begin
result := result +{&} typeToString(t.sons[i]);
if i < sonsLen(t)-1 then result := result +{&} ', ';
add(result, typeToString(t.sons[i]));
if i < sonsLen(t)-1 then add(result, ', ');
end;
addChar(result, ')');
if t.sons[0] <> nil then
result := result +{&} ': ' +{&} TypeToString(t.sons[0]);
add(result, ': ' +{&} TypeToString(t.sons[0]));
if t.callConv <> ccDefault then
result := result +{&} '{.' +{&} CallingConvToStr[t.callConv] +{&} '.}';
add(result, '{.' +{&} CallingConvToStr[t.callConv] +{&} '.}');
end;
else begin
result := typeToStr[t.kind]

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