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. # Feel free to edit the templates as you need.
split.item.toc = "20" 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 # after this number of characters
doc.section = """ doc.section = """
@ -138,6 +138,10 @@ pre, span.tok {
color:black; color:black;
} }
span.red {
color: #A80000;
}
/* /*
:Author: David Goodger :Author: David Goodger
:Contact: goodger@python.org :Contact: goodger@python.org

View file

@ -33,6 +33,7 @@ path="$lib/extra"
debugger:off debugger:off
line_dir:off line_dir:off
opt:speed opt:speed
dead_code_elim:on
@end @end
# additional options always passed to the compiler: # additional options always passed to the compiler:
@ -51,12 +52,6 @@ hint[LineTooLong]=off
@end @end
# Configuration for the LLVM GCC compiler: # 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.debug = "-g"
llvm_gcc.options.always = "-w" llvm_gcc.options.always = "-w"
llvm_gcc.options.speed = "-O2" llvm_gcc.options.speed = "-O2"

View file

@ -1,6 +1,6 @@
======================================================= =======================================================
The Nimrod Compiler 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 This program is free software; you can redistribute it and/or

View file

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

View file

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

View file

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

View file

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

View file

@ -142,7 +142,7 @@
'selector must be of an ordinal type'}, 'selector must be of an ordinal type'},
{'errOrdXMustNotBeNegative': 'ord($1) must not be negative'}, {'errOrdXMustNotBeNegative': 'ord($1) must not be negative'},
{'errLenXinvalid': 'len($1) must be less than 32768'}, {'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'}, {'errExprCannotBeRaised': 'only objects can be raised'},
{'errBreakOnlyInLoop': "'break' only allowed in loop construct"}, {'errBreakOnlyInLoop': "'break' only allowed in loop construct"},
{'errTypeXhasUnknownSize': "type '$1' has unknown size"}, {'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: The documentation consists of several documents:
- | `First steps after installation <steps.html>`_ - | `Nimrod tutorial (part I) <tut1.html>`_
| Read this after installation for a quick introduction. | 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>`_ - | `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>`_ - | `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>`_ - | `User guide for the Embedded Nimrod Debugger <endb.html>`_
| This document describes how to use the Embedded debugger. The embedded | This document describes how to use the Embedded Debugger.
debugger currently has no GUI. Please help!
- | `Nimrod library documentation <lib.html>`_ - | `Nimrod library documentation <lib.html>`_
| This document describes Nimrod's standard library. | This document describes Nimrod's standard library.
- | `Nimrod internal documentation <intern.html>`_ - | `Nimrod internal documentation <intern.html>`_
| The internal documentation describes how the compiler is implemented. Read | 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>`_ - | `Index <theindex.html>`_
| The generated index. Often the quickest way to find the piece of | 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 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 lexbase buffer handling of the lexical analyser
scanner 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 ast type definitions of the abstract syntax tree (AST) and
node constructors node constructors
astalgo algorithms for containers of AST nodes; converting the 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 semexprs contains the semantic checking phase for expressions
semstmts contains the semantic checking phase for statements semstmts contains the semantic checking phase for statements
semtypes contains the semantic checking phase for types 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 idents implements a general mapping from identifiers to an internal
representation (``PIdent``) that is used, so that a simple representation (``PIdent``) that is used, so that a simple
id-comparison suffices to say whether two Nimrod identifiers id-comparison suffices to say whether two Nimrod identifiers
are equivalent are equivalent
ropes implements long strings represented as trees for
ropes implements long strings using represented as trees for
lazy evaluation; used mainly by the code generators lazy evaluation; used mainly by the code generators
ccgobj contains type definitions neeeded for C code generation transf transformations on the AST that need to be done before
and some helpers code generation
cgen main file of the C code generator
ccgutils contains helpers for the C code generator ccgutils contains helpers for the C code generator
ccgtypes the generator for C types ccgtypes the generator for C types
ccgstmts the generator for statements ccgstmts the generator for statements

View file

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

View file

@ -34,7 +34,6 @@ Path Purpose
on it! on it!
``web`` website of Nimrod; generated by ``koch.py`` ``web`` website of Nimrod; generated by ``koch.py``
from the ``*.txt`` and ``*.tmpl`` files from the ``*.txt`` and ``*.tmpl`` files
``koch`` the Koch Build System (written for Nimrod)
``obj`` generated ``*.obj`` files go into here ``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 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 that it can be translated to Nimrod code automatically. This conversion is
done by Nimrod itself via the undocumented ``boot`` command. Thus both Nimrod 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: Requirements for bootstrapping:
- Free Pascal (I used version 2.2) [optional] - Python (should work with version 1.5 or higher) (optional)
- Python (should work with version 1.5 or higher) - supported C compiler
- C compiler -- one of: Compiling the compiler is a simple matter of running::
* win32-lcc (currently broken) koch.py boot
* 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
| Compiling the compiler is a simple matter of running: For a release version use::
| ``koch.py boot``
| Or you can compile by hand, this is not difficult.
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 If you don't have Python, there is a ``boot`` Nimrod program which does roughly
been automated is accessible with it. It is a replacement for make and shell the same::
scripting with the advantage that it is more portable.
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 ``{@discard} <expr>``
that it can be translated to Nimrod code automatically. As a general rule, Tells the compiler that a ``discard`` statement is needed for Nimrod
Pascal code that does not translate to Nimrod automatically is forbidden. 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 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 The only case where things aren't as easy is when the garbage
collector needs some assembler tweaking to work. The standard collector needs some assembler tweaking to work. The standard
version of the GC uses C's ``setjmp`` function to store all registers 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. replace this generic code by some assembler code.
@ -132,11 +163,11 @@ The Garbage Collector
Introduction 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). (sequences, refs, strings).
This section describes how the new GC works. 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 References in the stack are not counted for better performance and easier C
code generation. code generation.
@ -170,7 +201,7 @@ modifying a ``TCellSet`` during traversation leads to undefined behaviour.
iterator elements(s: TCellSet): (elem: PCell) 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. 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* 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. It is essential to understand most of the compiler's code.
In order to compile Nimrod correctly, type-checking has to be seperated from 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 parsing. Otherwise generics would not work.
whole module only after it has been checked for semantics.
.. include:: filelist.txt .. 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 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 is needed to make life easier for the code generators. See the "ast" module
for the type definitions. 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])``. nodes. ``nodeKind[sons]`` is a short-cut for ``nodeKind([sons])``.
XXX: Description of the language's syntax and the corresponding trees. 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 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 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 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 module. However, calls to some procedures like ``addInt`` are inserted by
the CCG. Therefore the module ``magicsys`` contains a table the CCG. Therefore the module ``magicsys`` contains a table (``compilerprocs``)
(``compilerprocs``) with all symbols that are marked as ``compilerproc``. 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 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 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. 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 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. This document describes the lexis, the syntax, and the semantics of Nimrod.
The language constructs are explained using an extended BNF, in 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)?`` means an optional *a*; an alternative spelling for optional parts is
``[a]``. The ``|`` symbol is used to mark alternatives ``[a]``. The ``|`` symbol is used to mark alternatives
and has the lowest precedence. Parentheses may be used to group elements. and has the lowest precedence. Parentheses may be used to group elements.
Non-terminals are in lowercase, terminal symbols (including keywords) are in Non-terminals start with a lowercase letter, abstract terminal symbols are in
UPPERCASE. An example:: 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 Other parts of Nimrod - like scoping rules or runtime semantics are only
described in an informal manner. The reason is that formal semantics are 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`` The terminals ``IND`` (indentation), ``DED`` (dedentation) and ``SAD``
(same indentation) are generated by the scanner, denoting an indentation. (same indentation) are generated by the scanner, denoting an indentation.
These terminals are only generated for lines that are not empty or contain These terminals are only generated for lines that are not empty.
only whitespace and comments.
The parser and the scanner communicate over a stack which indentation terminal The parser and the scanner communicate over a stack which indentation terminal
should be generated: The stack consists of integers counting the spaces. The 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 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, 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 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 of the file, a ``DED`` token is generated for each number remaining on the
stack that is larger than zero. stack that is larger than zero.
Because the grammar contains some optional ``IND`` tokens, the scanner cannot 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 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 ``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 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 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 as they belong to the syntax tree! This feature enables perfect source-to-source
transformations (such as pretty-printing) and superior documentation generators. transformations (such as pretty-printing) and superior documentation generators.
A side-effect is that the human reader of the code always knows exactly which A nice side-effect is that the human reader of the code always knows exactly
code snippet the comment refers to. which code snippet the comment refers to.
Identifiers & Keywords 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 **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 this is that this allows programmers to use their own prefered spelling style
and libraries written by different programmers cannot use incompatible and libraries written by different programmers cannot use incompatible
conventions. The editors or IDE can show the identifiers as preferred. Another conventions. A Nimrod-aware editor or IDE can show the identifiers as
advantage is that it frees the programmer from remembering the exact spelling preferred. Another advantage is that it frees the programmer from remembering
of an identifier. the exact spelling of an identifier.
Literal strings Literal strings
@ -174,7 +181,7 @@ contain the following `escape sequences`:idx:\ :
Escape sequence Meaning Escape sequence Meaning
================== =================================================== ================== ===================================================
``\n`` `newline`:idx: ``\n`` `newline`:idx:
``\r`` `carriage return`:idx: ``\r``, ``\c`` `carriage return`:idx:
``\l`` `line feed`:idx: ``\l`` `line feed`:idx:
``\f`` `form feed`:idx: ``\f`` `form feed`:idx:
``\t`` `tabulator`:idx: ``\t`` `tabulator`:idx:
@ -184,8 +191,7 @@ contain the following `escape sequences`:idx:\ :
``\'`` `apostrophe`:idx: ``\'`` `apostrophe`:idx:
``\d+`` `character with decimal value d`:idx:; ``\d+`` `character with decimal value d`:idx:;
all decimal digits directly all decimal digits directly
following are used for the following are used for the character
character
``\a`` `alert`:idx: ``\a`` `alert`:idx:
``\b`` `backspace`:idx: ``\b`` `backspace`:idx:
``\e`` `escape`:idx: `[ESC]`: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 Strings in Nimrod may contain any 8-bit value, except embedded zeros.
which are not allowed for compability with `C`:idx:.
Literal strings can also be delimited by three double squotes Literal strings can also be delimited by three double squotes
``"""`` ... ``"""``. ``"""`` ... ``"""``.
Literals in this form may run for several lines, may contain ``"`` and do not Literals in this form may run for several lines, may contain ``"`` and do not
interpret any escape sequences. interpret any escape sequences.
For convenience, when the opening ``"""`` is immediately For convenience, when the opening ``"""`` is immediately followed by a newline,
followed by a newline, the newline is not included in the string. the newline is not included in the string.
There are also `raw string literals` that are preceded with the letter ``r`` 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 (or ``R``) and are delimited by matching double quotes (just like ordinary
string literals) and do not interpret the escape sequences. This is especially 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 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 for readability. Integer and floating point literals may be given in decimal (no
prefix), binary (prefix ``0b``), octal (prefix ``0o``) and prefix), binary (prefix ``0b``), octal (prefix ``0o``) and hexadecimal
hexadecimal (prefix ``0x``) notation. (prefix ``0x``) notation.
There exists a literal for each numerical type that is There exists a literal for each numerical type that is
defined. The suffix starting with an apostophe ('\'') is called a 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 unless the literal contains a dot or an ``E`` in which case it is of
type ``float``. type ``float``.
The following table specifies type suffixes: The type suffixes are:
================= ========================= ================= =========================
Type Suffix Resulting type of literal Type Suffix Resulting type of literal
@ -295,11 +300,11 @@ the three tokens `{`:tok:, `..`:tok:, `}`:tok: and not the two tokens
`{.`:tok:, `.}`:tok:. `{.`:tok:, `.}`:tok:.
In Nimrod one can define his own operators. An `operator`:idx: is any 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: These keywords are also operators:
``and or not xor shl shr div mod in notin is isnot``. ``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 cannot change. The compiler must be able to evaluate the expression in a
constant declaration at compile time. constant declaration at compile time.
.. Nimrod contains a sophisticated compile-time evaluator, so procedures which
Nimrod contains a sophisticated have no side-effect can be used in constant expressions too:
compile-time evaluator, so procedures declared with the ``{.noSideEffect.}``
pragma can be used in constant expressions:
.. code-block:: nimrod .. code-block:: nimrod
import strutils
from strutils import findSubStr
const const
x = findSubStr('a', "hallo") # x is 1; this is computed at compile time! constEval = contains("abc", 'b') # computed at compile time!
Types Types
@ -414,8 +416,8 @@ intXX
There are no `unsigned integer`:idx: types, only `unsigned operations`:idx: There are no `unsigned integer`:idx: types, only `unsigned operations`:idx:
that treat their arguments as unsigned. Unsigned operations all wrap around; that treat their arguments as unsigned. Unsigned operations all wrap around;
they may not lead to over- or underflow errors. Unsigned operations use the they cannot lead to over- or underflow errors. Unsigned operations use the
``%`` postfix as convention: ``%`` suffix as convention:
====================== ====================================================== ====================== ======================================================
operation meaning operation meaning
@ -498,16 +500,15 @@ the resulting programs will still handle UTF-8 properly as UTF-8 was specially
designed for this. designed for this.
Another reason is that Nimrod can support ``array[char, int]`` or Another reason is that Nimrod can support ``array[char, int]`` or
``set[char]`` efficiently as many algorithms rely on this feature. The ``set[char]`` efficiently as many algorithms rely on this feature. The
`TUniChar` type is used for Unicode characters, it can represent any Unicode `TRune` type is used for Unicode characters, it can represent any Unicode
character. ``TUniChar`` is declared the ``unicode`` standard module. character. ``TRune`` is declared the ``unicode`` module.
Enumeration types Enumeration types
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
`Enumeration`:idx: types define a new type whose values consist only of the ones `Enumeration`:idx: types define a new type whose values consist of the ones
specified. specified. The values are ordered. Example:
The values are ordered by the order in enum's declaration. Example:
.. code-block:: nimrod .. code-block:: nimrod
@ -528,8 +529,8 @@ with enumeration types.
For better interfacing to other programming languages, the fields of enum For better interfacing to other programming languages, the fields of enum
types can be assigned an explicit ordinal value. However, the ordinal values 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 have to be in ascending order. A field whose ordinal value is not
explicitly given, is assigned the value of the previous field + 1. explicitly given is assigned the value of the previous field + 1.
An explicit ordered enum can have *wholes*: An explicit ordered enum can have *wholes*:
@ -545,7 +546,7 @@ and ``pred`` are not available for them either.
Subrange types 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 type). To define a subrange type, one must specify it's limiting values: the
highest and lowest value of the type: 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 String type
~~~~~~~~~~~ ~~~~~~~~~~~
All string literals are of the type `string`:idx:. A string in Nimrod is very 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 zero-terminated and have a length field. One can retrieve the length with the
builtin ``len`` procedure; the length never counts the terminating zero. builtin ``len`` procedure; the length never counts the terminating zero.
The assignment operator for strings always copies the string. 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 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 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 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. 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 sequence operator ``@``. Another way to allocate space for a sequence is to
call the built-in ``newSeq`` procedure. call the built-in ``newSeq`` procedure.
A sequence may be passed to a parameter that is of type *open array*, but A sequence may be passed to a parameter that is of type *open array*.
not to a multi-dimensional open array, because it is impossible to do so in an
efficient manner.
Example: 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 checks can be disabled via pragmas or invoking the compiler with the
``--bound_checks:off`` command line switch. ``--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 Tuples and object types
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
@ -691,7 +708,7 @@ the ``is`` operator can be used to determine the object's type.
person: TPerson person: TPerson
assert(student is TStudent) # is true 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 marked by ``*``. In contrast to tuples, different object types are
never *equivalent*. never *equivalent*.
@ -904,9 +921,9 @@ Most calling conventions exist only for the Windows 32-bit platform.
Statements Statements and expressions
---------- --------------------------
Nimrod uses the common statement/expression paradigma: `Statements`:idx: do not Nimrod uses the common statement/expression paradigm: `Statements`:idx: do not
produce a value in contrast to expressions. Call expressions are statements. produce a value in contrast to expressions. Call expressions are statements.
If the called procedure returns a value, it is not a valid statement If the called procedure returns a value, it is not a valid statement
as statements do not produce values. To evaluate an expression for as statements do not produce values. To evaluate an expression for
@ -943,7 +960,7 @@ Discard statement
Syntax:: Syntax::
discardStmt ::= DISCARD expr discardStmt ::= 'discard' expr
Example: Example:
@ -962,11 +979,13 @@ Var statement
Syntax:: Syntax::
colonOrEquals ::= COLON typeDesc [EQUALS expr] | EQUALS expr colonOrEquals ::= ':' typeDesc ['=' expr] | '=' expr
varField ::= symbol ["*"] [pragma] varField ::= symbol ['*'] [pragma]
varPart ::= symbol (comma symbol)* [comma] colonOrEquals [COMMENT | IND COMMENT] varPart ::= symbol (comma symbol)* [comma] colonOrEquals [COMMENT | IND COMMENT]
varSection ::= VAR (varPart varSection ::= 'var' (varPart
| indPush (COMMENT|varPart) (SAD (COMMENT|varPart))* DED) | indPush (COMMENT|varPart)
(SAD (COMMENT|varPart))* DED indPop)
`Var`:idx: statements declare new local and global variables and `Var`:idx: statements declare new local and global variables and
initialize them. A comma seperated list of variables can be used to specify initialize them. A comma seperated list of variables can be used to specify
@ -992,7 +1011,7 @@ char '\0'
bool false bool false
ref or pointer type nil ref or pointer type nil
procedural type nil procedural type nil
sequence nil sequence nil (**not** ``@[]``)
string nil (**not** "") string nil (**not** "")
tuple[x: A, y: B, ...] (default(A), default(B), ...) tuple[x: A, y: B, ...] (default(A), default(B), ...)
(analogous for objects) (analogous for objects)
@ -1007,12 +1026,12 @@ Const section
Syntax:: Syntax::
colonAndEquals ::= [COLON typeDesc] EQUALS expr colonAndEquals ::= [':' typeDesc] '=' expr
constDecl ::= CONST
indPush constDecl ::= symbol ['*'] [pragma] colonAndEquals [COMMENT | IND COMMENT]
symbol ["*"] [pragma] colonAndEquals | COMMENT
(SAD symbol ["*"] [pragma] colonAndEquals)* constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop
DED
Example: Example:
@ -1031,7 +1050,7 @@ If statement
Syntax:: Syntax::
ifStmt ::= IF expr COLON stmt (ELIF expr COLON stmt)* [ELSE COLON stmt] ifStmt ::= 'if' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
Example: Example:
@ -1061,9 +1080,9 @@ Case statement
Syntax:: Syntax::
caseStmt ::= CASE expr (OF sliceList COLON stmt)* caseStmt ::= 'case' expr ('of' sliceExprList ':' stmt)*
(ELIF expr COLON stmt)* ('elif' expr ':' stmt)*
[ELSE COLON stmt] ['else' ':' stmt]
Example: Example:
@ -1094,7 +1113,7 @@ When statement
Syntax:: Syntax::
whenStmt ::= WHEN expr COLON stmt (ELIF expr COLON stmt)* [ELSE COLON stmt] whenStmt ::= 'when' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
Example: Example:
@ -1116,8 +1135,7 @@ exceptions:
* The statements do not open a new scope if they introduce new identifiers. * 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 * The statements that belong to the expression that evaluated to true are
translated by the compiler, the other statements are not checked for translated by the compiler, the other statements are not checked for
syntax or semantics at all! This holds also for any ``expr`` coming semantics! However, each ``expr`` is checked for semantics.
after the expression that evaluated to true.
The ``when`` statement enables conditional compilation techniques. As The ``when`` statement enables conditional compilation techniques. As
a special syntatic extension, the ``when`` construct is also available a special syntatic extension, the ``when`` construct is also available
@ -1129,7 +1147,7 @@ Raise statement
Syntax:: Syntax::
raiseStmt ::= RAISE [expr] raiseStmt ::= 'raise' [expr]
Example: Example:
@ -1152,11 +1170,11 @@ Try statement
Syntax:: Syntax::
qualifiedIdent ::= symbol ['.' symbol]
exceptList ::= [qualifiedIdent (comma qualifiedIdent)* [comma]] exceptList ::= [qualifiedIdent (comma qualifiedIdent)* [comma]]
tryStmt ::= TRY COLON stmt tryStmt ::= 'try' ':' stmt
(EXCEPT exceptList COLON stmt)* ('except' exceptList ':' stmt)*
[FINALLY COLON stmt] ['finally' ':' stmt]
Example: Example:
@ -1176,6 +1194,8 @@ Example:
echo("could not convert string to integer") echo("could not convert string to integer")
except EIO: except EIO:
echo("IO error!") echo("IO error!")
except:
echo("Unknown exception!")
finally: finally:
closeFile(f) closeFile(f)
@ -1203,7 +1223,7 @@ Return statement
Syntax:: Syntax::
returnStmt ::= RETURN [expr] returnStmt ::= 'return' [expr]
Example: 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:: variables, ``result`` is initialized to (binary) zero::
.. code-block:: nimrod .. code-block:: nimrod
proc returnZero(): int = nil # implicitely returns 0 proc returnZero(): int =
# implicitely returns 0
Yield statement Yield statement
@ -1232,7 +1253,7 @@ Yield statement
Syntax:: Syntax::
yieldStmt ::= YIELD expr yieldStmt ::= 'yield' expr
Example: Example:
@ -1252,7 +1273,7 @@ Block statement
Syntax:: Syntax::
blockStmt ::= BLOCK [symbol] COLON stmt blockStmt ::= 'block' [symbol] ':' stmt
Example: Example:
@ -1277,7 +1298,7 @@ Break statement
Syntax:: Syntax::
breakStmt ::= BREAK [symbol] breakStmt ::= 'break' [symbol]
Example: Example:
@ -1294,7 +1315,7 @@ While statement
Syntax:: Syntax::
whileStmt ::= WHILE expr COLON stmt whileStmt ::= 'while' expr ':' stmt
Example: Example:
@ -1316,7 +1337,7 @@ Continue statement
Syntax:: Syntax::
continueStmt ::= CONTINUE continueStmt ::= 'continue'
A `continue`:idx: statement leads to the immediate next iteration of the A `continue`:idx: statement leads to the immediate next iteration of the
surrounding loop construct. It is only allowed within a loop. A continue surrounding loop construct. It is only allowed within a loop. A continue
@ -1340,7 +1361,7 @@ Assembler statement
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
Syntax:: 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 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 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 ``'`'``. 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 Procedures
~~~~~~~~~~ ~~~~~~~~~~
What most programming languages call `methods`:idx: or `funtions`:idx: are 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 procedure declaration defines an identifier and associates it with a block
of code. A procedure may call itself recursively. The syntax is:: of code. A procedure may call itself recursively. The syntax is::
param ::= symbol (comma symbol)* [comma] COLON typeDesc param ::= symbol (comma symbol)* [comma] ':' typeDesc
paramList ::= [PAR_LE [param (comma param)* [comma]] PAR_RI] [COLON 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] procDecl ::= 'proc' symbol ['*'] [genericParams] paramList [pragma]
paramList [pragma] ['=' stmt]
[EQUALS 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 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 variable named `result`:idx: that represents the return value. Procs can be
overloaded. The overloading resolution algorithm tries to find the proc that is overloaded. The overloading resolution algorithm tries to find the proc that is
@ -1384,13 +1448,6 @@ is used if the caller does not provide a value for this parameter. Example:
for i in 0..len(s) - 1: for i in 0..len(s) - 1:
result[i] = toLower(s[i]) # calls toLower for characters; no recursion! result[i] = toLower(s[i]) # calls toLower for characters; no recursion!
`Operators`:idx: are procedures with a special operator symbol as identifier:
.. code-block:: nimrod
proc `$` (x: int): string =
# converts an integer to a string; this is a prefix operator.
return intToStr(x)
Calling a procedure can be done in many different ways: Calling a procedure can be done in many different ways:
.. code-block:: nimrod .. code-block:: nimrod
@ -1406,20 +1463,106 @@ Calling a procedure can be done in many different ways:
callme 0, 1, "abc", '\t' 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
proc `$` (x: int): string =
# converts an integer to a string; this is a prefix operator.
return intToStr(x)
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 `*+` (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
Iterators and the for statement Iterators and the for statement
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Syntax:: 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 param ::= symbol (comma symbol)* [comma] ':' typeDesc
paramList ::= [PAR_LE [param (comma param)* [comma]] PAR_RI] [COLON 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] iteratorDecl ::= 'iterator' symbol ['*'] [genericParams] paramList [pragma]
[EQUALS stmt] ['=' stmt]
The `for`:idx: statement is an abstract mechanism to iterate over the elements 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`` of a container. It relies on an `iterator`:idx: to do so. Like ``while``
@ -1473,13 +1616,15 @@ Type sections
Syntax:: Syntax::
typeDef ::= typeDesc | objectDef | enumDef typeDef ::= typeDesc | objectDef | enumDef
genericParams ::= BRACKET_LE (symbol [EQUALS typeDesc] )* BRACKET_RI
typeDecl ::= TYPE genericParam ::= symbol [':' typeDesc]
indPush genericParams ::= '[' genericParam (comma genericParam)* [comma] ']'
symbol ["*"] [genericParams] [EQUALS typeDef]
(SAD symbol ["*"] [genericParams] [EQUALS typeDef])* typeDecl ::= COMMENT
DED | symbol ['*'] [genericParams] ['=' typeDef] [COMMENT|IND COMMENT]
typeSection ::= 'type' indPush typeDecl (SAD typeDecl)* DED indPop
Example: Example:
@ -1504,6 +1649,8 @@ possible within a single ``type`` section.
Generics Generics
~~~~~~~~ ~~~~~~~~
`Version 0.7.4: Complex generic types like in the example do not work.`:red:
Example: Example:
.. code-block:: nimrod .. code-block:: nimrod
@ -1578,17 +1725,110 @@ Example:
# this definition exists in the System module # this definition exists in the System module
not (a == b) 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
~~~~~~ ------
`Macros`:idx: are the most powerful feature of Nimrod. They should be used `Macros`:idx: are the most powerful feature of Nimrod. They can be used
only to implement `domain specific languages`:idx:. They may lead to code 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. 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 Modules
@ -1622,12 +1862,12 @@ This is best illustrated by an example:
# Module B # Module B
import A # A is not parsed here! Only the already known symbols 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 proc p*(x: A.T1): A.T1 =
# this works because the compiler has already
# added T1 to A's interface symbol table # added T1 to A's interface symbol table
return x + 1
proc p(x: A.T1): A.T1 = return x + 1
Scope rules Scope rules
@ -1649,7 +1889,7 @@ procedure or iterator overloading purposes.
Tuple or object scope Tuple or object scope
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
The field identifiers inside a tuple or object definition are valid in the The field identifiers inside a tuple or object definition are valid in the
following places: following places:
@ -1659,16 +1899,14 @@ following places:
Module scope Module scope
~~~~~~~~~~~~ ~~~~~~~~~~~~
All identifiers in the interface part of a module are valid from the point of All identifiers of a module are valid from the point of declaration until
declaration, until the end of the module. Furthermore, the identifiers are the end of the module. Identifiers from indirectly dependent modules are *not*
known in other modules that import the module. Identifiers from indirectly available. The `system`:idx: module is automatically imported in every other
dependent modules are *not* available. The `system`:idx: module is automatically module.
imported in all other modules.
If a module imports an identifier by two different modules, If a module imports an identifier by two different modules, each occurance of
each occurance of the identifier has to be qualified, unless it is an the identifier has to be qualified, unless it is an overloaded procedure or
overloaded procedure or iterator in which case the overloading iterator in which case the overloading resolution takes place:
resolution takes place:
.. code-block:: nimrod .. code-block:: nimrod
# Module A # Module A
@ -1680,7 +1918,7 @@ resolution takes place:
# Module C # Module C
import A, B import A, B
write(stdout, x) # error: x is ambigious 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 var x = 4
write(stdout, x) # not ambigious: uses the module C's x write(stdout, x) # not ambigious: uses the module C's x
@ -1698,14 +1936,14 @@ Pragmas
Syntax:: Syntax::
colonExpr ::= expr [COLON expr] colonExpr ::= expr [':' expr]
colonExprList ::= [colonExpr (comma colonExpr)* [comma]] 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/ Pragmas are Nimrod's method to give the compiler additional information/
commands without introducing a massive number of new keywords. Pragmas are 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. special ``{.`` and ``.}`` curly brackets.
@ -1759,10 +1997,6 @@ compilation option pragmas
-------------------------- --------------------------
The listed pragmas here can be used to override the code generation options The listed pragmas here can be used to override the code generation options
for a section of code. for a section of code.
::
"{." pragma: val {pragma: val} ".}"
The implementation currently provides the following possible options (later The implementation currently provides the following possible options (later
various others may be added). 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 hints on|off Turns the hint messages of the compiler
on or off. on or off.
optimization none|speed|size Optimize the code for speed or size, or optimization none|speed|size Optimize the code for speed or size, or
disable optimization. For non-optimizing disable optimization.
compilers this option has no effect.
callconv cdecl|... Specifies the default calling convention for callconv cdecl|... Specifies the default calling convention for
all procedures (and procedure types) that all procedures (and procedure types) that
follow. follow.

View file

@ -12,7 +12,7 @@ Introduction
This document describes the usage of the *Nimrod compiler* This document describes the usage of the *Nimrod compiler*
on the different supported platforms. It is not a definition of the Nimrod 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 Nimrod is free software; it is licensed under the
`GNU General Public License <gpl.html>`_. `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 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. 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 Additional Features
=================== ===================
@ -128,7 +120,7 @@ the C code. Thus it makes the following possible, for example:
.. code-block:: Nimrod .. code-block:: Nimrod
var var
EOF {.importc: "EOF", no_decl.}: cint # pretend EOF was a variable, as EACCES {.importc, no_decl.}: cint # pretend EACCES was a variable, as
# Nimrod does not know its value # Nimrod does not know its value
However, the ``header`` pragma is often the better alternative. 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 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 Line_dir Option
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
The `line_dir`:idx: option can be turned on or off. If on the generated C code 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. 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 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 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 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 everything that can be done in C can be done in Nimrod.
assembler *might*.
Optimizing string handling 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 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 usable. The missing features can easily be circumvented. An indication of this
fact is that Nimrod's fact is that Nimrod's *whole* documentation itself (including this document) is
*whole* documentation itself (including this document) is
processed by Nimrod's |rst| parser. (Which is an order of magnitude faster than processed by Nimrod's |rst| parser. (Which is an order of magnitude faster than
Docutils' parser.) 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 ---------------------------------------- # --------------------- constants ----------------------------------------
NIMROD_VERSION = '0.7.2' NIMROD_VERSION = '0.7.4'
# This string contains Nimrod's version. It is the only place # This string contains Nimrod's version. It is the only place
# where the version needs to be updated. The rest is done by # where the version needs to be updated. The rest is done by
# the build process automatically. It is replaced **everywhere** # the build process automatically. It is replaced **everywhere**
@ -35,8 +35,6 @@ force = False
GENERATE_DIFF = False GENERATE_DIFF = False
# if set, a diff.log file is generated when bootstrapping # 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 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 Error(msg): sys.exit("[Koch] *** ERROR: " + msg)
def Warn(msg): print "[Koch] *** WARNING: " + msg def Warn(msg): print "[Koch] *** WARNING: " + msg
def Echo(msg): print "[Koch] " + msg def Echo(msg): print "[Koch] " + msg
@ -140,7 +113,7 @@ BaseDir = _baseDir
def Path(a): def Path(a):
# Gets a UNIX like path and converts it to a path on this platform. # Gets a UNIX like path and converts it to a path on this platform.
# With UNIX like, I mean: slashes, not backslashes, only relative # With UNIX like, I mean: slashes, not backslashes, only relative
# paths ('../etc' can be used) # paths ('../etc') can be used
result = a result = a
if os.sep != "/": result = replace(result, "/", os.sep) if os.sep != "/": result = replace(result, "/", os.sep)
if os.pardir != "..": result = replace(result, "..", os.pardir) if os.pardir != "..": result = replace(result, "..", os.pardir)
@ -401,12 +374,12 @@ Options:
Possible Commands: Possible Commands:
nim builds the Pascal version of Nimrod nim builds the Pascal version of Nimrod
rod [options] builds the Nimrod version of Nimrod (with options) rod [options] builds the Nimrod version of Nimrod (with options)
doc builds the documentation in HTML
clean cleans Nimrod project; removes generated files clean cleans Nimrod project; removes generated files
boot [options] bootstraps with given command line options boot [options] bootstraps with given command line options
rodsrc generates Nimrod version from Pascal version rodsrc generates Nimrod version from Pascal version
web generates the website web generates the website
profile profile the Nimrod compiler profile profile the Nimrod compiler
csource build the C sources for installation
zip build the installation ZIP package zip build the installation ZIP package
inno build the Inno Setup installer inno build the Inno Setup installer
""" % (NIMROD_VERSION + ' ' * (44-len(NIMROD_VERSION)), sys.version) """ % (NIMROD_VERSION + ' ' * (44-len(NIMROD_VERSION)), sys.version)
@ -436,7 +409,6 @@ def main(args):
cmd = args[i] cmd = args[i]
if cmd == "rod": cmd_rod(join(args[i+1:])) if cmd == "rod": cmd_rod(join(args[i+1:]))
elif cmd == "nim": cmd_nim() elif cmd == "nim": cmd_nim()
elif cmd == "doc": cmd_doc()
elif cmd == "clean": cmd_clean() elif cmd == "clean": cmd_clean()
elif cmd == "boot": cmd_boot(join(args[i+1:])) elif cmd == "boot": cmd_boot(join(args[i+1:]))
elif cmd == "rodsrc": cmd_rodsrc() elif cmd == "rodsrc": cmd_rodsrc()
@ -444,11 +416,14 @@ def main(args):
elif cmd == "profile": cmd_profile() elif cmd == "profile": cmd_profile()
elif cmd == "zip": cmd_zip() elif cmd == "zip": cmd_zip()
elif cmd == "inno": cmd_inno() elif cmd == "inno": cmd_inno()
elif cmd == "csource": cmd_csource()
else: Error("illegal command: " + cmd) else: Error("illegal command: " + cmd)
def cmd_zip(): def cmd_csource():
Exec("nimrod cc -r tools/niminst --var:version=%s csource rod/nimrod" % Exec("nimrod cc -r tools/niminst --var:version=%s csource rod/nimrod" %
NIMROD_VERSION) NIMROD_VERSION)
def cmd_zip():
Exec("nimrod cc -r tools/niminst --var:version=%s zip rod/nimrod" % Exec("nimrod cc -r tools/niminst --var:version=%s zip rod/nimrod" %
NIMROD_VERSION) NIMROD_VERSION)
@ -586,50 +561,10 @@ def cmd_profile():
# ------------------ web ------------------------------------------------------ # ------------------ 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(): def cmd_web():
Exec("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=%s" Exec("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=%s"
% NIMROD_VERSION) % 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(): def getVersion():

View file

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

View file

@ -281,7 +281,7 @@ proc getServerSoftware*(): string =
proc setTestData*(keysvalues: openarray[string]) = proc setTestData*(keysvalues: openarray[string]) =
## fills the appropriate environment variables to test your CGI application. ## 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: ## provide embedded (name, value)-pairs. Example:
## ##
## .. code-block:: Nimrod ## .. code-block:: Nimrod
@ -300,6 +300,7 @@ proc setTestData*(keysvalues: openarray[string]) =
proc writeContentType*() = proc writeContentType*() =
## call this before starting to send your HTML data to `stdout`. This ## call this before starting to send your HTML data to `stdout`. This
## is just a shorthand for: ## is just a shorthand for:
##
## .. code-block:: Nimrod ## .. code-block:: Nimrod
## write(stdout, "Content-type: text/html\n\n") ## write(stdout, "Content-type: text/html\n\n")
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 import
glib2 glib2

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -270,7 +270,8 @@
# #
# #
# {.deadCodeElim: on.}
when defined(windows): when defined(windows):
const SDLLibName = "SDL.dll" const SDLLibName = "SDL.dll"
elif defined(macosx): 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_LoadWAV*(filename: cstring): PMix_Chunk
proc Mix_LoadMUS*(filename: cstring): PMix_Music{.cdecl, proc Mix_LoadMUS*(filename: cstring): PMix_Music{.cdecl,
importc, dynlib: SDL_MixerLibName.} 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 # Load a wave file of the mixer format from a memory buffer
proc Mix_QuickLoad_WAV*(mem: PUint8): PMix_Chunk{.cdecl, proc Mix_QuickLoad_WAV*(mem: PUint8): PMix_Chunk{.cdecl,
importc, dynlib: SDL_MixerLibName.} importc, dynlib: SDL_MixerLibName.}

View file

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

View file

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

View file

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

View file

@ -13,11 +13,10 @@
# * incremental # * incremental
# * non-recursive # * non-recursive
# * generational # * generational
# * excellent performance
# Future Improvements: # Future Improvements:
# * Both dlmalloc and TLSF lack zero-overhead object allocation. Thus, for # * 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 # * Support for multi-threading. However, locks for the reference counting
# might turn out to be too slow. # might turn out to be too slow.
@ -332,7 +331,7 @@ proc CellSetPut(t: var TCellSet, key: TAddress): PPageDesc =
# ---------- slightly higher level procs -------------------------------------- # ---------- 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 u = cast[TAddress](cell)
var t = CellSetGet(s, u shr PageShift) var t = CellSetGet(s, u shr PageShift)
if t != nil: if t != nil:
@ -468,23 +467,6 @@ proc prepareDealloc(cell: PCell) =
proc setStackBottom(theStackBottom: pointer) {.compilerproc.} = proc setStackBottom(theStackBottom: pointer) {.compilerproc.} =
stackBottom = theStackBottom 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.} = proc PossibleRoot(gch: var TGcHeap, c: PCell) {.inline.} =
if canbeCycleRoot(c): incl(gch.cycleRoots, c) if canbeCycleRoot(c): incl(gch.cycleRoots, c)
@ -535,6 +517,23 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) =
if dest^ != nil: decRef(usrToCell(dest^)) if dest^ != nil: decRef(usrToCell(dest^))
dest^ = src 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 = proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
assert(n.kind == nkCase) assert(n.kind == nkCase)
var d: int var d: int

View file

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

View file

@ -31,6 +31,40 @@
# cog.out(toEnum(key, val)) # cog.out(toEnum(key, val))
#]]] #]]]
type 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 TNimrodTypeKind* = enum
ntyNone, ntyBool, ntyChar, ntyEmpty, ntyNone, ntyBool, ntyChar, ntyEmpty,
ntyArrayConstr, ntyNil, ntyGeneric, ntyGenericInst, ntyArrayConstr, ntyNil, ntyGeneric, ntyGenericInst,
@ -49,40 +83,6 @@ type
nskMacro, nskTemplate, nskField, nskEnumField, nskMacro, nskTemplate, nskField, nskEnumField,
nskForVar, nskModule, nskLabel, nskStub nskForVar, nskModule, nskLabel, nskStub
TNimSymKinds* = set[TNimrodSymKind] 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]]] #[[[end]]]
type type

View file

@ -421,18 +421,4 @@ struct NimException {
}; };
#endif #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 #endif

View file

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

View file

@ -139,7 +139,7 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
inc(c.bufpos) # skip '\' inc(c.bufpos) # skip '\'
case c.buf[c.bufpos] case c.buf[c.bufpos]
of 'n', 'N': of 'n', 'N':
tok.literal = tok.literal & nl add(tok.literal, nl)
Inc(c.bufpos) Inc(c.bufpos)
of 'r', 'R', 'c', 'C': of 'r', 'R', 'c', 'C':
add(tok.literal, '\c') add(tok.literal, '\c')

View file

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

View file

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

View file

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

View file

@ -91,10 +91,13 @@ proc writeln[Ty](f: TFile, x: Ty) =
write(f, "\n") write(f, "\n")
proc writeln[Ty](f: TFile, x: openArray[Ty]) = proc writeln[Ty](f: TFile, x: openArray[Ty]) =
write(f, x) for i in items(x): write(f, i)
write(f, "\n") write(f, "\n")
proc echo[Ty](x: Ty) = writeln(stdout, x) 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: # interface to the C procs:
proc fopen(filename, mode: CString): pointer {.importc: "fopen", noDecl.} 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 # we don't use refcounts because that's a behaviour
# the programmer may not want # 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: # implementation:
proc resize(old: int): int {.inline.} = 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.} proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.}
## Boolean not; returns true iff ``x == false``. ## 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".} proc new*[T](a: var ref T) {.magic: "New".}
## creates a new object of type ``T`` and returns a safe (traced) ## creates a new object of type ``T`` and returns a safe (traced)
## reference to it in ``a``. ## reference to it in ``a``.
@ -94,6 +105,19 @@ type
seq*{.magic: "Seq".}[T] ## Generic type to construct sequences. seq*{.magic: "Seq".}[T] ## Generic type to construct sequences.
set*{.magic: "Set".}[T] ## Generic type to construct bit sets. 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 Byte* = Int8 ## this is an alias for ``int8``, that is a signed
## int 8 bits wide. ## 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: int16): int16 {.magic: "AbsI", noSideEffect.}
proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.} proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.}
proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.} proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.}
## returns the absolute value of `x`. If `x` is ``low(x)`` (that is ## returns the absolute value of `x`. If `x` is ``low(x)`` (that
## -MININT for its type), an overflow exception is thrown (if overflow ## is -MININT for its type), an overflow exception is thrown (if overflow
## checking is turned on). ## checking is turned on).
proc min*(x, y: int): int {.magic: "MinI", noSideEffect.} 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 min*(x, y: float): float {.magic: "MinF64", noSideEffect.}
proc max*(x, y: float): float {.magic: "MaxF64", 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 # set operators
proc `*` *[T](x, y: set[T]): set[T] {.magic: "MulSet", noSideEffect.} proc `*` *[T](x, y: set[T]): set[T] {.magic: "MulSet", noSideEffect.}
## This operator computes the intersection of two sets. ## 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``. ## "is greater" operator. This is the same as ``y < x``.
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. ## One should overload this proc if one wants to overload the ``in`` operator.
## The parameters are in reverse order! This is because the unification ## The parameters are in reverse order! ``a in b`` is a template for
## algorithm that Nimrod uses for overload resolution works from left to ## ``contains(b, a)``.
## right. ## 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 ## But for the ``in`` operator that would be the wrong direction for this
## piece of code: ## 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 ## 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 ## 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 ## ``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. ## passes its arguments in reverse order.
template `in` * (x, y: expr): expr = in_Operator(y, x) template `in` * (x, y: expr): expr = contains(y, x)
template `not_in` * (x, y: expr): expr = not in_Operator(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.} proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
template `is_not` *(x, y: expr): expr = not (x is y) template `is_not` *(x, y: expr): expr = not (x is y)
@ -620,11 +633,13 @@ proc add * (x: var string, y: char) {.magic: "AppendStrCh".}
proc add*(x: var string, y: string) {.magic: "AppendStrStr".} proc add*(x: var string, y: string) {.magic: "AppendStrStr".}
when not defined(ECMAScript): when not defined(ECMAScript):
{.push overflow_checks:off}
proc add* (x: var string, y: cstring) = proc add* (x: var string, y: cstring) =
var i = 0 var i = 0
while y[i] != '\0': while y[i] != '\0':
add(x, y[i]) add(x, y[i])
inc(i) inc(i)
{.pop.}
else: else:
proc add* (x: var string, y: cstring) {.pure.} = proc add* (x: var string, y: cstring) {.pure.} =
asm """ 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.} proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.}
## takes any Nimrod variable and returns its string representation. It ## 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. ## debugging tool.
type type
@ -730,6 +745,15 @@ const
## is the endianness of the target CPU. This is a valuable piece of ## 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. ## 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 {. proc toFloat*(i: int): float {.
magic: "ToFloat", noSideEffect, importc: "toFloat".} magic: "ToFloat", noSideEffect, importc: "toFloat".}
## converts an integer `i` into a ``float``. If the conversion ## 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 ## as it is. This operator is useful for generic code, so
## that ``$expr`` also works if ``expr`` is already a string. ## 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: # undocumented:
proc getRefcount*[T](x: ref T): int {.importc: "getRefcount".} proc getRefcount*[T](x: ref T): int {.importc: "getRefcount".}
## retrieves the reference count of an heap-allocated object. The ## 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 ## equivalent to ``writeln(stdout, x); flush(stdout)``. BUT: This is
## available for the ECMAScript target too! ## 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 = template newException(exceptn, message: expr): expr =
block: # open a new scope 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): when not defined(EcmaScript) and not defined(NimrodVM):
include hti
proc initGC() proc initGC()
var var
@ -1370,13 +1402,6 @@ when not defined(EcmaScript) and not defined(NimrodVM):
include arithm include arithm
{.pop.} # stack trace {.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 const
GenericSeqSize = (2 * sizeof(int)) GenericSeqSize = (2 * sizeof(int))
@ -1417,13 +1442,12 @@ elif defined(NimrodVM):
proc getFreeMem(): int = return -1 proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1 proc getTotalMem(): int = return -1
proc echo[Ty](x: Ty) = nil proc echo[Ty](x: Ty) = nil
proc echo[Ty](x: openarray[Ty]) = nil
proc cmp(x, y: string): int = proc cmp(x, y: string): int =
if x == y: return 0 if x == y: return 0
if x < y: return -1 if x < y: return -1
return 1 return 1
include macros
{.pop.} # checks {.pop.} # checks
{.pop.} # hints {.pop.} # hints

View file

@ -276,8 +276,8 @@ else:
proc getDateStr(): string = proc getDateStr(): string =
var ti = getLocalTime(getTime()) var ti = getLocalTime(getTime())
result = $ti.year & "-" & intToStr(ord(ti.month)+1, 2) & result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) &
"-" & intToStr(ti.monthDay, 2) '-' & intToStr(ti.monthDay, 2)
proc getClockStr(): string = proc getClockStr(): string =
var ti = getLocalTime(getTime()) 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 ## Define ``winUnicode`` before importing this module for the
## unicode version. ## unicode version.
{.deadCodeElim: on.}
type type
ATOM* = int16 ATOM* = int16
TAtom* = ATOM TAtom* = ATOM

View file

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

View file

@ -66,12 +66,93 @@ for key, val in enums.items():
cog.out(b) cog.out(b)
]]]*) ]]]*)
type type
TTypeFlag = ( TNodeKind = (
tfVarargs, tfFinal, tfAcyclic, tfEnumHasWholes); nkNone, nkEmpty, nkIdent, nkSym,
TTypeFlags = set of TTypeFlag; 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 const
TypeFlagToStr: array [TTypeFlag] of string = ( NodeKindToStr: array [TNodeKind] of string = (
'tfVarargs', 'tfFinal', 'tfAcyclic', 'tfEnumHasWholes'); '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 type
TTypeKind = ( TTypeKind = (
tyNone, tyBool, tyChar, tyEmpty, tyNone, tyBool, tyChar, tyEmpty,
@ -95,25 +176,6 @@ const
'tyCString', 'tyForward', 'tyInt', 'tyInt8', 'tyCString', 'tyForward', 'tyInt', 'tyInt8',
'tyInt16', 'tyInt32', 'tyInt64', 'tyFloat', 'tyInt16', 'tyInt32', 'tyInt64', 'tyFloat',
'tyFloat32', 'tyFloat64', 'tyFloat128'); '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 type
TNodeFlag = ( TNodeFlag = (
nfNone, nfBase2, nfBase8, nfBase16, nfNone, nfBase2, nfBase8, nfBase16,
@ -123,6 +185,13 @@ const
NodeFlagToStr: array [TNodeFlag] of string = ( NodeFlagToStr: array [TNodeFlag] of string = (
'nfNone', 'nfBase2', 'nfBase8', 'nfBase16', 'nfNone', 'nfBase2', 'nfBase8', 'nfBase16',
'nfAllConst', 'nfTransf', 'nfSem'); 'nfAllConst', 'nfTransf', 'nfSem');
type
TTypeFlag = (
tfVarargs, tfFinal, tfAcyclic, tfEnumHasWholes);
TTypeFlags = set of TTypeFlag;
const
TypeFlagToStr: array [TTypeFlag] of string = (
'tfVarargs', 'tfFinal', 'tfAcyclic', 'tfEnumHasWholes');
type type
TSymKind = ( TSymKind = (
skUnknownSym, skConditional, skDynLib, skParam, skUnknownSym, skConditional, skDynLib, skParam,
@ -138,75 +207,6 @@ const
'skVar', 'skProc', 'skIterator', 'skConverter', 'skVar', 'skProc', 'skIterator', 'skConverter',
'skMacro', 'skTemplate', 'skField', 'skEnumField', 'skMacro', 'skTemplate', 'skField', 'skEnumField',
'skForVar', 'skModule', 'skLabel', 'skStub'); '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]]]} {[[[end]]]}
type type
@ -240,22 +240,23 @@ type
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16,
mToU32, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mToU32, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr,
mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr,
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mEnumToStr, mAnd, mOr, mEqStr, mLeStr, mLtStr,
mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet, mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
mConStrStr, mConArrArr, mConArrT, mConTArr, mConTT, mSlice, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mConTArr, mConTT,
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq, mInRange, mInSet, mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq, mInRange,
mAsgn, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mAssert, mInSet, mAsgn, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
mSwap, mIsNil, mArrToSeq, mArray, mOpenArray, mRange, mAssert, mSwap, mIsNil, mArrToSeq, mArray, mOpenArray,
mSet, mSeq, mInt, mInt8, mInt16, mInt32, mRange, mSet, mSeq, mInt, mInt8, mInt16,
mInt64, mFloat, mFloat32, mFloat64, mBool, mChar, mInt32, mInt64, mFloat, mFloat32, mFloat64, mBool,
mString, mCstring, mPointer, mAnyEnum, mEmptySet, mIntSetBaseType, mChar, mString, mCstring, mPointer, mAnyEnum, mEmptySet,
mNil, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor, mIntSetBaseType, mNil, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion,
mNimrodMinor, mNimrodPatch, mCpuEndian, mNaN, mInf, mNegInf, mNimrodMajor, mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU,
mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNaN, mInf, mNegInf, mNLen, mNChild, mNSetChild,
mNKind, mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNAdd, mNAddMultiple, mNDel, mNKind, mNIntVal, mNFloatVal,
mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal,
mNSetStrVal, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNNewNimNode, mNCopyNimNode,
mEqIdent, mNHint, mNWarning, mNError mNCopyNimTree, mStrToIdent, mIdentToStr, mEqIdent, mNHint, mNWarning,
mNError
//[[[end]]] //[[[end]]]
); );
@ -322,7 +323,6 @@ type
locProc, // location is a proc (an address of a procedure) locProc, // location is a proc (an address of a procedure)
locData, // location is a constant locData, // location is a constant
locCall, // location is a call expression locCall, // location is a call expression
locImmediate, // location is an immediate value
locOther // location is something other locOther // location is something other
); );
@ -498,22 +498,23 @@ const // "MagicToStr" array:
'Ze16ToI', 'Ze16ToI64', 'Ze32ToI64', 'ZeIToI64', 'ToU8', 'ToU16', 'Ze16ToI', 'Ze16ToI64', 'Ze32ToI64', 'ZeIToI64', 'ToU8', 'ToU16',
'ToU32', 'ToFloat', 'ToBiggestFloat', 'ToInt', 'ToBiggestInt', 'CharToStr', 'ToU32', 'ToFloat', 'ToBiggestFloat', 'ToInt', 'ToBiggestInt', 'CharToStr',
'BoolToStr', 'IntToStr', 'Int64ToStr', 'FloatToStr', 'CStrToStr', 'StrToStr', 'BoolToStr', 'IntToStr', 'Int64ToStr', 'FloatToStr', 'CStrToStr', 'StrToStr',
'And', 'Or', 'EqStr', 'LeStr', 'LtStr', 'EqSet', 'EnumToStr', 'And', 'Or', 'EqStr', 'LeStr', 'LtStr',
'LeSet', 'LtSet', 'MulSet', 'PlusSet', 'MinusSet', 'SymDiffSet', 'EqSet', 'LeSet', 'LtSet', 'MulSet', 'PlusSet', 'MinusSet',
'ConStrStr', 'ConArrArr', 'ConArrT', 'ConTArr', 'ConTT', 'Slice', 'SymDiffSet', 'ConStrStr', 'ConArrArr', 'ConArrT', 'ConTArr', 'ConTT',
'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'AppendSeqSeq', 'InRange', 'InSet', 'Slice', 'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'AppendSeqSeq', 'InRange',
'Asgn', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq', 'Assert', 'InSet', 'Asgn', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq',
'Swap', 'IsNil', 'ArrToSeq', 'Array', 'OpenArray', 'Range', 'Assert', 'Swap', 'IsNil', 'ArrToSeq', 'Array', 'OpenArray',
'Set', 'Seq', 'Int', 'Int8', 'Int16', 'Int32', 'Range', 'Set', 'Seq', 'Int', 'Int8', 'Int16',
'Int64', 'Float', 'Float32', 'Float64', 'Bool', 'Char', 'Int32', 'Int64', 'Float', 'Float32', 'Float64', 'Bool',
'String', 'Cstring', 'Pointer', 'AnyEnum', 'EmptySet', 'IntSetBaseType', 'Char', 'String', 'Cstring', 'Pointer', 'AnyEnum', 'EmptySet',
'Nil', 'IsMainModule', 'CompileDate', 'CompileTime', 'NimrodVersion', 'NimrodMajor', 'IntSetBaseType', 'Nil', 'IsMainModule', 'CompileDate', 'CompileTime', 'NimrodVersion',
'NimrodMinor', 'NimrodPatch', 'CpuEndian', 'NaN', 'Inf', 'NegInf', 'NimrodMajor', 'NimrodMinor', 'NimrodPatch', 'CpuEndian', 'HostOS', 'HostCPU',
'NLen', 'NChild', 'NSetChild', 'NAdd', 'NAddMultiple', 'NDel', 'NaN', 'Inf', 'NegInf', 'NLen', 'NChild', 'NSetChild',
'NKind', 'NIntVal', 'NFloatVal', 'NSymbol', 'NIdent', 'NGetType', 'NAdd', 'NAddMultiple', 'NDel', 'NKind', 'NIntVal', 'NFloatVal',
'NStrVal', 'NSetIntVal', 'NSetFloatVal', 'NSetSymbol', 'NSetIdent', 'NSetType', 'NSymbol', 'NIdent', 'NGetType', 'NStrVal', 'NSetIntVal', 'NSetFloatVal',
'NSetStrVal', 'NNewNimNode', 'NCopyNimNode', 'NCopyNimTree', 'StrToIdent', 'IdentToStr', 'NSetSymbol', 'NSetIdent', 'NSetType', 'NSetStrVal', 'NNewNimNode', 'NCopyNimNode',
'EqIdent', 'NHint', 'NWarning', 'NError' 'NCopyNimTree', 'StrToIdent', 'IdentToStr', 'EqIdent', 'NHint', 'NWarning',
'NError'
//[[[end]]] //[[[end]]]
); );
@ -1342,13 +1343,13 @@ end;
function IntSetContains(const s: TIntSet; key: int): bool; function IntSetContains(const s: TIntSet; key: int): bool;
var var
u: int; u: TBitScalar;
t: PTrunk; t: PTrunk;
begin begin
t := IntSetGet(s, key shr TrunkShift); t := IntSetGet(s, shru(key, TrunkShift));
if t <> nil then begin if t <> nil then begin
u := key and TrunkMask; 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 end
else else
result := false result := false
@ -1356,27 +1357,27 @@ end;
procedure IntSetIncl(var s: TIntSet; key: int); procedure IntSetIncl(var s: TIntSet; key: int);
var var
u: int; u: TBitScalar;
t: PTrunk; t: PTrunk;
begin begin
t := IntSetPut(s, key shr TrunkShift); t := IntSetPut(s, shru(key, TrunkShift));
u := key and TrunkMask; u := key and TrunkMask;
t.bits[u shr IntShift] := t.bits[u shr IntShift] t.bits[shru(u, IntShift)] := t.bits[shru(u, IntShift)]
or (1 shl (u and IntMask)); or shlu(1, u and IntMask);
end; end;
function IntSetContainsOrIncl(var s: TIntSet; key: int): bool; function IntSetContainsOrIncl(var s: TIntSet; key: int): bool;
var var
u: int; u: TBitScalar;
t: PTrunk; t: PTrunk;
begin begin
t := IntSetGet(s, key shr TrunkShift); t := IntSetGet(s, shru(key, TrunkShift));
if t <> nil then begin if t <> nil then begin
u := key and TrunkMask; 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 if not result then
t.bits[u shr IntShift] := t.bits[u shr IntShift] t.bits[shru(u, IntShift)] := t.bits[shru(u, IntShift)]
or (1 shl (u and IntMask)); or shlu(1, u and IntMask);
end end
else begin else begin
IntSetIncl(s, key); IntSetIncl(s, key);

View file

@ -456,23 +456,21 @@ begin
[istr, lineInfoToStr(n.info)]); [istr, lineInfoToStr(n.info)]);
case n.kind of case n.kind of
nkCharLit..nkInt64Lit: 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: nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, '$n$1"floatVal": $2', appf(result, ',$n$1"floatVal": $2', [istr, toRopeF(n.floatVal)]);
[istr, toRopeF(n.floatVal)]);
nkStrLit..nkTripleStrLit: nkStrLit..nkTripleStrLit:
appf(result, '$n$1"strVal": $2', appf(result, ',$n$1"strVal": $2', [istr, makeYamlString(n.strVal)]);
[istr, makeYamlString(n.strVal)]);
nkSym: nkSym:
appf(result, ',$n$1"sym": $2', appf(result, ',$n$1"sym": $2',
[istr, symToYamlAux(n.sym, marker, indent+2, maxRecDepth)]); [istr, symToYamlAux(n.sym, marker, indent+2, maxRecDepth)]);
nkIdent: begin nkIdent: begin
if n.ident <> nil then if n.ident <> nil then
appf(result, '$n$1"ident": $2', appf(result, ',$n$1"ident": $2',
[istr, makeYamlString(n.ident.s)]) [istr, makeYamlString(n.ident.s)])
else else
appf(result, '$n$1"ident": null', [istr]) appf(result, ',$n$1"ident": null', [istr])
end end
else begin else begin
if sonsLen(n) > 0 then begin if sonsLen(n) > 0 then begin
@ -552,12 +550,12 @@ begin
if maxRecDepth <> 0 then begin if maxRecDepth <> 0 then begin
case n.kind of case n.kind of
nkCharLit..nkInt64Lit: 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: nkFloatLit, nkFloat32Lit, nkFloat64Lit:
appf(result, '$n$1"floatVal": $2', appf(result, ',$n$1"floatVal": $2',
[istr, toRopeF(n.floatVal)]); [istr, toRopeF(n.floatVal)]);
nkStrLit..nkTripleStrLit: nkStrLit..nkTripleStrLit:
appf(result, '$n$1"strVal": $2', appf(result, ',$n$1"strVal": $2',
[istr, makeYamlString(n.strVal)]); [istr, makeYamlString(n.strVal)]);
nkSym: nkSym:
appf(result, ',$n$1"sym": $2_$3', appf(result, ',$n$1"sym": $2_$3',
@ -565,10 +563,10 @@ begin
nkIdent: begin nkIdent: begin
if n.ident <> nil then if n.ident <> nil then
appf(result, '$n$1"ident": $2', appf(result, ',$n$1"ident": $2',
[istr, makeYamlString(n.ident.s)]) [istr, makeYamlString(n.ident.s)])
else else
appf(result, '$n$1"ident": null', [istr]) appf(result, ',$n$1"ident": null', [istr])
end end
else begin else begin
if sonsLen(n) > 0 then begin if sonsLen(n) > 0 then begin

View file

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

View file

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

View file

@ -8,7 +8,7 @@
// //
//var //var
// newDummyVar: int; // just to check the rodgen mechanism // newDummyVar: int; // just to check the symbol file mechanism
// ------------------------- Name Mangling -------------------------------- // ------------------------- Name Mangling --------------------------------
@ -461,6 +461,22 @@ begin
app(result, '};' + tnl); app(result, '};' + tnl);
end; 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); procedure pushType(m: BModule; typ: PType);
var var
L: int; L: int;
@ -571,13 +587,15 @@ begin
if result = nil then begin if result = nil then begin
result := getTypeName(t); result := getTypeName(t);
if not isImportedType(t) then if not isImportedType(t) then
appf(m.s[cfsForwardTypes], appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]);
getForwardStructFormat(), [result]);
IdTablePut(m.forwTypeCache, t, result) IdTablePut(m.forwTypeCache, t, result)
end; end;
IdTablePut(m.typeCache, t, result); IdTablePut(m.typeCache, t, result);
// always call for sideeffects: // 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); if not isImportedType(t) then app(m.s[cfsTypes], recdesc);
end; end;
tySet: begin tySet: begin
@ -735,9 +753,11 @@ begin
assert(n.sons[0].kind = nkSym); assert(n.sons[0].kind = nkSym);
field := n.sons[0].sym; field := n.sons[0].sym;
tmp := getTempName(); tmp := getTempName();
useMagic(m, 'chckNil');
appf(m.s[cfsTypeInit3], '$1.kind = 3;$n' + appf(m.s[cfsTypeInit3], '$1.kind = 3;$n' +
'$1.offset = offsetof($2, $3);$n' + '$1.offset = offsetof($2, $3);$n' +
'$1.typ = $4;$n' + '$1.typ = $4;$n' +
'chckNil($1.typ);$n' +
'$1.name = $5;$n' + '$1.name = $5;$n' +
'$1.sons = &$6[0];$n' + '$1.sons = &$6[0];$n' +
'$1.len = $7;$n', '$1.len = $7;$n',
@ -781,9 +801,11 @@ begin
end; end;
nkSym: begin nkSym: begin
field := n.sym; field := n.sym;
useMagic(m, 'chckNil');
appf(m.s[cfsTypeInit3], '$1.kind = 1;$n' + appf(m.s[cfsTypeInit3], '$1.kind = 1;$n' +
'$1.offset = offsetof($2, $3);$n' + '$1.offset = offsetof($2, $3);$n' +
'$1.typ = $4;$n' + '$1.typ = $4;$n' +
'chckNil($1.typ);$n' +
'$1.name = $5;$n', '$1.name = $5;$n',
[expr, getTypeDesc(m, typ), field.loc.r, [expr, getTypeDesc(m, typ), field.loc.r,
genTypeInfo(m, field.typ), genTypeInfo(m, field.typ),
@ -804,6 +826,41 @@ begin
appf(m.s[cfsTypeInit3], '$1->node = &$2;$n', [name, tmp]); appf(m.s[cfsTypeInit3], '$1->node = &$2;$n', [name, tmp]);
end; 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); procedure genEnumInfo(m: BModule; typ: PType; name: PRope);
var var
nodePtrs, elemNode, enumNames, enumArray, counter, specialCases: PRope; nodePtrs, elemNode, enumNames, enumArray, counter, specialCases: PRope;
@ -817,7 +874,8 @@ begin
genTypeInfoAux(m, typ, name); genTypeInfoAux(m, typ, name);
nodePtrs := getTempName(); nodePtrs := getTempName();
len := sonsLen(typ.n); 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; enumNames := nil;
specialCases := nil; specialCases := nil;
firstNimNode := m.typeNodes; firstNimNode := m.typeNodes;
@ -879,61 +937,55 @@ function genTypeInfo(m: BModule; typ: PType): PRope;
var var
t: PType; t: PType;
id: int; id: int;
dataGen: bool; dataGenerated: bool;
begin begin
t := getUniqueType(typ); t := getUniqueType(typ);
id := IiTableGet(gToTypeInfoId, t.id); id := IiTableGet(gToTypeInfoId, t.id);
if id = invalidKey then begin if id = invalidKey then begin
dataGen := false; dataGenerated := false;
case t.kind of id := t.id; // getID();
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;
IiTablePut(gToTypeInfoId, t.id, id); IiTablePut(gToTypeInfoId, t.id, id);
end end
else else
dataGen := true; dataGenerated := true;
result := ropef('NTI$1', [toRope(id)]); 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: // declare type information structures:
useMagic(m, 'TNimType'); useMagic(m, 'TNimType');
useMagic(m, 'TNimNode'); useMagic(m, 'TNimNode');
if dataGen then
appf(m.s[cfsVars], 'extern TNimType* $1; /* $2 */$n', appf(m.s[cfsVars], 'extern TNimType* $1; /* $2 */$n',
[result, toRope(typeToString(t))]); [result, toRope(typeToString(t))]);
end; end;
if dataGen then exit; if dataGenerated then exit;
case t.kind of case t.kind of
tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyPointer, tyProc, tyBool, tyChar, tyCString, tyString,
tyInt..tyFloat128, tyVar: tyInt..tyFloat128, tyVar:
genTypeInfoAuxBase(m, t, result, toRope('0'+'')); genTypeInfoAuxBase(gmti, t, result, toRope('0'+''));
tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(m, t, result); tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(gmti, t, result);
tyArrayConstr, tyArray: genArrayInfo(m, t, result); tyArrayConstr, tyArray: genArrayInfo(gmti, t, result);
tySet: genSetInfo(m, t, result); tySet: genSetInfo(gmti, t, result);
tyEnum: genEnumInfo(m, t, result); tyEnum: genEnumInfo(gmti, t, result);
tyObject, tyTuple: genObjectInfo(m, 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] + ')'); else InternalError('genTypeInfo(' + typekindToStr[t.kind] + ')');
end end
end; end;
procedure genTypeSection(m: BModule; n: PNode);
begin
end;
(*
procedure genTypeSection(m: BModule; n: PNode); procedure genTypeSection(m: BModule; n: PNode);
var var
i: int; i: int;
a: PNode; a: PNode;
t: PType; t: PType;
begin begin
if not (optDeadCodeElim in gGlobalOptions) then begin
for i := 0 to sonsLen(n)-1 do begin for i := 0 to sonsLen(n)-1 do begin
a := n.sons[i]; a := n.sons[i];
if a.kind = nkCommentStmt then continue; if a.kind = nkCommentStmt then continue;
@ -958,4 +1010,6 @@ begin
else begin end else begin end
end end
end end
end
end; end;
*)

View file

@ -27,16 +27,48 @@ function GetUniqueType(key: PType): PType;
implementation implementation
var 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; function GetUniqueType(key: PType): PType;
var var
t: PType; t: PType;
h: THash; h: THash;
k: TTypeKind;
begin begin
// this was a hotspot in the compiler! // this is a hotspot in the compiler!
result := key; result := key;
if key = nil then exit; 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 case key.Kind of
tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
tyInt..tyFloat128, tyProc, tyAnyEnum: begin end; tyInt..tyFloat128, tyProc, tyAnyEnum: begin end;
@ -62,7 +94,7 @@ begin
end end
end; end;
tyGenericInst: result := GetUniqueType(lastSon(key)); tyGenericInst: result := GetUniqueType(lastSon(key));
end; end; *)
end; end;
function TableGetType(const tab: TIdTable; key: PType): PObject; function TableGetType(const tab: TIdTable; key: PType): PObject;
@ -122,6 +154,6 @@ begin
app(result, toRope(res)); app(result, toRope(res));
end; end;
initialization begin
InitIdTable(gTypeTable); InitTypeTables();
end. end.

View file

@ -73,7 +73,6 @@ type
cpsStmts // section of local statements for C proc cpsStmts // section of local statements for C proc
); );
TCProcSections = array [TCProcSection] of PRope; TCProcSections = array [TCProcSection] of PRope;
// TCProcSections represents a generated C proc // TCProcSections represents a generated C proc
@ -112,12 +111,13 @@ type
typeCache: TIdTable; // cache the generated types typeCache: TIdTable; // cache the generated types
forwTypeCache: TIdTable; // cache for forward declarations of types forwTypeCache: TIdTable; // cache for forward declarations of types
declaredThings: TIntSet; // things we have declared in this .c file 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 headerFiles: TLinkedList; // needed headers to include
typeInfoMarker: TIntSet; // needed for generating type information typeInfoMarker: TIntSet; // needed for generating type information
initProc: BProc; // code for init procedure initProc: BProc; // code for init procedure
typeStack: TTypeSeq; // used for type generation typeStack: TTypeSeq; // used for type generation
dataCache: TNodeTable; dataCache: TNodeTable;
forwardedProcs: TSymSeq; // keep forwarded procs here
typeNodes, nimTypes: int;// used for type info generation 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; end;
@ -126,7 +126,49 @@ var
mainModProcs, mainModInit: PRope; // parts of the main module mainModProcs, mainModInit: PRope; // parts of the main module
gMapping: PRope; // the generated mapping file (if requested) gMapping: PRope; // the generated mapping file (if requested)
gProcProfile: Natural; // proc profile counter 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); procedure initLoc(var result: TLoc; k: TLocKind; typ: PType; s: TStorageLoc);
begin begin
@ -209,25 +251,12 @@ end;
// -------------------------- Variable manager ---------------------------- // -------------------------- 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); procedure assignLocalVar(p: BProc; s: PSym);
begin begin
//assert(s.loc.k == locNone) // not yet assigned //assert(s.loc.k == locNone) // not yet assigned
// this need not be fullfilled for inline procs; they are regenerated // this need not be fullfilled for inline procs; they are regenerated
// for each module that uses them! // for each module that uses them!
if s.loc.k = locNone then
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack); fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack);
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t)); app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t));
if sfRegister in s.flags then if sfRegister in s.flags then
@ -248,11 +277,19 @@ end;
procedure assignGlobalVar(m: BModule; s: PSym); procedure assignGlobalVar(m: BModule; s: PSym);
begin begin
if s.loc.k = locNone then
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap); fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap);
useHeader(m, s); useHeader(m, s);
if lfNoDecl in s.loc.flags then exit; if lfNoDecl in s.loc.flags then exit;
if sfImportc in s.flags then app(m.s[cfsVars], 'extern '); 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 = if [optStackTrace, optEndb] * m.module.options =
[optStackTrace, optEndb] then begin [optStackTrace, optEndb] then begin
useMagic(m, 'dbgRegisterGlobal'); useMagic(m, 'dbgRegisterGlobal');
@ -282,6 +319,12 @@ begin
end end
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 ------------------------------- // -------------------------- label manager -------------------------------
// note that a label is a location too // note that a label is a location too
@ -296,11 +339,11 @@ begin
appf(p.s[cpsStmts], '$1: ;$n', [labl]) appf(p.s[cpsStmts], '$1: ;$n', [labl])
end; end;
procedure genProcPrototype(m: BModule; sym: PSym); forward;
procedure genVarPrototype(m: BModule; sym: PSym); forward; procedure genVarPrototype(m: BModule; sym: PSym); forward;
procedure genConstPrototype(m: BModule; sym: PSym); forward; procedure genConstPrototype(m: BModule; sym: PSym); forward;
procedure genProc(m: BModule; prc: PSym); forward; procedure genProc(m: BModule; prc: PSym); forward;
procedure genStmts(p: BProc; t: PNode); forward; procedure genStmts(p: BProc; t: PNode); forward;
procedure genProcPrototype(m: BModule; sym: PSym); forward;
{$include 'ccgexprs.pas'} {$include 'ccgexprs.pas'}
{$include 'ccgstmts.pas'} {$include 'ccgstmts.pas'}
@ -343,11 +386,11 @@ begin
tmp := ropef('Dl_$1', [toRope(sym.id)]); tmp := ropef('Dl_$1', [toRope(sym.id)]);
sym.loc.r := tmp; // from now on we only need the internal name sym.loc.r := tmp; // from now on we only need the internal name
sym.typ.sym := nil; // generate a new name sym.typ.sym := nil; // generate a new name
appf(m.s[cfsDynLibInit], appf(m.s[cfsDynLibInit], '$1 = ($2) nimGetProcAddr($3, $4);$n',
'$1 = ($2) nimGetProcAddr($3, $4);$n', [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString(ropeToStr(extname))]);
[tmp, getTypeDesc(m, sym.typ), lib.name,
makeCString(ropeToStr(extname))]); app(m.s[cfsVars], getTypeDesc(m, sym.loc.t));
declareGlobalVar(m, sym) appf(m.s[cfsVars], ' $1;$n', [sym.loc.r]);
end; end;
// ----------------------------- sections --------------------------------- // ----------------------------- sections ---------------------------------
@ -356,16 +399,16 @@ procedure UseMagic(m: BModule; const name: string);
var var
sym: PSym; sym: PSym;
begin 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); sym := magicsys.getCompilerProc(name);
if sym <> nil then
case sym.kind of case sym.kind of
skProc, skConverter: genProcPrototype(m, sym); skProc, skConverter: genProc(m, sym);
skVar: genVarPrototype(m, sym); skVar: genVarPrototype(m, sym);
skType: {@discard} getTypeDesc(m, sym.typ); skType: {@discard} getTypeDesc(m, sym.typ);
else InternalError('useMagic: ' + name) else InternalError('useMagic: ' + name)
end end
else if not (sfSystemModule in m.module.flags) then
rawMessage(errSystemNeeds, name); // don't be too picky here
end; end;
procedure generateHeaders(m: BModule); procedure generateHeaders(m: BModule);
@ -405,27 +448,15 @@ begin
result := (s.typ.sons[0] <> nil) and not isInvalidReturnType(s.typ.sons[0]) result := (s.typ.sons[0] <> nil) and not isInvalidReturnType(s.typ.sons[0])
end; end;
procedure genProc(m: BModule; prc: PSym); procedure genProcAux(m: BModule; prc: PSym);
var var
p: BProc; p: BProc;
generatedProc, header, returnStmt: PRope; generatedProc, header, returnStmt: PRope;
i, profileId: int; i: int;
res, param: PSym; res, param: PSym;
begin 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); p := newProc(prc, m);
header := genProcHeader(m, prc); 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; returnStmt := nil;
assert(prc.ast <> nil); assert(prc.ast <> nil);
@ -493,7 +524,55 @@ begin
app(generatedProc, '}' + tnl); app(generatedProc, '}' + tnl);
end; end;
app(m.s[cfsProcs], generatedProc); 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
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; end;
procedure genVarPrototype(m: BModule; sym: PSym); procedure genVarPrototype(m: BModule; sym: PSym);
@ -522,6 +601,7 @@ end;
procedure genConstPrototype(m: BModule; sym: PSym); procedure genConstPrototype(m: BModule; sym: PSym);
begin begin
useHeader(m, sym); useHeader(m, sym);
if sym.loc.k = locNone then
fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnUnknown); fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnUnknown);
if (lfNoDecl in sym.loc.Flags) or if (lfNoDecl in sym.loc.Flags) or
intSetContainsOrIncl(m.declaredThings, sym.id) then intSetContainsOrIncl(m.declaredThings, sym.id) then
@ -535,32 +615,6 @@ begin
end end
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; function getFileHeader(const cfilenoext: string): PRope;
begin begin
if optCompileOnly in gGlobalOptions then if optCompileOnly in gGlobalOptions then
@ -593,6 +647,7 @@ procedure genMainProc(m: BModule);
const const
CommonMainBody = CommonMainBody =
' setStackBottom(dummy);$n' + ' setStackBottom(dummy);$n' +
' nim__datInit();$n' +
' systemInit();$n' + ' systemInit();$n' +
'$1' + '$1' +
'$2'; '$2';
@ -652,8 +707,7 @@ var
initname: PRope; initname: PRope;
begin begin
initname := getInitName(m); initname := getInitName(m);
appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', [initname]);
[initname]);
if not (sfSystemModule in m.flags) then if not (sfSystemModule in m.flags) then
appf(mainModInit, '$1();$n', [initname]); appf(mainModInit, '$1();$n', [initname]);
end; end;
@ -669,14 +723,18 @@ begin
{@discard} lists.IncludeStr(m.headerFiles, '<cycle.h>'); {@discard} lists.IncludeStr(m.headerFiles, '<cycle.h>');
end; end;
initname := getInitName(m.module); initname := getInitName(m.module);
registerModuleToMain(m.module);
prc := ropef('N_NOINLINE(void, $1)(void) {$n', [initname]); 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', appf(m.s[cfsTypeInit1], 'static TNimNode $1[$2];$n',
[m.typeNodesName, toRope(m.typeNodes)]); [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', appf(m.s[cfsTypeInit1], 'static TNimType $1[$2];$n',
[m.nimTypesName, toRope(m.nimTypes)]); [m.nimTypesName, toRope(m.nimTypes)]);
end;
if optStackTrace in m.initProc.options then begin if optStackTrace in m.initProc.options then begin
getFrameDecl(m.initProc); getFrameDecl(m.initProc);
app(prc, m.initProc.s[cpsLocals]); app(prc, m.initProc.s[cpsLocals]);
@ -716,7 +774,7 @@ begin
for i := low(TCFileSection) to cfsProcs do app(result, m.s[i]) for i := low(TCFileSection) to cfsProcs do app(result, m.s[i])
end; end;
function newModule(module: PSym; const filename: string): BModule; function rawNewModule(module: PSym; const filename: string): BModule;
begin begin
new(result); new(result);
{@ignore} {@ignore}
@ -724,7 +782,7 @@ begin
{@emit} {@emit}
InitLinkedList(result.headerFiles); InitLinkedList(result.headerFiles);
intSetInit(result.declaredThings); intSetInit(result.declaredThings);
intSetInit(result.debugDeclared); intSetInit(result.declaredProtos);
result.cfilename := filename; result.cfilename := filename;
result.filename := filename; result.filename := filename;
initIdTable(result.typeCache); initIdTable(result.typeCache);
@ -735,12 +793,36 @@ begin
result.initProc.options := gOptions; result.initProc.options := gOptions;
initNodeTable(result.dataCache); initNodeTable(result.dataCache);
{@emit result.typeStack := @[];} {@emit result.typeStack := @[];}
{@emit result.forwardedProcs := @[];}
result.typeNodesName := getTempName(); result.typeNodesName := getTempName();
result.nimTypesName := getTempName(); result.nimTypesName := getTempName();
end; 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; function myOpen(module: PSym; const filename: string): PPassContext;
begin begin
if gmti = nil then registerTypeInfoModule();
result := newModule(module, filename); result := newModule(module, filename);
end; end;
@ -749,6 +831,7 @@ function myOpenCached(module: PSym; const filename: string;
var var
cfile, cfilenoext, objFile: string; cfile, cfilenoext, objFile: string;
begin begin
if gmti = nil then registerTypeInfoModule();
//MessageOut('cgen.myOpenCached has been called ' + filename); //MessageOut('cgen.myOpenCached has been called ' + filename);
cfile := changeFileExt(completeCFilePath(filename), cExt); cfile := changeFileExt(completeCFilePath(filename), cExt);
cfilenoext := changeFileExt(cfile, ''); cfilenoext := changeFileExt(cfile, '');
@ -761,6 +844,8 @@ begin
end; *) end; *)
addFileToLink(cfilenoext); addFileToLink(cfilenoext);
registerModuleToMain(module); registerModuleToMain(module);
// XXX: this cannot be right here, initalization has to be appended during
// the ``myClose`` call
result := nil; result := nil;
end; end;
@ -790,11 +875,52 @@ begin
genStmts(m.initProc, n); genStmts(m.initProc, n);
end; 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 var
cfile, cfilenoext: string; cfile, cfilenoext: string;
m: BModule;
code: PRope; 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 begin
result := n; result := n;
if b = nil then exit; if b = nil then exit;
@ -803,22 +929,24 @@ begin
m.initProc.options := gOptions; m.initProc.options := gOptions;
genStmts(m.initProc, n); genStmts(m.initProc, n);
end; end;
// generate code for the init statements of the module: registerModuleToMain(m.module);
genInitCode(m); if not (optDeadCodeElim in gGlobalOptions) and
finishTypeDescriptions(m); not (sfDeadCodeElim in m.module.flags) then
cfile := completeCFilePath(m.cfilename); finishModule(m);
cfilenoext := changeFileExt(cfile, '');
if sfMainModule in m.module.flags then begin if sfMainModule in m.module.flags then begin
// generate main file:
app(m.s[cfsProcHeaders], mainModProcs);
genMainProc(m); 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; end;
code := genModule(m, cfilenoext); if not (optDeadCodeElim in gGlobalOptions) and
if shouldRecompile(code, changeFileExt(cfile, cExt), cfilenoext) then begin not (sfDeadCodeElim in m.module.flags) then
addFileToCompile(cfilenoext); // is to compile writeModule(m);
end;
addFileToLink(cfilenoext);
if sfMainModule in m.module.flags then writeMapping(cfile, gMapping);
end; end;
function cgenPass(): TPass; function cgenPass(): TPass;
@ -832,4 +960,5 @@ end;
initialization initialization
InitIiTable(gToTypeInfoId); InitIiTable(gToTypeInfoId);
IntSetInit(gGeneratedSyms);
end. end.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -61,7 +61,7 @@ begin
result := x -{%} 1; result := x -{%} 1;
// complicated, to make it a nop if sizeof(int) == 4, // complicated, to make it a nop if sizeof(int) == 4,
// because shifting more than 31 bits is undefined in C // 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 16);
result := result or (result shr 8); result := result or (result shr 8);
result := result or (result shr 4); 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 getColNumber(const L: TBaseLexer; pos: int): int;
function HandleCR(var 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 // position to continue the scanning from. `pos` must be the position
// of the CR. // of the CR.
@ -211,8 +211,7 @@ end;
function getColNumber(const L: TBaseLexer; pos: int): int; function getColNumber(const L: TBaseLexer; pos: int): int;
begin begin
result := pos - L.lineStart; result := abs(pos - L.lineStart);
assert(result >= 0);
end; end;
function getCurrentLine(const L: TBaseLexer; marker: boolean = true): string; function getCurrentLine(const L: TBaseLexer; marker: boolean = true): string;

View file

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

View file

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

View file

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

View file

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

View file

@ -149,8 +149,12 @@ function subU(a, b: biggestInt): biggestInt;
function mulU(a, b: biggestInt): biggestInt; function mulU(a, b: biggestInt): biggestInt;
function divU(a, b: biggestInt): biggestInt; function divU(a, b: biggestInt): biggestInt;
function modU(a, b: biggestInt): biggestInt; function modU(a, b: biggestInt): biggestInt;
function shlU(a, b: biggestInt): biggestInt; function shlU(a, b: biggestInt): biggestInt; overload;
function shrU(a, b: biggestInt): biggestInt; 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 ltU(a, b: biggestInt): bool;
function leU(a, b: biggestInt): bool; function leU(a, b: biggestInt): bool;
@ -281,6 +285,16 @@ begin
result := biggestInt(biggestUInt(a) shr biggestUInt(b)); result := biggestInt(biggestUInt(a) shr biggestUInt(b));
end; 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; function ltU(a, b: biggestInt): bool;
begin begin
result := biggestUInt(a) < biggestUInt(b); result := biggestUInt(a) < biggestUInt(b);

View file

@ -31,10 +31,10 @@ const
//cog.outl('VersionMinor = %s;' % ver[1]) //cog.outl('VersionMinor = %s;' % ver[1])
//cog.outl('VersionPatch = %s;' % ver[2]) //cog.outl('VersionPatch = %s;' % ver[2])
//]]] //]]]
VersionAsString = '0.7.2'; VersionAsString = '0.7.4';
VersionMajor = 0; VersionMajor = 0;
VersionMinor = 7; VersionMinor = 7;
VersionPatch = 2; VersionPatch = 4;
//[[[[end]]]] //[[[[end]]]]
implementation 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 if startsWith(dir, prefix) then begin
result := ncopy(dir, length(prefix) + strStart); exit result := ncopy(dir, length(prefix) + strStart); exit
end; 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 result := dir
end; end;
@ -209,7 +215,7 @@ var
head, tail: string; head, tail: string;
begin begin
splitPath(path, head, tail); splitPath(path, head, tail);
result := joinPath([projectPath, genSubDir, shortenDir(head), result := joinPath([projectPath, genSubDir, shortenDir(head +{&} dirSep),
changeFileExt(tail, ext)]) changeFileExt(tail, ext)])
end; end;
@ -219,9 +225,11 @@ var
head, tail, subdir: string; head, tail, subdir: string;
begin begin
splitPath(f, head, tail); splitPath(f, head, tail);
subdir := joinPath([projectPath, genSubDir, shortenDir(head)]); subdir := joinPath([projectPath, genSubDir, shortenDir(head +{&} dirSep)]);
if createSubDir then if createSubDir then begin
//Writeln(output, subdir);
createDir(subdir); createDir(subdir);
end;
result := joinPath(subdir, tail) result := joinPath(subdir, tail)
end; end;

View file

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

View file

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

View file

@ -23,7 +23,7 @@ uses
type type
TSystemOS = ( 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). // condsyms (end of module).
osNone, osNone,
osDos, osDos,

View file

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

View file

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

View file

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

View file

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

View file

@ -14,7 +14,7 @@ unit ropes;
efficiently; especially concatenation is done in O(1) instead of O(N). efficiently; especially concatenation is done in O(1) instead of O(N).
Ropes make use a lazy evaluation: They are essentially concatenation Ropes make use a lazy evaluation: They are essentially concatenation
trees that are only flattened when converting to a native Nimrod 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. nil pointer.
A little picture makes everything clear: A little picture makes everything clear:
@ -541,7 +541,6 @@ begin
if (r.data <> snil) then begin if (r.data <> snil) then begin
if r.len > bufSize then if r.len > bufSize then
// A token bigger than 1 KB? - This cannot happen in reality. // 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'); internalError('ropes: token too long');
readBytes := readBuffer(bin, buf, r.len); readBytes := readBuffer(bin, buf, r.len);
result := (readBytes = r.len) // BUGFIX result := (readBytes = r.len) // BUGFIX

View file

@ -494,7 +494,13 @@ begin
if n.kind = rnLeaf then begin if n.kind = rnLeaf then begin
for i := strStart to length(n.text)+strStart-1 do begin for i := strStart to length(n.text)+strStart-1 do begin
case n.text[i] of 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; if b then begin addChar(r, '-'); b := false; end;
addChar(r, n.text[i]) addChar(r, n.text[i])
end; end;
@ -1235,7 +1241,7 @@ begin
result := nil; result := nil;
if (p.tok[p.idx].kind = tkIndent) if (p.tok[p.idx].kind = tkIndent)
and (p.tok[p.idx+1].symbol = ':'+'') then begin 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); result := newRstNode(rnFieldList);
inc(p.idx); inc(p.idx);
while true do begin while true do begin
@ -1252,10 +1258,10 @@ var
i: int; i: int;
f: PRstNode; f: PRstNode;
begin begin
assert(n.kind = rnDirective);
result := ''; result := '';
if n.sons[1] = nil then exit; 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 for i := 0 to rsonsLen(n.sons[1])-1 do begin
f := n.sons[1].sons[i]; f := n.sons[1].sons[i];
if cmpIgnoreStyle(addNodes(f.sons[0]), fieldname) = 0 then begin if cmpIgnoreStyle(addNodes(f.sons[0]), fieldname) = 0 then begin

View file

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

View file

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

View file

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

View file

@ -94,6 +94,25 @@ begin
end end
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; function evalOp(m: TMagic; n, a, b: PNode): PNode;
// if this is an unary operation, b is nil // if this is an unary operation, b is nil
begin begin
@ -246,11 +265,18 @@ begin
// available for interpretation. I don't know how to fix this. // available for interpretation. I don't know how to fix this.
//result := newStrNodeT(renderTree(a, {@set}[renderNoComments]), n); //result := newStrNodeT(renderTree(a, {@set}[renderNoComments]), n);
end; end;
mIntToStr, mInt64ToStr, mBoolToStr, mCharToStr: mIntToStr, mInt64ToStr:
result := newStrNodeT(toString(getOrdValue(a)), n); 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); mFloatToStr: result := newStrNodeT(toStringF(getFloat(a)), n);
mCStrToStr: result := newStrNodeT(getStrOrChar(a), n); mCStrToStr, mCharToStr: result := newStrNodeT(getStrOrChar(a), n);
mStrToStr: result := a; mStrToStr: result := a;
mEnumToStr: result := newStrNodeT(enumValToString(a), n);
mArrToSeq: begin mArrToSeq: begin
result := copyTree(a); result := copyTree(a);
result.typ := n.typ; result.typ := n.typ;
@ -370,6 +396,10 @@ begin
mNimrodMinor: result := newIntNodeT(VersionMinor, n); mNimrodMinor: result := newIntNodeT(VersionMinor, n);
mNimrodPatch: result := newIntNodeT(VersionPatch, n); mNimrodPatch: result := newIntNodeT(VersionPatch, n);
mCpuEndian: result := newIntNodeT(ord(CPU[targetCPU].endian), 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); mNaN: result := newFloatNodeT(NaN, n);
mInf: result := newFloatNodeT(Inf, n); mInf: result := newFloatNodeT(Inf, n);
mNegInf: result := newFloatNodeT(NegInf, n); mNegInf: result := newFloatNodeT(NegInf, n);

View file

@ -324,7 +324,7 @@ begin
a := newNodeI(nkAsgn, n.sons[0].info); a := newNodeI(nkAsgn, n.sons[0].info);
n.sons[0] := fitNode(c, restype, n.sons[0]); 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``: // to ``result = result; return``:
if (n.sons[0].kind = nkSym) and (sfResult in n.sons[0].sym.flags) then if (n.sons[0].kind = nkSym) and (sfResult in n.sons[0].sym.flags) then
begin begin
@ -378,14 +378,14 @@ function semVar(c: PContext; n: PNode): PNode;
var var
i, j, len: int; i, j, len: int;
a, b, def: PNode; a, b, def: PNode;
typ: PType; typ, tup: PType;
v: PSym; v: PSym;
begin begin
result := copyNode(n); result := copyNode(n);
for i := 0 to sonsLen(n)-1 do begin for i := 0 to sonsLen(n)-1 do begin
a := n.sons[i]; a := n.sons[i];
if a.kind = nkCommentStmt then continue; 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); checkMinSonsLen(a, 3);
len := sonsLen(a); len := sonsLen(a);
if a.sons[len-2] <> nil then if a.sons[len-2] <> nil then
@ -401,14 +401,21 @@ begin
end end
else else
def := nil; 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 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]); v := semIdentWithPragma(c, skVar, a.sons[j], {@set}[sfStar, sfMinus]);
include(v.flags, sfGlobal); include(v.flags, sfGlobal);
end end
else else
v := semIdentWithPragma(c, skVar, a.sons[j], {@set}[]); 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 if v.flags * [sfStar, sfMinus] <> {@set}[] then
include(v.flags, sfInInterface); include(v.flags, sfInInterface);
addInterfaceDecl(c, v); addInterfaceDecl(c, v);
@ -443,7 +450,7 @@ begin
if a.sons[1] <> nil then typ := semTypeNode(c, a.sons[1], nil) if a.sons[1] <> nil then typ := semTypeNode(c, a.sons[1], nil)
else typ := nil; else typ := nil;
def := semConstExpr(c, a.sons[2]); def := semAndEvalConstExpr(c, a.sons[2]);
// check type compability between def.typ and typ: // check type compability between def.typ and typ:
if (typ <> nil) then begin if (typ <> nil) then begin
def := fitRemoveHiddenConv(c, typ, def); def := fitRemoveHiddenConv(c, typ, def);
@ -495,7 +502,7 @@ begin
iter := skipGeneric(n.sons[len-2].typ); iter := skipGeneric(n.sons[len-2].typ);
openScope(c.tab); openScope(c.tab);
if iter.kind <> tyTuple then begin 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 := newSymS(skForVar, n.sons[0], c);
v.typ := iter; v.typ := iter;
n.sons[0] := newSymNode(v); n.sons[0] := newSymNode(v);
@ -503,7 +510,7 @@ begin
end end
else begin else begin
if len-2 <> sonsLen(iter) then if len-2 <> sonsLen(iter) then
liMessage(n.info, errWrongNumberOfLoopVariables); liMessage(n.info, errWrongNumberOfVariables);
for i := 0 to len-3 do begin for i := 0 to len-3 do begin
v := newSymS(skForVar, n.sons[i], c); v := newSymS(skForVar, n.sons[i], c);
v.typ := iter.sons[i]; v.typ := iter.sons[i];
@ -840,6 +847,7 @@ begin
closeScope(c.tab); // close scope for parameters closeScope(c.tab); // close scope for parameters
popOwner(); popOwner();
c.p := oldP; // restore c.p := oldP; // restore
result.typ := s.typ;
end; end;
function semProcAux(c: PContext; n: PNode; kind: TSymKind): PNode; 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; if templ = nil then begin result := nil; exit end;
case templ.kind of case templ.kind of
nkSym: begin nkSym: begin
if (templ.sym.kind = skTypeParam) if (templ.sym.kind = skTypeParam) then
and (templ.sym.owner.id = sym.id) then //and (templ.sym.owner.id = sym.id) then
result := copyTree(actual.sons[templ.sym.position+1]) result := copyTree(actual.sons[templ.sym.position+1])
else else
result := copyNode(templ) result := copyNode(templ)
@ -285,15 +285,18 @@ begin
result.containerID := s.typ.containerID; // ... but the same containerID result.containerID := s.typ.containerID; // ... but the same containerID
result.sym := s; result.sym := s;
if (s.typ.containerID = 0) then if (s.typ.containerID = 0) then
InternalError(n.info, 'semGeneric'); InternalError(n.info, 'semtypes.semGeneric');
for i := 1 to sonsLen(n)-1 do begin for i := 1 to sonsLen(n)-1 do begin
elem := semTypeNode(c, n.sons[i], nil); 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); addSon(result, elem);
end; end;
if s.ast <> nil then begin if s.ast <> nil then begin
if (result.kind = tyGenericInst) then begin
inst := instGenericAux(c, s.ast.sons[2], n, s); inst := instGenericAux(c, s.ast.sons[2], n, s);
if result.kind = tyGenericInst then begin internalError(n.info, 'Generic containers not implemented');
// XXX: implementation does not work this way
// does checking of instantiated type for us: // does checking of instantiated type for us:
elem := semTypeNode(c, inst, nil); elem := semTypeNode(c, inst, nil);
elem.id := result.containerID; elem.id := result.containerID;
@ -304,6 +307,8 @@ begin
end end
else else
liMessage(n.info, errCannotInstantiateX, s.name.s); liMessage(n.info, errCannotInstantiateX, s.name.s);
(*if computeSize(result) < 0 then
liMessage(s.info, errIllegalRecursionInTypeX, s.name.s);*)
end; end;
function semIdentVis(c: PContext; kind: TSymKind; n: PNode; function semIdentVis(c: PContext; kind: TSymKind; n: PNode;

View file

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

View file

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

View file

@ -75,13 +75,14 @@ const
function strip(const s: string; const chars: TCharSet = WhiteSpace): string; function strip(const s: string; const chars: TCharSet = WhiteSpace): string;
function allCharsInSet(const s: string; const theSet: TCharSet): bool; function allCharsInSet(const s: string; const theSet: TCharSet): bool;
function quoteIfSpaceExists(const s: string): string; function quoteIfContainsWhite(const s: string): string;
implementation implementation
function quoteIfSpaceExists(const s: string): string; function quoteIfContainsWhite(const s: string): string;
begin begin
if (findSubStr(' ', s) >= strStart) and (s[strStart] <> '"') then if ((findSubStr(' ', s) >= strStart)
or (findSubStr(#9, s) >= strStart)) and (s[strStart] <> '"') then
result := '"' +{&} s +{&} '"' result := '"' +{&} s +{&} '"'
else else
result := s 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 uses
sysutils, nsystem, charsets, strutils, sysutils, nsystem, charsets, strutils,
lists, options, ast, astalgo, trees, treetab, lists, options, ast, astalgo, trees, treetab,
msgs, nos, idents, rnimsyn, types, passes, semfold; msgs, nos, idents, rnimsyn, types, passes, semfold, magicsys;
const const
genPrefix = ':tmp'; // prefix for generated names genPrefix = ':tmp'; // prefix for generated names
@ -139,7 +139,7 @@ More efficient, but not implementable:
function newAsgnStmt(c: PTransf; le, ri: PNode): PNode; function newAsgnStmt(c: PTransf; le, ri: PNode): PNode;
begin begin
result := newNodeI(nkAsgn, ri.info); result := newNodeI(nkFastAsgn, ri.info);
addSon(result, le); addSon(result, le);
addSon(result, ri); addSon(result, ri);
end; end;
@ -224,6 +224,28 @@ begin
end end
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; function transformYield(c: PTransf; n: PNode): PNode;
var var
e: PNode; e: PNode;
@ -239,10 +261,8 @@ begin
transform(c, copyTree(e.sons[i])))); transform(c, copyTree(e.sons[i]))));
end end
end end
else begin else
// XXX: tuple unpacking: unpackTuple(c, e, result);
internalError(n.info, 'tuple unpacking is not implemented');
end
end end
else begin else begin
e := transform(c, copyTree(e)); e := transform(c, copyTree(e));
@ -523,7 +543,7 @@ end;
(* (*
# example: # example:
proc map(f: proc (x: int): int {.closure}, a: seq[int]): seq[int] = proc map(f: proc (x: int): int {.closure}, a: seq[int]): seq[int] =
result = [] result = @[]
for elem in a: for elem in a:
add result, f(a) add result, f(a)
@ -534,12 +554,12 @@ end;
proc map(f: proc(x: int): int, closure: pointer, proc map(f: proc(x: int): int, closure: pointer,
a: seq[int]): seq[int] = a: seq[int]): seq[int] =
result = [] result = @[]
for elem in a: for elem in a:
add result, f(a, closure) add result, f(a, closure)
type type
PMyClosure = ref record PMyClosure = ref object
y: var int y: var int
proc myLambda(x: int, closure: pointer) = proc myLambda(x: int, closure: pointer) =

View file

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

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