version 0.7.4
This commit is contained in:
parent
1c8ddca7e0
commit
439aa2d04d
114 changed files with 13664 additions and 10110 deletions
|
|
@ -3,7 +3,7 @@
|
|||
# Feel free to edit the templates as you need.
|
||||
|
||||
split.item.toc = "20"
|
||||
# too long entries in the table of contents get truncated
|
||||
# too long entries in the table of contents wrap around
|
||||
# after this number of characters
|
||||
|
||||
doc.section = """
|
||||
|
|
@ -138,6 +138,10 @@ pre, span.tok {
|
|||
color:black;
|
||||
}
|
||||
|
||||
span.red {
|
||||
color: #A80000;
|
||||
}
|
||||
|
||||
/*
|
||||
:Author: David Goodger
|
||||
:Contact: goodger@python.org
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ path="$lib/extra"
|
|||
debugger:off
|
||||
line_dir:off
|
||||
opt:speed
|
||||
dead_code_elim:on
|
||||
@end
|
||||
|
||||
# additional options always passed to the compiler:
|
||||
|
|
@ -51,12 +52,6 @@ hint[LineTooLong]=off
|
|||
@end
|
||||
|
||||
# Configuration for the LLVM GCC compiler:
|
||||
@if windows:
|
||||
llvm_gcc.path = r"$nimrod\dist\llvm-gcc4.2\bin"
|
||||
@elif macosx:
|
||||
llvm_gcc.path =
|
||||
r"/Users/andreasrumpf/download/C/llvm-gcc4.2-2.3-x86-darwin8/bin"
|
||||
@end
|
||||
llvm_gcc.options.debug = "-g"
|
||||
llvm_gcc.options.always = "-w"
|
||||
llvm_gcc.options.speed = "-O2"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
=======================================================
|
||||
The Nimrod Compiler
|
||||
Copyright (C) 2004-2008 Andreas Rumpf
|
||||
Copyright (C) 2004-2009 Andreas Rumpf
|
||||
=======================================================
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Advanced commands::
|
||||
pas convert a Pascal file to Nimrod standard syntax
|
||||
pas convert a Pascal file to Nimrod syntax
|
||||
pretty pretty print the inputfile
|
||||
gen_depend generate a DOT file containing the
|
||||
module dependency graph
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
'sfCompileTime', # proc can be evaluated at compile time
|
||||
'sfThreadVar', # variable is a thread variable
|
||||
'sfMerge', # proc can be merged with itself
|
||||
'sfDeadCodeElim', # dead code elimination for the module is turned on
|
||||
],
|
||||
|
||||
'TypeFlag': [
|
||||
|
|
@ -116,7 +117,7 @@
|
|||
# end of atoms
|
||||
'nkDotCall', # used to temporarily flag a nkCall node; this is used
|
||||
# for transforming ``s.len`` to ``len(s)``
|
||||
'nkCommand', # a call like ``p 2 4`` without parenthesis
|
||||
'nkCommand', # a call like ``p 2, 4`` without parenthesis
|
||||
'nkCall', # a call like p(x, y) or an operation like +(a, b)
|
||||
'nkGenericCall', # a call with given type parameters
|
||||
'nkExplicitTypeListCall', # a call with given explicit typelist
|
||||
|
|
@ -125,6 +126,7 @@
|
|||
'nkIdentDefs', # a definition like `a, b: typeDesc = expr`
|
||||
# either typeDesc or expr may be nil; used in
|
||||
# formal parameters, var statements, etc.
|
||||
'nkVarTuple', # a ``var (a, b) = expr`` construct
|
||||
'nkInfix', # a call like (a + b)
|
||||
'nkPrefix', # a call like !a
|
||||
'nkPostfix', # something like a! (also used for visibility)
|
||||
|
|
@ -166,6 +168,7 @@
|
|||
# end of expressions
|
||||
|
||||
'nkAsgn', # a = b
|
||||
'nkFastAsgn', # internal node for a fast ``a = b`` (no string copy)
|
||||
'nkDefaultTypeParam', # `ident = typeDesc` in generic parameters
|
||||
'nkGenericParams', # generic parameters
|
||||
'nkFormalParams', # formal parameters
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ Options:
|
|||
--bound_checks:on|off code generation for bound checks ON|OFF
|
||||
--overflow_checks:on|off code generation for over-/underflow checks ON|OFF
|
||||
-a, --assertions:on|off code generation for assertions ON|OFF
|
||||
--dead_code_elim:on|off whole program dead code elimination ON|OFF
|
||||
--opt:none|speed|size optimize not at all or for speed|size
|
||||
--app:console|gui|lib generate a console|GUI application or a shared lib
|
||||
-r, --run run the compiled program with given arguments
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@
|
|||
'FloatToStr',
|
||||
'CStrToStr',
|
||||
'StrToStr',
|
||||
'EnumToStr',
|
||||
|
||||
# special ones:
|
||||
'And',
|
||||
|
|
@ -209,6 +210,8 @@
|
|||
'NimrodMinor',
|
||||
'NimrodPatch',
|
||||
'CpuEndian',
|
||||
'HostOS',
|
||||
'HostCPU',
|
||||
'NaN',
|
||||
'Inf',
|
||||
'NegInf',
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@
|
|||
'selector must be of an ordinal type'},
|
||||
{'errOrdXMustNotBeNegative': 'ord($1) must not be negative'},
|
||||
{'errLenXinvalid': 'len($1) must be less than 32768'},
|
||||
{'errWrongNumberOfLoopVariables': 'wrong number of loop variables'},
|
||||
{'errWrongNumberOfVariables': 'wrong number of variables'},
|
||||
{'errExprCannotBeRaised': 'only objects can be raised'},
|
||||
{'errBreakOnlyInLoop': "'break' only allowed in loop construct"},
|
||||
{'errTypeXhasUnknownSize': "type '$1' has unknown size"},
|
||||
|
|
|
|||
38
deinstall.sh
38
deinstall.sh
|
|
@ -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
|
||||
|
||||
17
doc/docs.txt
17
doc/docs.txt
|
|
@ -3,25 +3,28 @@
|
|||
|
||||
The documentation consists of several documents:
|
||||
|
||||
- | `First steps after installation <steps.html>`_
|
||||
| Read this after installation for a quick introduction.
|
||||
- | `Nimrod tutorial (part I) <tut1.html>`_
|
||||
| The Nimrod tutorial part one deals with the basics.
|
||||
|
||||
- | `Nimrod tutorial (part II) <tut2.html>`_
|
||||
| The Nimrod tutorial part two deals with the advanced language constructs.
|
||||
|
||||
- | `Nimrod manual <manual.html>`_
|
||||
| Read this to get to know the Nimrod programming system.
|
||||
| The Nimrod manual is a draft that will evolve into a proper specification.
|
||||
|
||||
- | `User guide for the Nimrod Compiler <nimrodc.html>`_
|
||||
| The user guide lists command line arguments, Nimrodc's special features, etc.
|
||||
| The user guide lists command line arguments, special features of the
|
||||
compiler, etc.
|
||||
|
||||
- | `User guide for the Embedded Nimrod Debugger <endb.html>`_
|
||||
| This document describes how to use the Embedded debugger. The embedded
|
||||
debugger currently has no GUI. Please help!
|
||||
| This document describes how to use the Embedded Debugger.
|
||||
|
||||
- | `Nimrod library documentation <lib.html>`_
|
||||
| This document describes Nimrod's standard library.
|
||||
|
||||
- | `Nimrod internal documentation <intern.html>`_
|
||||
| The internal documentation describes how the compiler is implemented. Read
|
||||
this if you want to hack the compiler or develop advanced macros.
|
||||
this if you want to hack the compiler.
|
||||
|
||||
- | `Index <theindex.html>`_
|
||||
| The generated index. Often the quickest way to find the piece of
|
||||
|
|
|
|||
|
|
@ -4,9 +4,19 @@ Short description of Nimrod's modules
|
|||
============== ==========================================================
|
||||
Module Description
|
||||
============== ==========================================================
|
||||
nimrod main module: parses the command line and calls
|
||||
```main.MainCommand``
|
||||
main implements the top-level command dispatching
|
||||
lexbase buffer handling of the lexical analyser
|
||||
scanner lexical analyser
|
||||
pnimsyn Nimrod's parser
|
||||
rnimsyn Nimrod code renderer (AST back to its textual form)
|
||||
|
||||
paslex lexer for Pascal
|
||||
pasparse parser for Pascal; Pascal's advanced OO features are not
|
||||
supported
|
||||
|
||||
options contains global and local compiler options
|
||||
ast type definitions of the abstract syntax tree (AST) and
|
||||
node constructors
|
||||
astalgo algorithms for containers of AST nodes; converting the
|
||||
|
|
@ -21,17 +31,20 @@ sigmatch contains the matching algorithm that is used for proc
|
|||
semexprs contains the semantic checking phase for expressions
|
||||
semstmts contains the semantic checking phase for statements
|
||||
semtypes contains the semantic checking phase for types
|
||||
semfold contains code to deal with constant folding
|
||||
evals contains an AST interpreter for compile time evaluation
|
||||
pragmas semantic checking of pragmas
|
||||
|
||||
idents implements a general mapping from identifiers to an internal
|
||||
representation (``PIdent``) that is used, so that a simple
|
||||
id-comparison suffices to say whether two Nimrod identifiers
|
||||
are equivalent
|
||||
|
||||
ropes implements long strings using represented as trees for
|
||||
ropes implements long strings represented as trees for
|
||||
lazy evaluation; used mainly by the code generators
|
||||
|
||||
ccgobj contains type definitions neeeded for C code generation
|
||||
and some helpers
|
||||
transf transformations on the AST that need to be done before
|
||||
code generation
|
||||
cgen main file of the C code generator
|
||||
ccgutils contains helpers for the C code generator
|
||||
ccgtypes the generator for C types
|
||||
ccgstmts the generator for statements
|
||||
|
|
|
|||
239
doc/grammar.txt
239
doc/grammar.txt
|
|
@ -1,45 +1,43 @@
|
|||
module ::= ([COMMENT] [SAD] stmt)*
|
||||
|
||||
comma ::= ',' [COMMENT] [IND]
|
||||
operator ::= OP0 | OR | XOR | AND | OP3 | OP4 | OP5 | IS | ISNOT | IN | NOTIN
|
||||
| OP6 | DIV | MOD | SHL | SHR | OP7 | NOT
|
||||
operator ::= OP0 | OR | XOR | AND | OP3 | OP4 | OP5 | OP6 | OP7
|
||||
| 'is' | 'isnot' | 'in' | 'notin'
|
||||
| 'div' | 'mod' | 'shl' | 'shr' | 'not'
|
||||
|
||||
prefixOperator ::= OP0 | OP3 | OP4 | OP5 | OP6 | OP7 | NOT
|
||||
prefixOperator ::= OP0 | OP3 | OP4 | OP5 | OP6 | OP7 | 'not'
|
||||
|
||||
optInd ::= [COMMENT] [IND]
|
||||
|
||||
|
||||
lowestExpr ::= orExpr ( OP0 optInd orExpr )*
|
||||
orExpr ::= andExpr ( OR | XOR optInd andExpr )*
|
||||
andExpr ::= cmpExpr ( AND optInd cmpExpr )*
|
||||
cmpExpr ::= ampExpr ( OP3 | IS | ISNOT | IN | NOTIN optInd ampExpr )*
|
||||
ampExpr ::= plusExpr ( OP4 optInd plusExpr )*
|
||||
plusExpr ::= mulExpr ( OP5 optInd mulExpr )*
|
||||
mulExpr ::= dollarExpr ( OP6 | DIV | MOD | SHL | SHR optInd dollarExpr )*
|
||||
dollarExpr ::= primary ( OP7 optInd primary )*
|
||||
lowestExpr ::= orExpr (OP0 optInd orExpr)*
|
||||
orExpr ::= andExpr (OR | 'xor' optInd andExpr)*
|
||||
andExpr ::= cmpExpr ('and' optInd cmpExpr)*
|
||||
cmpExpr ::= ampExpr (OP3 | 'is' | 'isnot' | 'in' | 'notin' optInd ampExpr)*
|
||||
ampExpr ::= plusExpr (OP4 optInd plusExpr)*
|
||||
plusExpr ::= mulExpr (OP5 optInd mulExpr)*
|
||||
mulExpr ::= dollarExpr (OP6 | 'div' | 'mod' | 'shl' | 'shr' optInd dollarExpr)*
|
||||
dollarExpr ::= primary (OP7 optInd primary)*
|
||||
|
||||
namedTypeOrExpr ::=
|
||||
DOTDOT [expr]
|
||||
| expr [EQUALS (expr [DOTDOT expr] | typeDescK | DOTDOT [expr] )
|
||||
| DOTDOT [expr]]
|
||||
'..' [expr]
|
||||
| expr ['=' (expr ['..' expr] | typeDescK | '..' [expr]) | '..' [expr]]
|
||||
| typeDescK
|
||||
|
||||
castExpr ::= CAST BRACKET_LE optInd typeDesc BRACKERT_RI
|
||||
PAR_LE optInd expr PAR_RI
|
||||
addrExpr ::= ADDR PAR_LE optInd expr PAR_RI
|
||||
symbol ::= ACC (KEYWORD | IDENT | operator | PAR_LE PAR_RI
|
||||
| BRACKET_LE BRACKET_RI | EQUALS | literal )+ ACC
|
||||
castExpr ::= 'cast' '[' optInd typeDesc [SAD] ']' '(' optInd expr [SAD] ')'
|
||||
addrExpr ::= 'addr' '(' optInd expr ')'
|
||||
symbol ::= '`' (KEYWORD | IDENT | operator | '(' ')'
|
||||
| '[' ']' | '=' | literal)+ '`'
|
||||
| IDENT
|
||||
primary ::= ( prefixOperator optInd )* ( symbol | constructor |
|
||||
| castExpr | addrExpr ) (
|
||||
DOT optInd symbol
|
||||
#| CURLY_LE namedTypeDescList CURLY_RI
|
||||
| PAR_LE optInd namedExprList PAR_RI
|
||||
| BRACKET_LE optInd
|
||||
[ namedTypeOrExpr (comma namedTypeOrExpr)* [comma] ]
|
||||
BRACKET_RI
|
||||
| CIRCUM
|
||||
| pragma )*
|
||||
primary ::= (prefixOperator optInd)* (symbol | constructor |
|
||||
| castExpr | addrExpr) (
|
||||
'.' optInd symbol
|
||||
| '(' optInd namedExprList [SAD] ')'
|
||||
| '[' optInd
|
||||
[namedTypeOrExpr (comma namedTypeOrExpr)* [comma]]
|
||||
[SAD] ']'
|
||||
| '^'
|
||||
| pragma)*
|
||||
|
||||
literal ::= INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
|
||||
| FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT
|
||||
|
|
@ -48,48 +46,42 @@ literal ::= INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
|
|||
| NIL
|
||||
|
||||
constructor ::= literal
|
||||
| BRACKET_LE optInd colonExprList BRACKET_RI # []-Constructor
|
||||
| CURLY_LE optInd sliceExprList CURLY_RI # {}-Constructor
|
||||
| PAR_LE optInd colonExprList PAR_RI # ()-Constructor
|
||||
| '[' optInd colonExprList [SAD] ']'
|
||||
| '{' optInd sliceExprList [SAD] '}'
|
||||
| '(' optInd colonExprList [SAD] ')'
|
||||
|
||||
exprList ::= [ expr (comma expr)* [comma] ]
|
||||
colonExpr ::= expr [':' expr]
|
||||
colonExprList ::= [colonExpr (comma colonExpr)* [comma]]
|
||||
|
||||
colonExpr ::= expr [COLON expr]
|
||||
colonExprList ::= [ colonExpr (comma colonExpr)* [comma] ]
|
||||
namedExpr ::= expr ['=' expr]
|
||||
namedExprList ::= [namedExpr (comma namedExpr)* [comma]]
|
||||
|
||||
namedExpr ::= expr [EQUALS expr] # actually this is symbol EQUALS expr|expr
|
||||
namedExprList ::= [ namedExpr (comma namedExpr)* [comma] ]
|
||||
sliceExpr ::= expr ['..' expr]
|
||||
sliceExprList ::= [sliceExpr (comma sliceExpr)* [comma]]
|
||||
|
||||
sliceExpr ::= expr [ DOTDOT expr ]
|
||||
sliceExprList ::= [ sliceExpr (comma sliceExpr)* [comma] ]
|
||||
|
||||
anonymousProc ::= LAMBDA paramList [pragma] EQUALS stmt
|
||||
anonymousProc ::= 'lambda' paramList [pragma] '=' stmt
|
||||
expr ::= lowestExpr
|
||||
| anonymousProc
|
||||
| IF expr COLON expr
|
||||
(ELIF expr COLON expr)*
|
||||
ELSE COLON expr
|
||||
| 'if' expr ':' expr ('elif' expr ':' expr)* 'else' ':' expr
|
||||
|
||||
namedTypeDesc ::= typeDescK | expr [EQUALS (typeDescK | expr)]
|
||||
namedTypeDescList ::= [ namedTypeDesc (comma namedTypeDesc)* [comma] ]
|
||||
namedTypeDesc ::= typeDescK | expr ['=' (typeDescK | expr)]
|
||||
namedTypeDescList ::= [namedTypeDesc (comma namedTypeDesc)* [comma]]
|
||||
|
||||
qualifiedIdent ::= symbol [ DOT symbol ]
|
||||
qualifiedIdent ::= symbol ['.' symbol]
|
||||
|
||||
typeDescK ::= VAR typeDesc
|
||||
| REF typeDesc
|
||||
| PTR typeDesc
|
||||
| TYPE expr
|
||||
| TUPLE tupleDesc
|
||||
| PROC paramList [pragma]
|
||||
typeDescK ::= 'var' typeDesc
|
||||
| 'ref' typeDesc
|
||||
| 'ptr' typeDesc
|
||||
| 'type' expr
|
||||
| 'tuple' tupleDesc
|
||||
| 'proc' paramList [pragma]
|
||||
|
||||
typeDesc ::= typeDescK | primary
|
||||
|
||||
optSemicolon ::= [SEMICOLON]
|
||||
|
||||
macroStmt ::= COLON [stmt] (OF [sliceExprList] COLON stmt
|
||||
| ELIF expr COLON stmt
|
||||
| EXCEPT exceptList COLON stmt )*
|
||||
[ELSE COLON stmt]
|
||||
macroStmt ::= ':' [stmt] ('of' [sliceExprList] ':' stmt
|
||||
|'elif' expr ':' stmt
|
||||
|'except' exceptList ':' stmt )*
|
||||
['else' ':' stmt]
|
||||
|
||||
simpleStmt ::= returnStmt
|
||||
| yieldStmt
|
||||
|
|
@ -107,88 +99,91 @@ complexStmt ::= ifStmt | whileStmt | caseStmt | tryStmt | forStmt
|
|||
| procDecl | iteratorDecl | macroDecl | templateDecl
|
||||
| constSection | typeSection | whenStmt | varSection
|
||||
|
||||
indPush ::= IND # push
|
||||
indPush ::= IND # and push indentation onto the stack
|
||||
indPop ::= # pop indentation from the stack
|
||||
|
||||
stmt ::= simpleStmt [SAD]
|
||||
| indPush (complexStmt | simpleStmt)
|
||||
([SAD] (complexStmt | simpleStmt) )*
|
||||
DED
|
||||
([SAD] (complexStmt | simpleStmt))*
|
||||
DED indPop
|
||||
|
||||
exprStmt ::= lowestExpr [EQUALS expr | [expr (comma expr)* [comma]] [macroStmt]]
|
||||
returnStmt ::= RETURN [expr]
|
||||
yieldStmt ::= YIELD expr
|
||||
discardStmt ::= DISCARD expr
|
||||
raiseStmt ::= RAISE [expr]
|
||||
breakStmt ::= BREAK [symbol]
|
||||
continueStmt ::= CONTINUE
|
||||
ifStmt ::= IF expr COLON stmt (ELIF expr COLON stmt)* [ELSE COLON stmt]
|
||||
whenStmt ::= WHEN expr COLON stmt (ELIF expr COLON stmt)* [ELSE COLON stmt]
|
||||
caseStmt ::= CASE expr (OF sliceExprList COLON stmt)*
|
||||
(ELIF expr COLON stmt)*
|
||||
[ELSE COLON stmt]
|
||||
whileStmt ::= WHILE expr COLON stmt
|
||||
forStmt ::= FOR symbol (comma symbol)* [comma] IN expr [DOTDOT expr] COLON stmt
|
||||
exceptList ::= [qualifiedIdent (comma qualifiedIdent)* [comma]]
|
||||
exprStmt ::= lowestExpr ['=' expr | [expr (comma expr)*] [macroStmt]]
|
||||
returnStmt ::= 'return' [expr]
|
||||
yieldStmt ::= 'yield' expr
|
||||
discardStmt ::= 'discard' expr
|
||||
raiseStmt ::= 'raise' [expr]
|
||||
breakStmt ::= 'break' [symbol]
|
||||
continueStmt ::= 'continue'
|
||||
ifStmt ::= 'if' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
|
||||
whenStmt ::= 'when' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
|
||||
caseStmt ::= 'case' expr [':'] ('of' sliceExprList ':' stmt)*
|
||||
('elif' expr ':' stmt)*
|
||||
['else' ':' stmt]
|
||||
whileStmt ::= 'while' expr ':' stmt
|
||||
forStmt ::= 'for' symbol (comma symbol)* 'in' expr ['..' expr] ':' stmt
|
||||
exceptList ::= [qualifiedIdent (comma qualifiedIdent)*]
|
||||
|
||||
tryStmt ::= TRY COLON stmt
|
||||
(EXCEPT exceptList COLON stmt)*
|
||||
[FINALLY COLON stmt]
|
||||
asmStmt ::= ASM [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
|
||||
blockStmt ::= BLOCK [symbol] COLON stmt
|
||||
tryStmt ::= 'try' ':' stmt
|
||||
('except' exceptList ':' stmt)*
|
||||
['finally' ':' stmt]
|
||||
asmStmt ::= 'asm' [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
|
||||
blockStmt ::= 'block' [symbol] ':' stmt
|
||||
filename ::= symbol | STR_LIT | RSTR_LIT | TRIPLESTR_LIT
|
||||
importStmt ::= IMPORT filename (comma filename)* [comma]
|
||||
includeStmt ::= INCLUDE filename (comma filename)* [comma]
|
||||
fromStmt ::= FROM filename IMPORT symbol (comma symbol)* [comma]
|
||||
importStmt ::= 'import' filename (comma filename)*
|
||||
includeStmt ::= 'include' filename (comma filename)*
|
||||
fromStmt ::= 'from' filename 'import' symbol (comma symbol)*
|
||||
|
||||
pragma ::= CURLYDOT_LE colonExprList (CURLYDOT_RI | CURLY_RI)
|
||||
pragma ::= '{.' optInd (colonExpr [comma])* [SAD] ('.}' | '}')
|
||||
|
||||
param ::= symbol (comma symbol)* [comma] COLON typeDesc
|
||||
paramList ::= [PAR_LE [param (comma param)* [comma]] PAR_RI] [COLON typeDesc]
|
||||
param ::= symbol (comma symbol)* ':' typeDesc
|
||||
paramList ::= ['(' [param (comma param)*] [SAD] ')'] [':' typeDesc]
|
||||
|
||||
genericParams ::= BRACKET_LE (symbol [EQUALS typeDesc] )* BRACKET_RI
|
||||
genericParam ::= symbol [':' typeDesc]
|
||||
genericParams ::= '[' genericParam (comma genericParam)* [SAD] ']'
|
||||
|
||||
procDecl ::= PROC symbol ["*"] [genericParams]
|
||||
paramList [pragma]
|
||||
[EQUALS stmt]
|
||||
macroDecl ::= MACRO symbol ["*"] [genericParams] paramList [pragma]
|
||||
[EQUALS stmt]
|
||||
iteratorDecl ::= ITERATOR symbol ["*"] [genericParams] paramList [pragma]
|
||||
[EQUALS stmt]
|
||||
templateDecl ::= TEMPLATE symbol ["*"] [genericParams] paramList [pragma]
|
||||
[EQUALS stmt]
|
||||
procDecl ::= 'proc' symbol ['*'] [genericParams] paramList [pragma]
|
||||
['=' stmt]
|
||||
macroDecl ::= 'macro' symbol ['*'] [genericParams] paramList [pragma]
|
||||
['=' stmt]
|
||||
iteratorDecl ::= 'iterator' symbol ['*'] [genericParams] paramList [pragma]
|
||||
['=' stmt]
|
||||
templateDecl ::= 'template' symbol ['*'] [genericParams] paramList [pragma]
|
||||
['=' stmt]
|
||||
|
||||
colonAndEquals ::= [COLON typeDesc] EQUALS expr
|
||||
colonAndEquals ::= [':' typeDesc] '=' expr
|
||||
|
||||
constDecl ::= symbol ["*"] [pragma] colonAndEquals [COMMENT | IND COMMENT]
|
||||
constDecl ::= symbol ['*'] [pragma] colonAndEquals [COMMENT | IND COMMENT]
|
||||
| COMMENT
|
||||
constSection ::= CONST indPush constDecl (SAD constDecl)* DED
|
||||
constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop
|
||||
typeDef ::= typeDesc | objectDef | enumDef
|
||||
|
||||
objectField ::= symbol ["*"] [pragma]
|
||||
objectField ::= symbol ['*'] [pragma]
|
||||
objectIdentPart ::=
|
||||
objectField (comma objectField)* [comma] COLON typeDesc [COMMENT|IND COMMENT]
|
||||
objectField (comma objectField)* ':' typeDesc [COMMENT|IND COMMENT]
|
||||
|
||||
objectWhen ::= WHEN expr COLON [COMMENT] objectPart
|
||||
(ELIF expr COLON [COMMENT] objectPart)*
|
||||
[ELSE COLON [COMMENT] objectPart]
|
||||
objectCase ::= CASE expr COLON typeDesc [COMMENT]
|
||||
(OF sliceExprList COLON [COMMENT] objectPart)*
|
||||
[ELSE COLON [COMMENT] objectPart]
|
||||
objectWhen ::= 'when' expr ':' [COMMENT] objectPart
|
||||
('elif' expr ':' [COMMENT] objectPart)*
|
||||
['else' ':' [COMMENT] objectPart]
|
||||
objectCase ::= 'case' expr ':' typeDesc [COMMENT]
|
||||
('of' sliceExprList ':' [COMMENT] objectPart)*
|
||||
['else' ':' [COMMENT] objectPart]
|
||||
|
||||
objectPart ::= objectWhen | objectCase | objectIdentPart | NIL
|
||||
| indPush objectPart (SAD objectPart)* DED
|
||||
tupleDesc ::= BRACKET_LE optInd [param (comma param)* [comma]] BRACKET_RI
|
||||
objectPart ::= objectWhen | objectCase | objectIdentPart | 'nil'
|
||||
| indPush objectPart (SAD objectPart)* DED indPop
|
||||
tupleDesc ::= '[' optInd [param (comma param)*] [SAD] ']'
|
||||
|
||||
objectDef ::= OBJECT [pragma] [OF typeDesc] objectPart
|
||||
enumField ::= symbol [EQUALS expr]
|
||||
enumDef ::= ENUM [OF typeDesc] (enumField [comma | COMMENT | IND COMMENT])+
|
||||
objectDef ::= 'object' [pragma] ['of' typeDesc] objectPart
|
||||
enumField ::= symbol ['=' expr]
|
||||
enumDef ::= 'enum' ['of' typeDesc] (enumField [comma] [COMMENT | IND COMMENT])+
|
||||
|
||||
typeDecl ::= COMMENT
|
||||
| symbol ["*"] [genericParams] [EQUALS typeDef] [COMMENT | IND COMMENT]
|
||||
| symbol ['*'] [genericParams] ['=' typeDef] [COMMENT | IND COMMENT]
|
||||
|
||||
typeSection ::= TYPE indPush typeDecl (SAD typeDecl)* DED
|
||||
typeSection ::= 'type' indPush typeDecl (SAD typeDecl)* DED indPop
|
||||
|
||||
colonOrEquals ::= COLON typeDesc [EQUALS expr] | EQUALS expr
|
||||
varField ::= symbol ["*"] [pragma]
|
||||
varPart ::= symbol (comma symbol)* [comma] colonOrEquals [COMMENT | IND COMMENT]
|
||||
varSection ::= VAR (varPart
|
||||
| indPush (COMMENT|varPart) (SAD (COMMENT|varPart))* DED)
|
||||
colonOrEquals ::= ':' typeDesc ['=' expr] | '=' expr
|
||||
varField ::= symbol ['*'] [pragma]
|
||||
varPart ::= symbol (comma symbol)* colonOrEquals [COMMENT | IND COMMENT]
|
||||
varSection ::= 'var' (varPart
|
||||
| indPush (COMMENT|varPart)
|
||||
(SAD (COMMENT|varPart))* DED indPop)
|
||||
|
|
|
|||
187
doc/intern.txt
187
doc/intern.txt
|
|
@ -34,7 +34,6 @@ Path Purpose
|
|||
on it!
|
||||
``web`` website of Nimrod; generated by ``koch.py``
|
||||
from the ``*.txt`` and ``*.tmpl`` files
|
||||
``koch`` the Koch Build System (written for Nimrod)
|
||||
``obj`` generated ``*.obj`` files go into here
|
||||
============ ==============================================
|
||||
|
||||
|
|
@ -45,44 +44,76 @@ Bootstrapping the compiler
|
|||
The compiler is written in a subset of Pascal with special annotations so
|
||||
that it can be translated to Nimrod code automatically. This conversion is
|
||||
done by Nimrod itself via the undocumented ``boot`` command. Thus both Nimrod
|
||||
and Free Pascal can compile the Nimrod compiler.
|
||||
and Free Pascal can compile the Nimrod compiler. However, the Pascal version
|
||||
has no garbage collector and leaks memory like crazy! So the Pascal version
|
||||
should only be used for bootstrapping.
|
||||
|
||||
Requirements for bootstrapping:
|
||||
|
||||
- Free Pascal (I used version 2.2) [optional]
|
||||
- Python (should work with version 1.5 or higher)
|
||||
- Python (should work with version 1.5 or higher) (optional)
|
||||
- supported C compiler
|
||||
|
||||
- C compiler -- one of:
|
||||
Compiling the compiler is a simple matter of running::
|
||||
|
||||
* win32-lcc (currently broken)
|
||||
* Borland C++ (tested with 5.5; currently broken)
|
||||
* Microsoft C++
|
||||
* Digital Mars C++
|
||||
* Watcom C++ (currently broken)
|
||||
* GCC
|
||||
* Intel C++
|
||||
* Pelles C (currently broken)
|
||||
* llvm-gcc
|
||||
koch.py boot
|
||||
|
||||
| Compiling the compiler is a simple matter of running:
|
||||
| ``koch.py boot``
|
||||
| Or you can compile by hand, this is not difficult.
|
||||
For a release version use::
|
||||
|
||||
If you want to debug the compiler, use the command::
|
||||
koch.py boot -d:release
|
||||
|
||||
koch.py boot --debugger:on
|
||||
The ``koch.py`` script is Nimrod's maintainance script. It is a replacement for
|
||||
make and shell scripting with the advantage that it is much more portable.
|
||||
|
||||
The ``koch.py`` script is Nimrod's maintainance script: Everything that has
|
||||
been automated is accessible with it. It is a replacement for make and shell
|
||||
scripting with the advantage that it is more portable.
|
||||
If you don't have Python, there is a ``boot`` Nimrod program which does roughly
|
||||
the same::
|
||||
|
||||
nimrod cc boot.nim
|
||||
./boot [-d:release]
|
||||
|
||||
|
||||
Coding standards
|
||||
================
|
||||
Pascal annotations
|
||||
==================
|
||||
There are some annotations that the Pascal sources use so that they can
|
||||
be converted to Nimrod automatically:
|
||||
|
||||
The compiler is written in a subset of Pascal with special annotations so
|
||||
that it can be translated to Nimrod code automatically. As a general rule,
|
||||
Pascal code that does not translate to Nimrod automatically is forbidden.
|
||||
``{@discard} <expr>``
|
||||
Tells the compiler that a ``discard`` statement is needed for Nimrod
|
||||
here.
|
||||
|
||||
``{@cast}typ(expr)``
|
||||
Tells the compiler that the Pascal conversion is a ``cast`` in Nimrod.
|
||||
|
||||
``{@emit <code>}``
|
||||
Emits ``<code>``. The code fragment needs to be in Pascal syntax.
|
||||
|
||||
``{@ignore} <codeA> {@emit <codeB>}``
|
||||
Ignores ``<codeA>`` and instead emits ``<codeB>`` which needs to be in
|
||||
Pascal syntax. An empty ``{@emit}`` is possible too (it then only closes
|
||||
the ``<codeA>`` part).
|
||||
|
||||
``record {@tuple}``
|
||||
Is used to tell the compiler that the record type should be transformed
|
||||
to a Nimrod tuple type.
|
||||
|
||||
``^ {@ptr}``
|
||||
Is used to tell the compiler that the pointer type should be transformed
|
||||
to a Nimrod ``ptr`` type. The default is a ``ref`` type.
|
||||
|
||||
``'a' + ''``
|
||||
The idiom ``+''`` is used to tell the compiler that it is a string
|
||||
literal and not a character literal. (Pascal does not distinguish between
|
||||
character literals and string literals of length 1.)
|
||||
|
||||
``+{&}``
|
||||
This tells the compiler that Pascal's ``+`` here is a string concatenation
|
||||
and thus should be converted to ``&``. Note that this is not needed if
|
||||
any of the operands is a string literal because the compiler then can
|
||||
figure this out by itself.
|
||||
|
||||
``{@set}['a', 'b', 'c']``
|
||||
Tells the compiler that Pascal's ``[]`` constructor is a set and not an
|
||||
array. This is only needed if the compiler cannot figure this out for
|
||||
itself.
|
||||
|
||||
|
||||
Porting to new platforms
|
||||
|
|
@ -99,7 +130,7 @@ check that the OS, System modules work and recompile Nimrod.
|
|||
The only case where things aren't as easy is when the garbage
|
||||
collector needs some assembler tweaking to work. The standard
|
||||
version of the GC uses C's ``setjmp`` function to store all registers
|
||||
on the hardware stack. It may be that the new platform needs to
|
||||
on the hardware stack. It may be necessary that the new platform needs to
|
||||
replace this generic code by some assembler code.
|
||||
|
||||
|
||||
|
|
@ -132,11 +163,11 @@ The Garbage Collector
|
|||
Introduction
|
||||
------------
|
||||
|
||||
We use the term *cell* here to refer to everything that is traced
|
||||
I use the term *cell* here to refer to everything that is traced
|
||||
(sequences, refs, strings).
|
||||
This section describes how the new GC works.
|
||||
|
||||
The basic algorithm is *Deferrent reference counting* with cycle detection.
|
||||
The basic algorithm is *Deferrent Reference Counting* with cycle detection.
|
||||
References in the stack are not counted for better performance and easier C
|
||||
code generation.
|
||||
|
||||
|
|
@ -170,7 +201,7 @@ modifying a ``TCellSet`` during traversation leads to undefined behaviour.
|
|||
iterator elements(s: TCellSet): (elem: PCell)
|
||||
|
||||
|
||||
All the operations have to be perform efficiently. Because a Cellset can
|
||||
All the operations have to perform efficiently. Because a Cellset can
|
||||
become huge a hash table alone is not suitable for this.
|
||||
|
||||
We use a mixture of bitset and hash table for this. The hash table maps *pages*
|
||||
|
|
@ -246,16 +277,10 @@ This syntax tree is the interface between the parser and the code generator.
|
|||
It is essential to understand most of the compiler's code.
|
||||
|
||||
In order to compile Nimrod correctly, type-checking has to be seperated from
|
||||
parsing. Otherwise generics would not work. Code generation is done for a
|
||||
whole module only after it has been checked for semantics.
|
||||
parsing. Otherwise generics would not work.
|
||||
|
||||
.. include:: filelist.txt
|
||||
|
||||
The first command line argument selects the backend. Thus the backend is
|
||||
responsible for calling the parser and semantic checker. However, when
|
||||
compiling ``import`` or ``include`` statements, the semantic checker needs to
|
||||
call the backend, this is done by embedding a PBackend into a TContext.
|
||||
|
||||
|
||||
The syntax tree
|
||||
---------------
|
||||
|
|
@ -265,7 +290,7 @@ may contain cycles. The AST changes its shape after semantic checking. This
|
|||
is needed to make life easier for the code generators. See the "ast" module
|
||||
for the type definitions.
|
||||
|
||||
We use the notation ``nodeKind(fields, [sons])`` for describing
|
||||
I use the notation ``nodeKind(fields, [sons])`` for describing
|
||||
nodes. ``nodeKind[sons]`` is a short-cut for ``nodeKind([sons])``.
|
||||
XXX: Description of the language's syntax and the corresponding trees.
|
||||
|
||||
|
|
@ -273,12 +298,16 @@ XXX: Description of the language's syntax and the corresponding trees.
|
|||
How the RTL is compiled
|
||||
=======================
|
||||
|
||||
The system module contains the part of the RTL which needs support by
|
||||
The ``system`` module contains the part of the RTL which needs support by
|
||||
compiler magic (and the stuff that needs to be in it because the spec
|
||||
says so). The C code generator generates the C code for it just like any other
|
||||
module. However, calls to some procedures like ``addInt`` are inserted by
|
||||
the CCG. Therefore the module ``magicsys`` contains a table
|
||||
(``compilerprocs``) with all symbols that are marked as ``compilerproc``.
|
||||
the CCG. Therefore the module ``magicsys`` contains a table (``compilerprocs``)
|
||||
with all symbols that are marked as ``compilerproc``. ``compilerprocs`` are
|
||||
needed by the code generator. A ``magic`` proc is not the same as a
|
||||
``compilerproc``: A ``magic`` is a proc that needs compiler magic for its
|
||||
semantic checking, a ``compilerproc`` is a proc that is used by the code
|
||||
generator.
|
||||
|
||||
|
||||
|
||||
|
|
@ -290,77 +319,3 @@ underlying C compiler already does all the hard work for us. The problem is the
|
|||
common runtime library, especially the memory manager. Note that Borland's
|
||||
Delphi had exactly the same problem. The workaround is to not link the GC with
|
||||
the Dll and provide an extra runtime dll that needs to be initialized.
|
||||
|
||||
|
||||
|
||||
How to implement closures
|
||||
=========================
|
||||
|
||||
A closure is a record of a proc pointer and a context ref. The context ref
|
||||
points to a garbage collected record that contains the needed variables.
|
||||
An example:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
|
||||
type
|
||||
TListRec = record
|
||||
data: string
|
||||
next: ref TListRec
|
||||
|
||||
proc forEach(head: ref TListRec, visitor: proc (s: string) {.closure.}) =
|
||||
var it = head
|
||||
while it != nil:
|
||||
visit(it.data)
|
||||
it = it.next
|
||||
|
||||
proc sayHello() =
|
||||
var L = new List(["hallo", "Andreas"])
|
||||
var temp = "jup\xff"
|
||||
forEach(L, lambda(s: string) =
|
||||
io.write(temp)
|
||||
io.write(s)
|
||||
)
|
||||
|
||||
|
||||
This should become the following in C:
|
||||
|
||||
.. code-block:: C
|
||||
typedef struct ... /* List type */
|
||||
|
||||
typedef struct closure {
|
||||
void (*PrcPart)(string, void*);
|
||||
void* ClPart;
|
||||
}
|
||||
|
||||
typedef struct Tcl_data {
|
||||
string temp; // all accessed variables are put in here!
|
||||
}
|
||||
|
||||
void forEach(TListRec* head, const closure visitor) {
|
||||
TListRec* it = head;
|
||||
while (it != NIM_NULL) {
|
||||
visitor.prc(it->data, visitor->cl_data);
|
||||
it = it->next;
|
||||
}
|
||||
}
|
||||
|
||||
void printStr(string s, void* cl_data) {
|
||||
Tcl_data* x = (Tcl_data*) cl_data;
|
||||
io_write(x->temp);
|
||||
io_write(s);
|
||||
}
|
||||
|
||||
void sayhello() {
|
||||
Tcl_data* data = new(...);
|
||||
asgnRef(&data->temp, "jup\xff");
|
||||
...
|
||||
|
||||
closure cl;
|
||||
cl.prc = printStr;
|
||||
cl.cl_data = data;
|
||||
foreach(L, cl);
|
||||
}
|
||||
|
||||
|
||||
What about nested closure? - There's not much difference: Just put all used
|
||||
variables in the data record.
|
||||
|
|
|
|||
523
doc/manual.txt
523
doc/manual.txt
|
|
@ -11,6 +11,10 @@ Nimrod Manual
|
|||
About this document
|
||||
===================
|
||||
|
||||
**Note**: This document is a draft! Several of Nimrod's features need more
|
||||
precise wording. This manual will evolve into a proper specification some
|
||||
day.
|
||||
|
||||
This document describes the lexis, the syntax, and the semantics of Nimrod.
|
||||
|
||||
The language constructs are explained using an extended BNF, in
|
||||
|
|
@ -18,10 +22,11 @@ which ``(a)*`` means 0 or more ``a``'s, ``a+`` means 1 or more ``a``'s, and
|
|||
``(a)?`` means an optional *a*; an alternative spelling for optional parts is
|
||||
``[a]``. The ``|`` symbol is used to mark alternatives
|
||||
and has the lowest precedence. Parentheses may be used to group elements.
|
||||
Non-terminals are in lowercase, terminal symbols (including keywords) are in
|
||||
UPPERCASE. An example::
|
||||
Non-terminals start with a lowercase letter, abstract terminal symbols are in
|
||||
UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
|
||||
with ``'``. An example::
|
||||
|
||||
if_stmt ::= IF expr COLON stmts (ELIF expr COLON stmts)* [ELSE stmts]
|
||||
ifStmt ::= 'if' expr ':' stmts ('elif' expr ':' stmts)* ['else' stmts]
|
||||
|
||||
Other parts of Nimrod - like scoping rules or runtime semantics are only
|
||||
described in an informal manner. The reason is that formal semantics are
|
||||
|
|
@ -90,8 +95,7 @@ Indentation consists only of spaces; tabulators are not allowed.
|
|||
The terminals ``IND`` (indentation), ``DED`` (dedentation) and ``SAD``
|
||||
(same indentation) are generated by the scanner, denoting an indentation.
|
||||
|
||||
These terminals are only generated for lines that are not empty or contain
|
||||
only whitespace and comments.
|
||||
These terminals are only generated for lines that are not empty.
|
||||
|
||||
The parser and the scanner communicate over a stack which indentation terminal
|
||||
should be generated: The stack consists of integers counting the spaces. The
|
||||
|
|
@ -100,14 +104,17 @@ If the current indentation token consists of more spaces than the entry at the
|
|||
top of the stack, a ``IND`` token is generated, else if it consists of the same
|
||||
number of spaces, a ``SAD`` token is generated. If it consists of fewer spaces,
|
||||
a ``DED`` token is generated for any item on the stack that is greater than the
|
||||
current. These items are then popped from the stack by the scanner. At the end
|
||||
current. These items are later popped from the stack by the parser. At the end
|
||||
of the file, a ``DED`` token is generated for each number remaining on the
|
||||
stack that is larger than zero.
|
||||
|
||||
Because the grammar contains some optional ``IND`` tokens, the scanner cannot
|
||||
push new indentation levels. This has to be done by the parser. The symbol
|
||||
``indPush`` indicates that an ``IND`` token is expected; the current number of
|
||||
leading spaces is pushed onto the stack by the parser.
|
||||
leading spaces is pushed onto the stack by the parser. The symbol ``indPop``
|
||||
denotes that the parser pops an item from the indentation stack. No token is
|
||||
consumed by ``indPop``.
|
||||
|
||||
|
||||
Comments
|
||||
--------
|
||||
|
|
@ -131,8 +138,8 @@ aligned to the preceding one, it does not start a new comment:
|
|||
Comments are tokens; they are only allowed at certain places in the input file
|
||||
as they belong to the syntax tree! This feature enables perfect source-to-source
|
||||
transformations (such as pretty-printing) and superior documentation generators.
|
||||
A side-effect is that the human reader of the code always knows exactly which
|
||||
code snippet the comment refers to.
|
||||
A nice side-effect is that the human reader of the code always knows exactly
|
||||
which code snippet the comment refers to.
|
||||
|
||||
|
||||
Identifiers & Keywords
|
||||
|
|
@ -159,9 +166,9 @@ case-sensitive and even underscores are ignored:
|
|||
**type** is a reserved word, and so is **TYPE** or **T_Y_P_E**. The idea behind
|
||||
this is that this allows programmers to use their own prefered spelling style
|
||||
and libraries written by different programmers cannot use incompatible
|
||||
conventions. The editors or IDE can show the identifiers as preferred. Another
|
||||
advantage is that it frees the programmer from remembering the exact spelling
|
||||
of an identifier.
|
||||
conventions. A Nimrod-aware editor or IDE can show the identifiers as
|
||||
preferred. Another advantage is that it frees the programmer from remembering
|
||||
the exact spelling of an identifier.
|
||||
|
||||
|
||||
Literal strings
|
||||
|
|
@ -174,7 +181,7 @@ contain the following `escape sequences`:idx:\ :
|
|||
Escape sequence Meaning
|
||||
================== ===================================================
|
||||
``\n`` `newline`:idx:
|
||||
``\r`` `carriage return`:idx:
|
||||
``\r``, ``\c`` `carriage return`:idx:
|
||||
``\l`` `line feed`:idx:
|
||||
``\f`` `form feed`:idx:
|
||||
``\t`` `tabulator`:idx:
|
||||
|
|
@ -184,8 +191,7 @@ contain the following `escape sequences`:idx:\ :
|
|||
``\'`` `apostrophe`:idx:
|
||||
``\d+`` `character with decimal value d`:idx:;
|
||||
all decimal digits directly
|
||||
following are used for the
|
||||
character
|
||||
following are used for the character
|
||||
``\a`` `alert`:idx:
|
||||
``\b`` `backspace`:idx:
|
||||
``\e`` `escape`:idx: `[ESC]`:idx:
|
||||
|
|
@ -194,15 +200,14 @@ contain the following `escape sequences`:idx:\ :
|
|||
================== ===================================================
|
||||
|
||||
|
||||
Strings in Nimrod may contain any 8-bit value, except embedded zeros
|
||||
which are not allowed for compability with `C`:idx:.
|
||||
Strings in Nimrod may contain any 8-bit value, except embedded zeros.
|
||||
|
||||
Literal strings can also be delimited by three double squotes
|
||||
``"""`` ... ``"""``.
|
||||
Literals in this form may run for several lines, may contain ``"`` and do not
|
||||
interpret any escape sequences.
|
||||
For convenience, when the opening ``"""`` is immediately
|
||||
followed by a newline, the newline is not included in the string.
|
||||
For convenience, when the opening ``"""`` is immediately followed by a newline,
|
||||
the newline is not included in the string.
|
||||
There are also `raw string literals` that are preceded with the letter ``r``
|
||||
(or ``R``) and are delimited by matching double quotes (just like ordinary
|
||||
string literals) and do not interpret the escape sequences. This is especially
|
||||
|
|
@ -253,8 +258,8 @@ Numerical constants
|
|||
|
||||
As can be seen in the productions, numerical constants can contain unterscores
|
||||
for readability. Integer and floating point literals may be given in decimal (no
|
||||
prefix), binary (prefix ``0b``), octal (prefix ``0o``) and
|
||||
hexadecimal (prefix ``0x``) notation.
|
||||
prefix), binary (prefix ``0b``), octal (prefix ``0o``) and hexadecimal
|
||||
(prefix ``0x``) notation.
|
||||
|
||||
There exists a literal for each numerical type that is
|
||||
defined. The suffix starting with an apostophe ('\'') is called a
|
||||
|
|
@ -262,7 +267,7 @@ defined. The suffix starting with an apostophe ('\'') is called a
|
|||
unless the literal contains a dot or an ``E`` in which case it is of
|
||||
type ``float``.
|
||||
|
||||
The following table specifies type suffixes:
|
||||
The type suffixes are:
|
||||
|
||||
================= =========================
|
||||
Type Suffix Resulting type of literal
|
||||
|
|
@ -295,11 +300,11 @@ the three tokens `{`:tok:, `..`:tok:, `}`:tok: and not the two tokens
|
|||
`{.`:tok:, `.}`:tok:.
|
||||
|
||||
In Nimrod one can define his own operators. An `operator`:idx: is any
|
||||
combination of the following characters that are not listed above::
|
||||
combination of the following characters that is not listed above::
|
||||
|
||||
+ - * / < >
|
||||
= @ $ ~ & %
|
||||
! ? ^ . |
|
||||
! ? ^ . | \
|
||||
|
||||
These keywords are also operators:
|
||||
``and or not xor shl shr div mod in notin is isnot``.
|
||||
|
|
@ -348,16 +353,13 @@ Constants
|
|||
cannot change. The compiler must be able to evaluate the expression in a
|
||||
constant declaration at compile time.
|
||||
|
||||
..
|
||||
Nimrod contains a sophisticated
|
||||
compile-time evaluator, so procedures declared with the ``{.noSideEffect.}``
|
||||
pragma can be used in constant expressions:
|
||||
Nimrod contains a sophisticated compile-time evaluator, so procedures which
|
||||
have no side-effect can be used in constant expressions too:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
from strutils import findSubStr
|
||||
const
|
||||
x = findSubStr('a', "hallo") # x is 1; this is computed at compile time!
|
||||
.. code-block:: nimrod
|
||||
import strutils
|
||||
const
|
||||
constEval = contains("abc", 'b') # computed at compile time!
|
||||
|
||||
|
||||
Types
|
||||
|
|
@ -414,8 +416,8 @@ intXX
|
|||
|
||||
There are no `unsigned integer`:idx: types, only `unsigned operations`:idx:
|
||||
that treat their arguments as unsigned. Unsigned operations all wrap around;
|
||||
they may not lead to over- or underflow errors. Unsigned operations use the
|
||||
``%`` postfix as convention:
|
||||
they cannot lead to over- or underflow errors. Unsigned operations use the
|
||||
``%`` suffix as convention:
|
||||
|
||||
====================== ======================================================
|
||||
operation meaning
|
||||
|
|
@ -498,16 +500,15 @@ the resulting programs will still handle UTF-8 properly as UTF-8 was specially
|
|||
designed for this.
|
||||
Another reason is that Nimrod can support ``array[char, int]`` or
|
||||
``set[char]`` efficiently as many algorithms rely on this feature. The
|
||||
`TUniChar` type is used for Unicode characters, it can represent any Unicode
|
||||
character. ``TUniChar`` is declared the ``unicode`` standard module.
|
||||
`TRune` type is used for Unicode characters, it can represent any Unicode
|
||||
character. ``TRune`` is declared the ``unicode`` module.
|
||||
|
||||
|
||||
|
||||
Enumeration types
|
||||
~~~~~~~~~~~~~~~~~
|
||||
`Enumeration`:idx: types define a new type whose values consist only of the ones
|
||||
specified.
|
||||
The values are ordered by the order in enum's declaration. Example:
|
||||
`Enumeration`:idx: types define a new type whose values consist of the ones
|
||||
specified. The values are ordered. Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
|
|
@ -528,8 +529,8 @@ with enumeration types.
|
|||
|
||||
For better interfacing to other programming languages, the fields of enum
|
||||
types can be assigned an explicit ordinal value. However, the ordinal values
|
||||
have to be in ascending order. A field whose ordinal value that is not
|
||||
explicitly given, is assigned the value of the previous field + 1.
|
||||
have to be in ascending order. A field whose ordinal value is not
|
||||
explicitly given is assigned the value of the previous field + 1.
|
||||
|
||||
An explicit ordered enum can have *wholes*:
|
||||
|
||||
|
|
@ -545,7 +546,7 @@ and ``pred`` are not available for them either.
|
|||
|
||||
Subrange types
|
||||
~~~~~~~~~~~~~~
|
||||
A `subrange`:idx: type is a range of values from an ordinal type (the host
|
||||
A `subrange`:idx: type is a range of values from an ordinal type (the base
|
||||
type). To define a subrange type, one must specify it's limiting values: the
|
||||
highest and lowest value of the type:
|
||||
|
||||
|
|
@ -566,7 +567,7 @@ A subrange type has the same size as its base type (``int`` in the example).
|
|||
String type
|
||||
~~~~~~~~~~~
|
||||
All string literals are of the type `string`:idx:. A string in Nimrod is very
|
||||
similar to a sequence of characters. However, strings in Nimrod both are
|
||||
similar to a sequence of characters. However, strings in Nimrod are both
|
||||
zero-terminated and have a length field. One can retrieve the length with the
|
||||
builtin ``len`` procedure; the length never counts the terminating zero.
|
||||
The assignment operator for strings always copies the string.
|
||||
|
|
@ -585,7 +586,7 @@ arrays, they can be used in case statements:
|
|||
Per convention, all strings are UTF-8 strings, but this is not enforced. For
|
||||
example, when reading strings from binary files, they are merely a sequence of
|
||||
bytes. The index operation ``s[i]`` means the i-th *char* of ``s``, not the
|
||||
i-th *unichar*. The iterator ``unichars`` from the ``unicode`` standard
|
||||
i-th *unichar*. The iterator ``runes`` from the ``unicode``
|
||||
module can be used for iteration over all unicode characters.
|
||||
|
||||
|
||||
|
|
@ -611,9 +612,7 @@ constructed by the array constructor ``[]`` in conjunction with the array to
|
|||
sequence operator ``@``. Another way to allocate space for a sequence is to
|
||||
call the built-in ``newSeq`` procedure.
|
||||
|
||||
A sequence may be passed to a parameter that is of type *open array*, but
|
||||
not to a multi-dimensional open array, because it is impossible to do so in an
|
||||
efficient manner.
|
||||
A sequence may be passed to a parameter that is of type *open array*.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -639,6 +638,24 @@ Arrays are always bounds checked (at compile-time or at runtime). These
|
|||
checks can be disabled via pragmas or invoking the compiler with the
|
||||
``--bound_checks:off`` command line switch.
|
||||
|
||||
An open array is also a means to implement passing a variable number of
|
||||
arguments to a procedure. The compiler converts the list of arguments
|
||||
to an array automatically:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc myWriteln(f: TFile, a: openarray[string]) =
|
||||
for s in items(a):
|
||||
write(f, s)
|
||||
write(f, "\n")
|
||||
|
||||
myWriteln(stdout, "abc", "def", "xyz")
|
||||
# is transformed by the compiler to:
|
||||
myWriteln(stdout, ["abc", "def", "xyz"])
|
||||
|
||||
This transformation is only done if the openarray parameter is the
|
||||
last parameter in the procedure header. The current implementation does not
|
||||
support nested open arrays.
|
||||
|
||||
|
||||
Tuples and object types
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
@ -691,7 +708,7 @@ the ``is`` operator can be used to determine the object's type.
|
|||
person: TPerson
|
||||
assert(student is TStudent) # is true
|
||||
|
||||
Object fields that should be visible outside from the defining module, have to
|
||||
Object fields that should be visible from outside the defining module, have to
|
||||
marked by ``*``. In contrast to tuples, different object types are
|
||||
never *equivalent*.
|
||||
|
||||
|
|
@ -904,9 +921,9 @@ Most calling conventions exist only for the Windows 32-bit platform.
|
|||
|
||||
|
||||
|
||||
Statements
|
||||
----------
|
||||
Nimrod uses the common statement/expression paradigma: `Statements`:idx: do not
|
||||
Statements and expressions
|
||||
--------------------------
|
||||
Nimrod uses the common statement/expression paradigm: `Statements`:idx: do not
|
||||
produce a value in contrast to expressions. Call expressions are statements.
|
||||
If the called procedure returns a value, it is not a valid statement
|
||||
as statements do not produce values. To evaluate an expression for
|
||||
|
|
@ -943,7 +960,7 @@ Discard statement
|
|||
|
||||
Syntax::
|
||||
|
||||
discardStmt ::= DISCARD expr
|
||||
discardStmt ::= 'discard' expr
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -962,11 +979,13 @@ Var statement
|
|||
|
||||
Syntax::
|
||||
|
||||
colonOrEquals ::= COLON typeDesc [EQUALS expr] | EQUALS expr
|
||||
varField ::= symbol ["*"] [pragma]
|
||||
colonOrEquals ::= ':' typeDesc ['=' expr] | '=' expr
|
||||
varField ::= symbol ['*'] [pragma]
|
||||
varPart ::= symbol (comma symbol)* [comma] colonOrEquals [COMMENT | IND COMMENT]
|
||||
varSection ::= VAR (varPart
|
||||
| indPush (COMMENT|varPart) (SAD (COMMENT|varPart))* DED)
|
||||
varSection ::= 'var' (varPart
|
||||
| indPush (COMMENT|varPart)
|
||||
(SAD (COMMENT|varPart))* DED indPop)
|
||||
|
||||
|
||||
`Var`:idx: statements declare new local and global variables and
|
||||
initialize them. A comma seperated list of variables can be used to specify
|
||||
|
|
@ -992,7 +1011,7 @@ char '\0'
|
|||
bool false
|
||||
ref or pointer type nil
|
||||
procedural type nil
|
||||
sequence nil
|
||||
sequence nil (**not** ``@[]``)
|
||||
string nil (**not** "")
|
||||
tuple[x: A, y: B, ...] (default(A), default(B), ...)
|
||||
(analogous for objects)
|
||||
|
|
@ -1007,12 +1026,12 @@ Const section
|
|||
|
||||
Syntax::
|
||||
|
||||
colonAndEquals ::= [COLON typeDesc] EQUALS expr
|
||||
constDecl ::= CONST
|
||||
indPush
|
||||
symbol ["*"] [pragma] colonAndEquals
|
||||
(SAD symbol ["*"] [pragma] colonAndEquals)*
|
||||
DED
|
||||
colonAndEquals ::= [':' typeDesc] '=' expr
|
||||
|
||||
constDecl ::= symbol ['*'] [pragma] colonAndEquals [COMMENT | IND COMMENT]
|
||||
| COMMENT
|
||||
constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1031,7 +1050,7 @@ If statement
|
|||
|
||||
Syntax::
|
||||
|
||||
ifStmt ::= IF expr COLON stmt (ELIF expr COLON stmt)* [ELSE COLON stmt]
|
||||
ifStmt ::= 'if' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1061,9 +1080,9 @@ Case statement
|
|||
|
||||
Syntax::
|
||||
|
||||
caseStmt ::= CASE expr (OF sliceList COLON stmt)*
|
||||
(ELIF expr COLON stmt)*
|
||||
[ELSE COLON stmt]
|
||||
caseStmt ::= 'case' expr ('of' sliceExprList ':' stmt)*
|
||||
('elif' expr ':' stmt)*
|
||||
['else' ':' stmt]
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1094,7 +1113,7 @@ When statement
|
|||
|
||||
Syntax::
|
||||
|
||||
whenStmt ::= WHEN expr COLON stmt (ELIF expr COLON stmt)* [ELSE COLON stmt]
|
||||
whenStmt ::= 'when' expr ':' stmt ('elif' expr ':' stmt)* ['else' ':' stmt]
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1116,8 +1135,7 @@ exceptions:
|
|||
* The statements do not open a new scope if they introduce new identifiers.
|
||||
* The statements that belong to the expression that evaluated to true are
|
||||
translated by the compiler, the other statements are not checked for
|
||||
syntax or semantics at all! This holds also for any ``expr`` coming
|
||||
after the expression that evaluated to true.
|
||||
semantics! However, each ``expr`` is checked for semantics.
|
||||
|
||||
The ``when`` statement enables conditional compilation techniques. As
|
||||
a special syntatic extension, the ``when`` construct is also available
|
||||
|
|
@ -1129,7 +1147,7 @@ Raise statement
|
|||
|
||||
Syntax::
|
||||
|
||||
raiseStmt ::= RAISE [expr]
|
||||
raiseStmt ::= 'raise' [expr]
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1152,11 +1170,11 @@ Try statement
|
|||
|
||||
Syntax::
|
||||
|
||||
qualifiedIdent ::= symbol ['.' symbol]
|
||||
exceptList ::= [qualifiedIdent (comma qualifiedIdent)* [comma]]
|
||||
tryStmt ::= TRY COLON stmt
|
||||
(EXCEPT exceptList COLON stmt)*
|
||||
[FINALLY COLON stmt]
|
||||
|
||||
tryStmt ::= 'try' ':' stmt
|
||||
('except' exceptList ':' stmt)*
|
||||
['finally' ':' stmt]
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1176,6 +1194,8 @@ Example:
|
|||
echo("could not convert string to integer")
|
||||
except EIO:
|
||||
echo("IO error!")
|
||||
except:
|
||||
echo("Unknown exception!")
|
||||
finally:
|
||||
closeFile(f)
|
||||
|
||||
|
|
@ -1203,7 +1223,7 @@ Return statement
|
|||
|
||||
Syntax::
|
||||
|
||||
returnStmt ::= RETURN [expr]
|
||||
returnStmt ::= 'return' [expr]
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1224,7 +1244,8 @@ value of the procedure. It is automatically declared by the compiler. As all
|
|||
variables, ``result`` is initialized to (binary) zero::
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc returnZero(): int = nil # implicitely returns 0
|
||||
proc returnZero(): int =
|
||||
# implicitely returns 0
|
||||
|
||||
|
||||
Yield statement
|
||||
|
|
@ -1232,7 +1253,7 @@ Yield statement
|
|||
|
||||
Syntax::
|
||||
|
||||
yieldStmt ::= YIELD expr
|
||||
yieldStmt ::= 'yield' expr
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1252,7 +1273,7 @@ Block statement
|
|||
|
||||
Syntax::
|
||||
|
||||
blockStmt ::= BLOCK [symbol] COLON stmt
|
||||
blockStmt ::= 'block' [symbol] ':' stmt
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1277,7 +1298,7 @@ Break statement
|
|||
|
||||
Syntax::
|
||||
|
||||
breakStmt ::= BREAK [symbol]
|
||||
breakStmt ::= 'break' [symbol]
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1294,7 +1315,7 @@ While statement
|
|||
|
||||
Syntax::
|
||||
|
||||
whileStmt ::= WHILE expr COLON stmt
|
||||
whileStmt ::= 'while' expr ':' stmt
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1316,7 +1337,7 @@ Continue statement
|
|||
|
||||
Syntax::
|
||||
|
||||
continueStmt ::= CONTINUE
|
||||
continueStmt ::= 'continue'
|
||||
|
||||
A `continue`:idx: statement leads to the immediate next iteration of the
|
||||
surrounding loop construct. It is only allowed within a loop. A continue
|
||||
|
|
@ -1340,7 +1361,7 @@ Assembler statement
|
|||
~~~~~~~~~~~~~~~~~~~
|
||||
Syntax::
|
||||
|
||||
asmStmt ::= ASM [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
|
||||
asmStmt ::= 'asm' [pragma] (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
|
||||
|
||||
The direct embedding of `assembler`:idx: code into Nimrod code is supported
|
||||
by the unsafe ``asm`` statement. Identifiers in the assembler code that refer to
|
||||
|
|
@ -1348,6 +1369,49 @@ Nimrod identifiers shall be enclosed in a special character which can be
|
|||
specified in the statement's pragmas. The default special character is ``'`'``.
|
||||
|
||||
|
||||
If expression
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
An `if expression` is almost like an if statement, but it is an expression.
|
||||
Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
p(if x > 8: 9 else: 10)
|
||||
|
||||
An if expression always results in a value, so the ``else`` part is
|
||||
required. ``Elif`` parts are also allowed (but unlikely to be good
|
||||
style).
|
||||
|
||||
|
||||
Type convertions
|
||||
~~~~~~~~~~~~~~~~
|
||||
Syntactically a `type conversion` is like a procedure call, but a
|
||||
type name replaces the procedure name. A type conversion is always
|
||||
safe in the sense that a failure to convert a type to another
|
||||
results in an exception (if it cannot be determined statically).
|
||||
|
||||
|
||||
Type casts
|
||||
~~~~~~~~~~
|
||||
Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
cast[int](x)
|
||||
|
||||
Type casts are a crude mechanism to interpret the bit pattern of
|
||||
an expression as if it would be of another type. Type casts are
|
||||
only needed for low-level programming and are inherently unsafe.
|
||||
|
||||
|
||||
The addr operator
|
||||
~~~~~~~~~~~~~~~~~
|
||||
The `addr` operator returns the address of an l-value. If the
|
||||
type of the location is ``T``, the `addr` operator result is
|
||||
of the type ``ptr T``. Taking the address of an object that resides
|
||||
on the stack is **unsafe**, as the pointer may live longer than the
|
||||
object on the stack and can thus reference a non-existing object.
|
||||
|
||||
|
||||
Procedures
|
||||
~~~~~~~~~~
|
||||
What most programming languages call `methods`:idx: or `funtions`:idx: are
|
||||
|
|
@ -1355,16 +1419,16 @@ called `procedures`:idx: in Nimrod (which is the correct terminology). A
|
|||
procedure declaration defines an identifier and associates it with a block
|
||||
of code. A procedure may call itself recursively. The syntax is::
|
||||
|
||||
param ::= symbol (comma symbol)* [comma] COLON typeDesc
|
||||
paramList ::= [PAR_LE [param (comma param)* [comma]] PAR_RI] [COLON typeDesc]
|
||||
param ::= symbol (comma symbol)* [comma] ':' typeDesc
|
||||
paramList ::= ['(' [param (comma param)* [comma]] ')'] [':' typeDesc]
|
||||
|
||||
genericParams ::= BRACKET_LE (symbol [EQUALS typeDesc] )* BRACKET_RI
|
||||
genericParam ::= symbol [':' typeDesc]
|
||||
genericParams ::= '[' genericParam (comma genericParam)* [comma] ']'
|
||||
|
||||
procDecl ::= PROC symbol ["*"] [genericParams]
|
||||
paramList [pragma]
|
||||
[EQUALS stmt]
|
||||
procDecl ::= 'proc' symbol ['*'] [genericParams] paramList [pragma]
|
||||
['=' stmt]
|
||||
|
||||
If the ``EQUALS stmt`` part is missing, it is a `forward`:idx: declaration. If
|
||||
If the ``= stmt`` part is missing, it is a `forward`:idx: declaration. If
|
||||
the proc returns a value, the procedure body can access an implicit declared
|
||||
variable named `result`:idx: that represents the return value. Procs can be
|
||||
overloaded. The overloading resolution algorithm tries to find the proc that is
|
||||
|
|
@ -1384,6 +1448,24 @@ is used if the caller does not provide a value for this parameter. Example:
|
|||
for i in 0..len(s) - 1:
|
||||
result[i] = toLower(s[i]) # calls toLower for characters; no recursion!
|
||||
|
||||
Calling a procedure can be done in many different ways:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ...
|
||||
|
||||
# call with positional arguments # parameter bindings:
|
||||
callme(0, 1, "abc", '\t', true) # (x=0, y=1, s="abc", c='\t', b=true)
|
||||
# call with named and positional arguments:
|
||||
callme(y=1, x=0, "abd", '\t') # (x=0, y=1, s="abd", c='\t', b=false)
|
||||
# call with named arguments (order is not relevant):
|
||||
callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false)
|
||||
# call as a command statement: no () needed:
|
||||
callme 0, 1, "abc", '\t'
|
||||
|
||||
|
||||
A procedure cannot modify its parameters (unless the parameters have the
|
||||
type `var`).
|
||||
|
||||
`Operators`:idx: are procedures with a special operator symbol as identifier:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
|
@ -1391,19 +1473,79 @@ is used if the caller does not provide a value for this parameter. Example:
|
|||
# converts an integer to a string; this is a prefix operator.
|
||||
return intToStr(x)
|
||||
|
||||
Calling a procedure can be done in many different ways:
|
||||
Operators with one parameter are prefix operators, operators with two
|
||||
parameters are infix operators. There is no way to declare postfix
|
||||
operators: All postfix operators are built-in and handled by the
|
||||
grammar explicitely.
|
||||
|
||||
Any operator can be called like an ordinary proc with the '`opr`'
|
||||
notation. (Thus an operator can have more than two parameters):
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ...
|
||||
proc `*+` (a, b, c: int): int =
|
||||
# Multiply and add
|
||||
return a * b + c
|
||||
|
||||
assert `*+`(3, 4, 6) == `*`(a, `+`(b, c))
|
||||
|
||||
|
||||
|
||||
Var parameters
|
||||
~~~~~~~~~~~~~~
|
||||
The type of a parameter may be prefixed with the ``var`` keyword:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc divmod(a, b: int, res, remainder: var int) =
|
||||
res = a div b
|
||||
remainder = a mod b
|
||||
|
||||
var
|
||||
x, y: int
|
||||
|
||||
divmod(8, 5, x, y) # modifies x and y
|
||||
assert x == 1
|
||||
assert y == 3
|
||||
|
||||
In the example, ``res`` and ``remainder`` are `var parameters`.
|
||||
Var parameters can be modified by the procedure and the changes are
|
||||
visible to the caller. The argument passed to a var parameter has to be
|
||||
an l-value. Var parameters are implemented as hidden pointers. The
|
||||
above example is equivalent to:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc divmod(a, b: int, res, remainder: ptr int) =
|
||||
res = a div b
|
||||
remainder = a mod b
|
||||
|
||||
var
|
||||
x, y: int
|
||||
divmod(8, 5, addr(x), addr(y))
|
||||
assert x == 1
|
||||
assert y == 3
|
||||
|
||||
In the examples, var parameters or pointers are used to provide two
|
||||
return values. This can be done in a cleaner way by returning a tuple:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc divmod(a, b: int): tuple[res, remainder: int] =
|
||||
return (a div b, a mod b)
|
||||
|
||||
var t = divmod(8, 5)
|
||||
assert t.res == 1
|
||||
assert t.remainder = 3
|
||||
|
||||
Even more elegant is to use `tuple unpacking` to access the tuple's fields:
|
||||
|
||||
.. code-block:: nimrod
|
||||
var (x, y) = divmod(8, 5) # tuple unpacking
|
||||
assert x == 1
|
||||
assert y == 3
|
||||
|
||||
Unfortunately, this form of tuple unpacking is not yet implemented.
|
||||
|
||||
..
|
||||
XXX remove this as soon as tuple unpacking is implemented
|
||||
|
||||
# call with positional arguments# parameter bindings:
|
||||
callme(0, 1, "abc", '\t', true) # (x=0, y=1, s="abc", c='\t', b=true)
|
||||
# call with named and positional arguments:
|
||||
callme(y=1, x=0, "abd", '\t') # (x=0, y=1, s="abd", c='\t', b=false)
|
||||
# call with named arguments (order is not relevant):
|
||||
callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false)
|
||||
# call as a command statement: no () needed:
|
||||
callme 0, 1, "abc", '\t'
|
||||
|
||||
|
||||
Iterators and the for statement
|
||||
|
|
@ -1411,15 +1553,16 @@ Iterators and the for statement
|
|||
|
||||
Syntax::
|
||||
|
||||
forStmt ::= FOR symbol (comma symbol)* [comma] IN expr [DOTDOT expr] COLON stmt
|
||||
forStmt ::= 'for' symbol (comma symbol)* [comma] 'in' expr ['..' expr] ':' stmt
|
||||
|
||||
param ::= symbol (comma symbol)* [comma] COLON typeDesc
|
||||
paramList ::= [PAR_LE [param (comma param)* [comma]] PAR_RI] [COLON typeDesc]
|
||||
param ::= symbol (comma symbol)* [comma] ':' typeDesc
|
||||
paramList ::= ['(' [param (comma param)* [comma]] ')'] [':' typeDesc]
|
||||
|
||||
genericParams ::= BRACKET_LE (symbol [EQUALS typeDesc] )* BRACKET_RI
|
||||
genericParam ::= symbol [':' typeDesc]
|
||||
genericParams ::= '[' genericParam (comma genericParam)* [comma] ']'
|
||||
|
||||
iteratorDecl ::= ITERATOR symbol ["*"] [genericParams] paramList [pragma]
|
||||
[EQUALS stmt]
|
||||
iteratorDecl ::= 'iterator' symbol ['*'] [genericParams] paramList [pragma]
|
||||
['=' stmt]
|
||||
|
||||
The `for`:idx: statement is an abstract mechanism to iterate over the elements
|
||||
of a container. It relies on an `iterator`:idx: to do so. Like ``while``
|
||||
|
|
@ -1473,13 +1616,15 @@ Type sections
|
|||
Syntax::
|
||||
|
||||
typeDef ::= typeDesc | objectDef | enumDef
|
||||
genericParams ::= BRACKET_LE (symbol [EQUALS typeDesc] )* BRACKET_RI
|
||||
|
||||
typeDecl ::= TYPE
|
||||
indPush
|
||||
symbol ["*"] [genericParams] [EQUALS typeDef]
|
||||
(SAD symbol ["*"] [genericParams] [EQUALS typeDef])*
|
||||
DED
|
||||
genericParam ::= symbol [':' typeDesc]
|
||||
genericParams ::= '[' genericParam (comma genericParam)* [comma] ']'
|
||||
|
||||
typeDecl ::= COMMENT
|
||||
| symbol ['*'] [genericParams] ['=' typeDef] [COMMENT|IND COMMENT]
|
||||
|
||||
typeSection ::= 'type' indPush typeDecl (SAD typeDecl)* DED indPop
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -1504,6 +1649,8 @@ possible within a single ``type`` section.
|
|||
Generics
|
||||
~~~~~~~~
|
||||
|
||||
`Version 0.7.4: Complex generic types like in the example do not work.`:red:
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
|
@ -1578,17 +1725,110 @@ Example:
|
|||
# this definition exists in the System module
|
||||
not (a == b)
|
||||
|
||||
writeln(5 != 6) # the compiler rewrites that to: writeln(not (5 == 6))
|
||||
assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))
|
||||
|
||||
|
||||
Macros
|
||||
~~~~~~
|
||||
------
|
||||
|
||||
`Macros`:idx: are the most powerful feature of Nimrod. They should be used
|
||||
only to implement `domain specific languages`:idx:. They may lead to code
|
||||
`Macros`:idx: are the most powerful feature of Nimrod. They can be used
|
||||
to implement `domain specific languages`:idx:. But they may lead to code
|
||||
that is harder to understand and maintain. So one ought to use them sparingly.
|
||||
The usage of ordinary procs, iterators or generics is preferred to the usage of
|
||||
macros.
|
||||
|
||||
While macros enable advanced compile-time code tranformations, they
|
||||
cannot change Nimrod's syntax. However, this is no real restriction because
|
||||
Nimrod's syntax is flexible enough anyway.
|
||||
|
||||
To write macros, one needs to know how the Nimrod concrete syntax is converted
|
||||
to an abstract syntax tree. (Unfortunately the AST is not yet documented.)
|
||||
|
||||
There are two ways to invoke a macro:
|
||||
(1) invoking a macro like a procedure call (`expression macros`)
|
||||
(2) invoking a macro with the special ``macrostmt`` syntax (`statement macros`)
|
||||
|
||||
|
||||
Expression Macros
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following example implements a powerful ``debug`` command that accepts a
|
||||
variable number of arguments:
|
||||
|
||||
.. code-block:: nimrod
|
||||
# to work with Nimrod syntax trees, we need an API that is defined in the
|
||||
# ``macros`` module:
|
||||
import macros
|
||||
|
||||
macro debug(n: expr): stmt =
|
||||
# `n` is a Nimrod AST that contains the whole macro expression
|
||||
# this macro returns a list of statements:
|
||||
result = newNimNode(nnkStmtList, n)
|
||||
# iterate over any argument that is passed to this macro:
|
||||
for i in 1..n.len-1:
|
||||
# add a call to the statement list that writes the expression;
|
||||
# `toStrLit` converts an AST to its string representation:
|
||||
add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i])))
|
||||
# add a call to the statement list that writes ": "
|
||||
add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
|
||||
# add a call to the statement list that writes the expressions value:
|
||||
add(result, newCall("writeln", newIdentNode("stdout"), n[i]))
|
||||
|
||||
var
|
||||
a: array [0..10, int]
|
||||
x = "some string"
|
||||
a[0] = 42
|
||||
a[1] = 45
|
||||
|
||||
debug(a[0], a[1], x)
|
||||
|
||||
The macro call expands to:
|
||||
|
||||
.. code-block:: nimrod
|
||||
write(stdout, "a[0]")
|
||||
write(stdout, ": ")
|
||||
writeln(stdout, a[0])
|
||||
|
||||
write(stdout, "a[1]")
|
||||
write(stdout, ": ")
|
||||
writeln(stdout, a[1])
|
||||
|
||||
write(stdout, "x")
|
||||
write(stdout, ": ")
|
||||
writeln(stdout, x)
|
||||
|
||||
|
||||
Statement Macros
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Statement macros are defined just as expression macros. However, they are
|
||||
invoked by an expression following a colon::
|
||||
|
||||
exprStmt ::= lowestExpr ['=' expr | [expr (comma expr)* [comma]] [macroStmt]]
|
||||
macroStmt ::= ':' [stmt] ('of' [sliceExprList] ':' stmt
|
||||
| 'elif' expr ':' stmt
|
||||
| 'except' exceptList ':' stmt )*
|
||||
['else' ':' stmt]
|
||||
|
||||
The following example outlines a macro that generates a lexical analyser from
|
||||
regular expressions:
|
||||
|
||||
.. code-block:: nimrod
|
||||
import macros
|
||||
|
||||
macro case_token(n: stmt): stmt =
|
||||
# creates a lexical analyser from regular expressions
|
||||
# ... (implementation is an exercise for the reader :-)
|
||||
nil
|
||||
|
||||
case_token: # this colon tells the parser it is a macro statement
|
||||
of r"[A-Za-z_]+[A-Za-z_0-9]*":
|
||||
return tkIdentifier
|
||||
of r"0-9+":
|
||||
return tkInteger
|
||||
of r"[\+\-\*\?]+":
|
||||
return tkOperator
|
||||
else:
|
||||
return tkUnknown
|
||||
|
||||
|
||||
|
||||
Modules
|
||||
|
|
@ -1622,12 +1862,12 @@ This is best illustrated by an example:
|
|||
|
||||
# Module B
|
||||
import A # A is not parsed here! Only the already known symbols
|
||||
# of A are imported here.
|
||||
# of A are imported.
|
||||
|
||||
proc p*(x: A.T1): A.T1 # this works because the compiler has already
|
||||
# added T1 to A's interface symbol table
|
||||
|
||||
proc p(x: A.T1): A.T1 = return x + 1
|
||||
proc p*(x: A.T1): A.T1 =
|
||||
# this works because the compiler has already
|
||||
# added T1 to A's interface symbol table
|
||||
return x + 1
|
||||
|
||||
|
||||
Scope rules
|
||||
|
|
@ -1649,7 +1889,7 @@ procedure or iterator overloading purposes.
|
|||
|
||||
|
||||
Tuple or object scope
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
The field identifiers inside a tuple or object definition are valid in the
|
||||
following places:
|
||||
|
||||
|
|
@ -1659,16 +1899,14 @@ following places:
|
|||
|
||||
Module scope
|
||||
~~~~~~~~~~~~
|
||||
All identifiers in the interface part of a module are valid from the point of
|
||||
declaration, until the end of the module. Furthermore, the identifiers are
|
||||
known in other modules that import the module. Identifiers from indirectly
|
||||
dependent modules are *not* available. The `system`:idx: module is automatically
|
||||
imported in all other modules.
|
||||
All identifiers of a module are valid from the point of declaration until
|
||||
the end of the module. Identifiers from indirectly dependent modules are *not*
|
||||
available. The `system`:idx: module is automatically imported in every other
|
||||
module.
|
||||
|
||||
If a module imports an identifier by two different modules,
|
||||
each occurance of the identifier has to be qualified, unless it is an
|
||||
overloaded procedure or iterator in which case the overloading
|
||||
resolution takes place:
|
||||
If a module imports an identifier by two different modules, each occurance of
|
||||
the identifier has to be qualified, unless it is an overloaded procedure or
|
||||
iterator in which case the overloading resolution takes place:
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Module A
|
||||
|
|
@ -1680,7 +1918,7 @@ resolution takes place:
|
|||
# Module C
|
||||
import A, B
|
||||
write(stdout, x) # error: x is ambigious
|
||||
write(sdtout, A.x) # no error: qualifier used
|
||||
write(stdout, A.x) # no error: qualifier used
|
||||
|
||||
var x = 4
|
||||
write(stdout, x) # not ambigious: uses the module C's x
|
||||
|
|
@ -1698,14 +1936,14 @@ Pragmas
|
|||
|
||||
Syntax::
|
||||
|
||||
colonExpr ::= expr [COLON expr]
|
||||
colonExprList ::= [ colonExpr (comma colonExpr)* [comma] ]
|
||||
colonExpr ::= expr [':' expr]
|
||||
colonExprList ::= [colonExpr (comma colonExpr)* [comma]]
|
||||
|
||||
pragma ::= CURLYDOT_LE colonExprList (CURLYDOT_RI | CURLY_RI)
|
||||
pragma ::= '{.' optInd (colonExpr [comma])* [SAD] ('.}' | '}')
|
||||
|
||||
Pragmas are Nimrod's method to give the compiler additional information/
|
||||
commands without introducing a massive number of new keywords. Pragmas are
|
||||
processed on the fly during parsing. Pragmas are always enclosed in the
|
||||
processed on the fly during semantic checking. Pragmas are enclosed in the
|
||||
special ``{.`` and ``.}`` curly brackets.
|
||||
|
||||
|
||||
|
|
@ -1759,10 +1997,6 @@ compilation option pragmas
|
|||
--------------------------
|
||||
The listed pragmas here can be used to override the code generation options
|
||||
for a section of code.
|
||||
::
|
||||
|
||||
"{." pragma: val {pragma: val} ".}"
|
||||
|
||||
|
||||
The implementation currently provides the following possible options (later
|
||||
various others may be added).
|
||||
|
|
@ -1785,8 +2019,7 @@ warnings on|off Turns the warning messages of the compiler
|
|||
hints on|off Turns the hint messages of the compiler
|
||||
on or off.
|
||||
optimization none|speed|size Optimize the code for speed or size, or
|
||||
disable optimization. For non-optimizing
|
||||
compilers this option has no effect.
|
||||
disable optimization.
|
||||
callconv cdecl|... Specifies the default calling convention for
|
||||
all procedures (and procedure types) that
|
||||
follow.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Introduction
|
|||
|
||||
This document describes the usage of the *Nimrod compiler*
|
||||
on the different supported platforms. It is not a definition of the Nimrod
|
||||
programming system (therefore is the Nimrod manual).
|
||||
programming language (therefore is the manual).
|
||||
|
||||
Nimrod is free software; it is licensed under the
|
||||
`GNU General Public License <gpl.html>`_.
|
||||
|
|
@ -61,14 +61,6 @@ However, the generated C code is not platform independant. C code generated for
|
|||
Linux does not compile on Windows, for instance. The comment on top of the
|
||||
C file lists the OS, CPU and CC the file has been compiled for.
|
||||
|
||||
The library lies in ``lib``. Directly in the library directory are essential
|
||||
Nimrod modules like the ``system`` and ``os`` modules. Under ``lib/base``
|
||||
are additional specialized libraries or interfaces to foreign libraries which
|
||||
are included in the standard distribution. The ``lib/extra`` directory is
|
||||
initially empty. Third party libraries should go there. In the default
|
||||
configuration the compiler always searches for libraries in ``lib``,
|
||||
``lib/base`` and ``lib/extra``.
|
||||
|
||||
|
||||
Additional Features
|
||||
===================
|
||||
|
|
@ -128,8 +120,8 @@ the C code. Thus it makes the following possible, for example:
|
|||
|
||||
.. code-block:: Nimrod
|
||||
var
|
||||
EOF {.importc: "EOF", no_decl.}: cint # pretend EOF was a variable, as
|
||||
# Nimrod does not know its value
|
||||
EACCES {.importc, no_decl.}: cint # pretend EACCES was a variable, as
|
||||
# Nimrod does not know its value
|
||||
|
||||
However, the ``header`` pragma is often the better alternative.
|
||||
|
||||
|
|
@ -164,14 +156,6 @@ strings automatically:
|
|||
printf("hallo %s", "world") # "world" will be passed as C string
|
||||
|
||||
|
||||
No_static Pragma
|
||||
~~~~~~~~~~~~~~~~
|
||||
The `no_static`:idx: pragma can be applied to almost any symbol and specifies
|
||||
that it shall not be declared ``static`` in the generated C code. Note that
|
||||
symbols in the interface part of a module never get declared ``static``, so
|
||||
only in very special cases this pragma is necessary.
|
||||
|
||||
|
||||
Line_dir Option
|
||||
~~~~~~~~~~~~~~~
|
||||
The `line_dir`:idx: option can be turned on or off. If on the generated C code
|
||||
|
|
@ -240,6 +224,22 @@ information that this cannot happen to the GC. If the programmer uses the
|
|||
memory, but nothing worse happens.
|
||||
|
||||
|
||||
Dead_code_elim Pragma
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
The `dead_code_elim`:idx: pragma only applies to whole modules: It tells the
|
||||
compiler to active (or deactivate) dead code elimination for the module the
|
||||
pragma appers in.
|
||||
|
||||
The ``--dead_code_elim:on`` command line switch has the same effect as marking
|
||||
any module with ``{.dead_code_elim:on}``. However, for some modules such as
|
||||
the GTK wrapper it makes sense to *always* turn on dead code elimination -
|
||||
no matter if it is globally active or not.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: nimrod
|
||||
{.dead_code_elim: on.}
|
||||
|
||||
|
||||
Disabling certain messages
|
||||
--------------------------
|
||||
|
|
@ -280,8 +280,8 @@ However, sometimes one has to optimize. Do it in the following order:
|
|||
|
||||
This section can only help you with the last item. Note that rewriting parts
|
||||
of your program in C is *never* necessary to speed up your program, because
|
||||
everything that can be done in C can be done in Nimrod. Rewriting parts in
|
||||
assembler *might*.
|
||||
everything that can be done in C can be done in Nimrod.
|
||||
|
||||
|
||||
Optimizing string handling
|
||||
--------------------------
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ compatible to the original implementation as one would like.
|
|||
|
||||
Even though Nimrod's |rst| parser does not parse all constructs, it is pretty
|
||||
usable. The missing features can easily be circumvented. An indication of this
|
||||
fact is that Nimrod's
|
||||
*whole* documentation itself (including this document) is
|
||||
fact is that Nimrod's *whole* documentation itself (including this document) is
|
||||
processed by Nimrod's |rst| parser. (Which is an order of magnitude faster than
|
||||
Docutils' parser.)
|
||||
|
||||
|
|
|
|||
906
doc/theindex.txt
906
doc/theindex.txt
File diff suppressed because it is too large
Load diff
1382
doc/tut1.txt
Normal file
1382
doc/tut1.txt
Normal file
File diff suppressed because it is too large
Load diff
718
doc/tut2.txt
Normal file
718
doc/tut2.txt
Normal 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
|
||||
|
||||
|
||||
215
doc/tutorial.txt
215
doc/tutorial.txt
|
|
@ -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
|
||||
126
install.sh
126
install.sh
|
|
@ -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
79
koch.py
|
|
@ -18,7 +18,7 @@ False = 0 == 1
|
|||
|
||||
# --------------------- constants ----------------------------------------
|
||||
|
||||
NIMROD_VERSION = '0.7.2'
|
||||
NIMROD_VERSION = '0.7.4'
|
||||
# This string contains Nimrod's version. It is the only place
|
||||
# where the version needs to be updated. The rest is done by
|
||||
# the build process automatically. It is replaced **everywhere**
|
||||
|
|
@ -35,8 +35,6 @@ force = False
|
|||
|
||||
GENERATE_DIFF = False
|
||||
# if set, a diff.log file is generated when bootstrapping
|
||||
# this uses quite a good amount of RAM (ca. 12 MB), so it should not be done
|
||||
# on underpowered systems.
|
||||
|
||||
USE_FPC = True
|
||||
|
||||
|
|
@ -45,31 +43,6 @@ BOOTCMD = "%s cc --compile:build/platdef.c %s rod/nimrod.nim"
|
|||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
DOC = split("""endb intern lib manual nimrodc steps overview""")
|
||||
SRCDOC = split("""system os strutils base/regexprs math complex times
|
||||
parseopt hashes strtabs lexbase parsecfg base/dialogs
|
||||
posix/posix
|
||||
streams base/odbcsql
|
||||
base/zip/zipfiles base/zip/zlib base/zip/libzip
|
||||
""")
|
||||
|
||||
ADD_SRCDOC = split("""
|
||||
base/cairo/cairo base/cairo/cairoft
|
||||
base/cairo/cairowin32 base/cairo/cairoxlib
|
||||
base/gtk/atk base/gtk/gdk2 base/gtk/gdk2pixbuf
|
||||
base/gtk/gdkglext base/gtk/glib2 base/gtk/gtk2
|
||||
base/gtk/gtkglext base/gtk/gtkhtml base/gtk/libglade2
|
||||
base/gtk/pango base/gtk/pangoutils
|
||||
windows/windows windows/mmsystem windows/nb30
|
||||
windows/ole2 windows/shellapi windows/shfolder
|
||||
base/x11/*.nim
|
||||
base/opengl/*.nim
|
||||
base/sdl/*.nim
|
||||
base/lua/*.nim
|
||||
""")
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
def Error(msg): sys.exit("[Koch] *** ERROR: " + msg)
|
||||
def Warn(msg): print "[Koch] *** WARNING: " + msg
|
||||
def Echo(msg): print "[Koch] " + msg
|
||||
|
|
@ -140,7 +113,7 @@ BaseDir = _baseDir
|
|||
def Path(a):
|
||||
# Gets a UNIX like path and converts it to a path on this platform.
|
||||
# With UNIX like, I mean: slashes, not backslashes, only relative
|
||||
# paths ('../etc' can be used)
|
||||
# paths ('../etc') can be used
|
||||
result = a
|
||||
if os.sep != "/": result = replace(result, "/", os.sep)
|
||||
if os.pardir != "..": result = replace(result, "..", os.pardir)
|
||||
|
|
@ -401,12 +374,12 @@ Options:
|
|||
Possible Commands:
|
||||
nim builds the Pascal version of Nimrod
|
||||
rod [options] builds the Nimrod version of Nimrod (with options)
|
||||
doc builds the documentation in HTML
|
||||
clean cleans Nimrod project; removes generated files
|
||||
boot [options] bootstraps with given command line options
|
||||
rodsrc generates Nimrod version from Pascal version
|
||||
web generates the website
|
||||
profile profile the Nimrod compiler
|
||||
csource build the C sources for installation
|
||||
zip build the installation ZIP package
|
||||
inno build the Inno Setup installer
|
||||
""" % (NIMROD_VERSION + ' ' * (44-len(NIMROD_VERSION)), sys.version)
|
||||
|
|
@ -436,7 +409,6 @@ def main(args):
|
|||
cmd = args[i]
|
||||
if cmd == "rod": cmd_rod(join(args[i+1:]))
|
||||
elif cmd == "nim": cmd_nim()
|
||||
elif cmd == "doc": cmd_doc()
|
||||
elif cmd == "clean": cmd_clean()
|
||||
elif cmd == "boot": cmd_boot(join(args[i+1:]))
|
||||
elif cmd == "rodsrc": cmd_rodsrc()
|
||||
|
|
@ -444,11 +416,14 @@ def main(args):
|
|||
elif cmd == "profile": cmd_profile()
|
||||
elif cmd == "zip": cmd_zip()
|
||||
elif cmd == "inno": cmd_inno()
|
||||
elif cmd == "csource": cmd_csource()
|
||||
else: Error("illegal command: " + cmd)
|
||||
|
||||
def cmd_zip():
|
||||
def cmd_csource():
|
||||
Exec("nimrod cc -r tools/niminst --var:version=%s csource rod/nimrod" %
|
||||
NIMROD_VERSION)
|
||||
|
||||
def cmd_zip():
|
||||
Exec("nimrod cc -r tools/niminst --var:version=%s zip rod/nimrod" %
|
||||
NIMROD_VERSION)
|
||||
|
||||
|
|
@ -586,50 +561,10 @@ def cmd_profile():
|
|||
|
||||
# ------------------ web ------------------------------------------------------
|
||||
|
||||
def buildDoc(destPath):
|
||||
# call nim for the documentation:
|
||||
for d in DOC:
|
||||
Exec("nimrod rst2html --putenv:nimrodversion=%s -o:%s/%s.html "
|
||||
"--index=%s/theindex doc/%s" %
|
||||
(NIMROD_VERSION, destPath, d, destPath, d))
|
||||
for d in SRCDOC:
|
||||
Exec("nimrod doc --putenv:nimrodversion=%s -o:%s/%s.html "
|
||||
"--index=%s/theindex lib/%s" %
|
||||
(NIMROD_VERSION, destPath, FilenameNoExt(d), destPath, d))
|
||||
Exec("nimrod rst2html -o:%s/theindex.html %s/theindex" % (destPath, destPath))
|
||||
|
||||
def buildAddDoc(destPath):
|
||||
# build additional documentation (without the index):
|
||||
def build(d):
|
||||
c = Changed("web__"+d, ["lib/"+d+".nim"], EXPLAIN)
|
||||
if c.check() or force:
|
||||
Exec("nimrod doc --putenv:nimrodversion=%s -o:%s/%s.html "
|
||||
" lib/%s" % (NIMROD_VERSION, destPath, FilenameNoExt(d), d))
|
||||
c.success()
|
||||
|
||||
for a in ADD_SRCDOC:
|
||||
if '*' in a:
|
||||
for d in Glob("lib/" + a): build(d)
|
||||
else:
|
||||
build(a)
|
||||
|
||||
def cmd_web():
|
||||
Exec("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=%s"
|
||||
% NIMROD_VERSION)
|
||||
|
||||
# ------------------ doc ------------------------------------------------------
|
||||
|
||||
def cmd_doc():
|
||||
c = Changed("doc", ["koch.py"] +
|
||||
Glob("doc/*.txt") + Glob("lib/*.txt") + Glob("lib/*.nim")+
|
||||
Glob("config/*.cfg"),
|
||||
EXPLAIN)
|
||||
if c.check() or force:
|
||||
cmd_nim() # we need Nimrod for processing the documentation
|
||||
buildDoc("doc")
|
||||
if Exists("doc/overview.html"):
|
||||
c.success()
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
def getVersion():
|
||||
|
|
|
|||
157
lib/arithm.nim
157
lib/arithm.nim
|
|
@ -102,51 +102,16 @@ proc absInt(a: int): int {.compilerProc, inline.} =
|
|||
raiseOverflow()
|
||||
|
||||
const
|
||||
asmVersion = defined(I386) and (defined(vcc) or defined(wcc) or defined(dmc))
|
||||
asmVersion = defined(I386) and (defined(vcc) or defined(wcc) or
|
||||
defined(dmc) or defined(gcc) or defined(llvm_gcc))
|
||||
# my Version of Borland C++Builder does not have
|
||||
# tasm32, which is needed for assembler blocks
|
||||
# this is why Borland is not included in the 'when'
|
||||
useInline = not asmVersion
|
||||
|
||||
when asmVersion and defined(gcc):
|
||||
proc addInt(a, b: int): int {.compilerProc, pure, inline.}
|
||||
proc subInt(a, b: int): int {.compilerProc, pure, inline.}
|
||||
proc mulInt(a, b: int): int {.compilerProc, pure, inline.}
|
||||
proc divInt(a, b: int): int {.compilerProc, pure, inline.}
|
||||
proc modInt(a, b: int): int {.compilerProc, pure, inline.}
|
||||
proc negInt(a: int): int {.compilerProc, pure, inline.}
|
||||
|
||||
elif asmVersion:
|
||||
proc addInt(a, b: int): int {.compilerProc, pure.}
|
||||
proc subInt(a, b: int): int {.compilerProc, pure.}
|
||||
proc mulInt(a, b: int): int {.compilerProc, pure.}
|
||||
proc divInt(a, b: int): int {.compilerProc, pure.}
|
||||
proc modInt(a, b: int): int {.compilerProc, pure.}
|
||||
proc negInt(a: int): int {.compilerProc, pure.}
|
||||
|
||||
elif useInline:
|
||||
proc addInt(a, b: int): int {.compilerProc, inline.}
|
||||
proc subInt(a, b: int): int {.compilerProc, inline.}
|
||||
proc mulInt(a, b: int): int {.compilerProc.}
|
||||
# mulInt is to large for inlining?
|
||||
proc divInt(a, b: int): int {.compilerProc, inline.}
|
||||
proc modInt(a, b: int): int {.compilerProc, inline.}
|
||||
proc negInt(a: int): int {.compilerProc, inline.}
|
||||
|
||||
else:
|
||||
proc addInt(a, b: int): int {.compilerProc.}
|
||||
proc subInt(a, b: int): int {.compilerProc.}
|
||||
proc mulInt(a, b: int): int {.compilerProc.}
|
||||
proc divInt(a, b: int): int {.compilerProc.}
|
||||
proc modInt(a, b: int): int {.compilerProc.}
|
||||
proc negInt(a: int): int {.compilerProc.}
|
||||
|
||||
# implementation:
|
||||
|
||||
when asmVersion and not defined(gcc):
|
||||
when asmVersion and not defined(gcc) and not defined(llvm_gcc):
|
||||
# assembler optimized versions for compilers that
|
||||
# have an intel syntax assembler:
|
||||
proc addInt(a, b: int): int =
|
||||
proc addInt(a, b: int): int {.compilerProc, pure.} =
|
||||
# a in eax, and b in edx
|
||||
asm """
|
||||
mov eax, `a`
|
||||
|
|
@ -156,7 +121,7 @@ when asmVersion and not defined(gcc):
|
|||
theEnd:
|
||||
"""
|
||||
|
||||
proc subInt(a, b: int): int =
|
||||
proc subInt(a, b: int): int {.compilerProc, pure.} =
|
||||
asm """
|
||||
mov eax, `a`
|
||||
sub eax, `b`
|
||||
|
|
@ -165,7 +130,7 @@ when asmVersion and not defined(gcc):
|
|||
theEnd:
|
||||
"""
|
||||
|
||||
proc negInt(a: int): int =
|
||||
proc negInt(a: int): int {.compilerProc, pure.} =
|
||||
asm """
|
||||
mov eax, `a`
|
||||
neg eax
|
||||
|
|
@ -174,7 +139,7 @@ when asmVersion and not defined(gcc):
|
|||
theEnd:
|
||||
"""
|
||||
|
||||
proc divInt(a, b: int): int =
|
||||
proc divInt(a, b: int): int {.compilerProc, pure.} =
|
||||
asm """
|
||||
mov eax, `a`
|
||||
mov ecx, `b`
|
||||
|
|
@ -185,7 +150,7 @@ when asmVersion and not defined(gcc):
|
|||
theEnd:
|
||||
"""
|
||||
|
||||
proc modInt(a, b: int): int =
|
||||
proc modInt(a, b: int): int {.compilerProc, pure.} =
|
||||
asm """
|
||||
mov eax, `a`
|
||||
mov ecx, `b`
|
||||
|
|
@ -197,7 +162,7 @@ when asmVersion and not defined(gcc):
|
|||
mov eax, edx
|
||||
"""
|
||||
|
||||
proc mulInt(a, b: int): int =
|
||||
proc mulInt(a, b: int): int {.compilerProc, pure.} =
|
||||
asm """
|
||||
mov eax, `a`
|
||||
mov ecx, `b`
|
||||
|
|
@ -208,99 +173,105 @@ when asmVersion and not defined(gcc):
|
|||
theEnd:
|
||||
"""
|
||||
|
||||
elif asmVersion and defined(gcc):
|
||||
proc addInt(a, b: int): int =
|
||||
asm """ "addl %1,%%eax\n"
|
||||
elif false: # asmVersion and (defined(gcc) or defined(llvm_gcc)):
|
||||
proc addInt(a, b: int): int {.compilerProc, inline.} =
|
||||
# don't use a pure proc here!
|
||||
asm """
|
||||
"addl %%ecx, %%eax\n"
|
||||
"jno 1\n"
|
||||
"call _raiseOverflow\n"
|
||||
"1: \n"
|
||||
:"=a"(`result`)
|
||||
:"a"(`a`), "c"(`b`)
|
||||
"""
|
||||
|
||||
proc subInt(a, b: int): int {.compilerProc, inline.} =
|
||||
asm """ "subl %%ecx,%%eax\n"
|
||||
"jno 1\n"
|
||||
"call _raiseOverflow\n"
|
||||
"1: \n"
|
||||
:"=a"(`result`)
|
||||
:"a"(`a`), "c"(`b`)
|
||||
"""
|
||||
|
||||
proc mulInt(a, b: int): int {.compilerProc, inline.} =
|
||||
asm """ "xorl %%edx, %%edx\n"
|
||||
"imull %%ecx\n"
|
||||
"jno 1\n"
|
||||
"call _raiseOverflow\n"
|
||||
"1: \n"
|
||||
:"=a"(`a`)
|
||||
:"a"(`a`), "r"(`b`)
|
||||
:"=a"(`result`)
|
||||
:"a"(`a`), "c"(`b`)
|
||||
:"%edx"
|
||||
"""
|
||||
|
||||
proc subInt(a, b: int): int =
|
||||
asm """ "subl %1,%%eax\n"
|
||||
"jno 1\n"
|
||||
"call _raiseOverflow\n"
|
||||
"1: \n"
|
||||
:"=a"(`a`)
|
||||
:"a"(`a`), "r"(`b`)
|
||||
proc negInt(a: int): int {.compilerProc, inline.} =
|
||||
asm """ "negl %%eax\n"
|
||||
"jno 1\n"
|
||||
"call _raiseOverflow\n"
|
||||
"1: \n"
|
||||
:"=a"(`result`)
|
||||
:"a"(`a`)
|
||||
"""
|
||||
|
||||
proc negInt(a: int): int =
|
||||
asm """ "negl %%eax\n"
|
||||
"jno 1\n"
|
||||
"call _raiseOverflow\n"
|
||||
"1: \n"
|
||||
:"=a"(`a`)
|
||||
:"a"(`a`)
|
||||
"""
|
||||
|
||||
proc divInt(a, b: int): int =
|
||||
proc divInt(a, b: int): int {.compilerProc, inline.} =
|
||||
asm """ "xorl %%edx, %%edx\n"
|
||||
"idivl %%ecx\n"
|
||||
"jno 1\n"
|
||||
"call _raiseOverflow\n"
|
||||
"1: \n"
|
||||
:"=a"(`a`)
|
||||
:"a"(`a`), "c"(`b`)
|
||||
:"%edx"
|
||||
:"=a"(`result`)
|
||||
:"a"(`a`), "c"(`b`)
|
||||
:"%edx"
|
||||
"""
|
||||
|
||||
proc modInt(a, b: int): int =
|
||||
proc modInt(a, b: int): int {.compilerProc, inline.} =
|
||||
asm """ "xorl %%edx, %%edx\n"
|
||||
"idivl %%ecx\n"
|
||||
"jno 1\n"
|
||||
"call _raiseOverflow\n"
|
||||
"1: \n"
|
||||
"movl %%edx, %%eax"
|
||||
:"=a"(`a`)
|
||||
:"a"(`a`), "c"(`b`)
|
||||
:"%edx"
|
||||
:"=a"(`result`)
|
||||
:"a"(`a`), "c"(`b`)
|
||||
:"%edx"
|
||||
"""
|
||||
|
||||
proc mulInt(a, b: int): int =
|
||||
asm """ "xorl %%edx, %%edx\n"
|
||||
"imull %%ecx\n"
|
||||
"jno 1\n"
|
||||
"call _raiseOverflow\n"
|
||||
"1: \n"
|
||||
:"=a"(`a`)
|
||||
:"a"(`a`), "c"(`b`)
|
||||
:"%edx"
|
||||
"""
|
||||
|
||||
else:
|
||||
# Platform independant versions of the above (slower!)
|
||||
|
||||
proc addInt(a, b: int): int =
|
||||
# Platform independant versions of the above (slower!)
|
||||
when not defined(addInt):
|
||||
proc addInt(a, b: int): int {.compilerProc, inline.} =
|
||||
result = a +% b
|
||||
if (result xor a) >= 0 or (result xor b) >= 0:
|
||||
return result
|
||||
raiseOverflow()
|
||||
|
||||
proc subInt(a, b: int): int =
|
||||
when not defined(subInt):
|
||||
proc subInt(a, b: int): int {.compilerProc, inline.} =
|
||||
result = a -% b
|
||||
if (result xor a) >= 0 or (result xor not b) >= 0:
|
||||
return result
|
||||
raiseOverflow()
|
||||
|
||||
proc negInt(a: int): int =
|
||||
when not defined(negInt):
|
||||
proc negInt(a: int): int {.compilerProc, inline.} =
|
||||
if a != low(int): return -a
|
||||
raiseOverflow()
|
||||
|
||||
proc divInt(a, b: int): int =
|
||||
when not defined(divInt):
|
||||
proc divInt(a, b: int): int {.compilerProc, inline.} =
|
||||
if b == 0:
|
||||
raiseDivByZero()
|
||||
if a == low(int) and b == -1:
|
||||
raiseOverflow()
|
||||
return a div b
|
||||
|
||||
proc modInt(a, b: int): int =
|
||||
when not defined(modInt):
|
||||
proc modInt(a, b: int): int {.compilerProc, inline.} =
|
||||
if b == 0:
|
||||
raiseDivByZero()
|
||||
return a mod b
|
||||
|
||||
when not defined(mulInt):
|
||||
#
|
||||
# This code has been inspired by Python's source code.
|
||||
# The native int product x*y is either exactly right or *way* off, being
|
||||
|
|
@ -321,7 +292,7 @@ else:
|
|||
# the only one that can lose catastrophic amounts of information, it's the
|
||||
# native int product that must have overflowed.
|
||||
#
|
||||
proc mulInt(a, b: int): int =
|
||||
proc mulInt(a, b: int): int {.compilerProc.} =
|
||||
var
|
||||
resAsFloat, floatProd: float
|
||||
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ proc getServerSoftware*(): string =
|
|||
|
||||
proc setTestData*(keysvalues: openarray[string]) =
|
||||
## fills the appropriate environment variables to test your CGI application.
|
||||
## This can only simulate the 'GET' 'REQUEST_METHOD'. `keysvalues` should
|
||||
## This can only simulate the 'GET' request method. `keysvalues` should
|
||||
## provide embedded (name, value)-pairs. Example:
|
||||
##
|
||||
## .. code-block:: Nimrod
|
||||
|
|
@ -300,6 +300,7 @@ proc setTestData*(keysvalues: openarray[string]) =
|
|||
proc writeContentType*() =
|
||||
## call this before starting to send your HTML data to `stdout`. This
|
||||
## is just a shorthand for:
|
||||
##
|
||||
## .. code-block:: Nimrod
|
||||
## write(stdout, "Content-type: text/html\n\n")
|
||||
write(stdout, "Content-type: text/html\n\n")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{.deadCodeElim: on.}
|
||||
|
||||
import
|
||||
glib2
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{.deadCodeElim: on.}
|
||||
|
||||
import
|
||||
glib2, gdk2pixbuf, pango
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{.deadCodeElim: on.}
|
||||
|
||||
import
|
||||
glib2
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{.deadCodeElim: on.}
|
||||
|
||||
import
|
||||
Glib2, Gdk2
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{.deadCodeElim: on.}
|
||||
|
||||
when defined(windows):
|
||||
const
|
||||
gliblib = "libglib-2.0-0.dll"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{.deadCodeElim: on.}
|
||||
|
||||
import
|
||||
glib2, atk, pango, gdk2pixbuf, gdk2
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{.deadCodeElim: on.}
|
||||
|
||||
import
|
||||
Glib2, Gdk2, Gtk2, GdkGLExt
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{.deadCodeElim: on.}
|
||||
|
||||
import
|
||||
gtk2, glib2, atk, pango, gdk2pixbuf, gdk2
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{.deadCodeElim: on.}
|
||||
|
||||
import
|
||||
glib2, gtk2
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{.deadCodeElim: on.}
|
||||
|
||||
import
|
||||
glib2
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
{.deadCodeElim: on.}
|
||||
|
||||
import
|
||||
glib2, pango
|
||||
|
||||
|
|
|
|||
|
|
@ -270,7 +270,8 @@
|
|||
#
|
||||
#
|
||||
|
||||
#
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
when defined(windows):
|
||||
const SDLLibName = "SDL.dll"
|
||||
elif defined(macosx):
|
||||
|
|
|
|||
|
|
@ -318,11 +318,6 @@ proc Mix_LoadWAV_RW*(src: PSDL_RWops, freesrc: int): PMix_Chunk{.cdecl,
|
|||
proc Mix_LoadWAV*(filename: cstring): PMix_Chunk
|
||||
proc Mix_LoadMUS*(filename: cstring): PMix_Music{.cdecl,
|
||||
importc, dynlib: SDL_MixerLibName.}
|
||||
##if 0 { This hasn't been hooked into music.c yet }
|
||||
#{ Load a music file from an SDL_RWop object (MikMod-specific currently)
|
||||
# Matt Campbell (matt@campbellhome.dhs.org) April 2000 }
|
||||
#function Mix_LoadMUS_RW(SDL_RWops *rw) : PMix_Music; cdecl;
|
||||
##endif
|
||||
# Load a wave file of the mixer format from a memory buffer
|
||||
proc Mix_QuickLoad_WAV*(mem: PUint8): PMix_Chunk{.cdecl,
|
||||
importc, dynlib: SDL_MixerLibName.}
|
||||
|
|
|
|||
|
|
@ -126,5 +126,5 @@ elif defined(mac):
|
|||
nss = NSLookupSymbolInModule(NSModule(lib), name)
|
||||
result = TProcAddr(NSAddressOfSymbol(nss))
|
||||
|
||||
else: # workaround a newly introduced bug :-(
|
||||
else:
|
||||
{.error: "no implementation for dyncalls".}
|
||||
|
|
|
|||
|
|
@ -326,6 +326,9 @@ proc echo*(x: cstring) = ewriteln(x)
|
|||
proc echo[Ty](x: Ty) =
|
||||
echo(x)
|
||||
|
||||
proc echo[Ty](x: openArray[Ty]) =
|
||||
for a in items(x): echo(a)
|
||||
|
||||
# Arithmetic:
|
||||
proc addInt(a, b: int): int {.pure, compilerproc.} =
|
||||
asm """
|
||||
|
|
|
|||
|
|
@ -61,9 +61,6 @@ type
|
|||
filename: CString
|
||||
len: int # length of slots (when not debugging always zero)
|
||||
|
||||
TTempFrame = tuple[ # used for recursion elimination in WriteStackTrace
|
||||
procname: CString, line: int]
|
||||
|
||||
var
|
||||
buf: string # cannot be allocated on the stack!
|
||||
assertBuf: string # we need a different buffer for
|
||||
|
|
@ -72,33 +69,45 @@ var
|
|||
|
||||
framePtr {.exportc.}: PFrame
|
||||
|
||||
tempFrames: array [0..255, TTempFrame] # cannot be allocated
|
||||
# on the stack!
|
||||
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
|
||||
|
||||
proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||
const
|
||||
firstCalls = 32
|
||||
var
|
||||
it = f
|
||||
i = 0
|
||||
total = 0
|
||||
while it != nil and i <= high(tempFrames):
|
||||
tempFrames[i] = (it.procname, it.line)
|
||||
while it != nil and i <= high(tempFrames)-(firstCalls-1):
|
||||
# the (-1) is for a nil entry that marks where the '...' should occur
|
||||
tempFrames[i] = it
|
||||
inc(i)
|
||||
inc(total)
|
||||
it = it.prev
|
||||
var b = it
|
||||
while it != nil:
|
||||
inc(total)
|
||||
it = it.prev
|
||||
# if the buffer overflowed print '...':
|
||||
for j in 1..total-i-(firstCalls-1):
|
||||
if b != nil: b = b.prev
|
||||
if total != i:
|
||||
add(s, "(")
|
||||
add(s, $(total-i))
|
||||
add(s, " calls omitted) ...\n")
|
||||
tempFrames[i] = nil
|
||||
inc(i)
|
||||
while b != nil and i <= high(tempFrames):
|
||||
tempFrames[i] = b
|
||||
inc(i)
|
||||
b = b.prev
|
||||
for j in countdown(i-1, 0):
|
||||
add(s, $tempFrames[j].procname)
|
||||
if tempFrames[j].line > 0:
|
||||
add(s, ", line: ")
|
||||
add(s, $tempFrames[j].line)
|
||||
add(s, "\n")
|
||||
if tempFrames[j] == nil:
|
||||
add(s, "(")
|
||||
add(s, $(total-i-1))
|
||||
add(s, " calls omitted) ...\n")
|
||||
else:
|
||||
add(s, $tempFrames[j].procname)
|
||||
if tempFrames[j].line > 0:
|
||||
add(s, ", line: ")
|
||||
add(s, $tempFrames[j].line)
|
||||
add(s, "\n")
|
||||
|
||||
proc rawWriteStackTrace(s: var string) =
|
||||
if framePtr == nil:
|
||||
|
|
@ -156,8 +165,7 @@ proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} =
|
|||
raise gAssertionFailed # newException(EAssertionFailed, assertBuf)
|
||||
|
||||
proc WriteStackTrace() =
|
||||
var
|
||||
s: string = ""
|
||||
var s = ""
|
||||
rawWriteStackTrace(s)
|
||||
writeToStdErr(s)
|
||||
|
||||
|
|
@ -208,13 +216,13 @@ assertBuf = newString(2048)
|
|||
setLen(buf, 0)
|
||||
setLen(assertBuf, 0)
|
||||
|
||||
proc raiseRangeError(val: biggestInt) {.compilerproc, noreturn.} =
|
||||
proc raiseRangeError(val: biggestInt) {.compilerproc, noreturn, noinline.} =
|
||||
raise newException(EOutOfRange, "value " & $val & " out of range")
|
||||
|
||||
proc raiseIndexError() {.compilerproc, noreturn.} =
|
||||
proc raiseIndexError() {.compilerproc, noreturn, noinline.} =
|
||||
raise newException(EInvalidIndex, "index out of bounds")
|
||||
|
||||
proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
|
||||
proc raiseFieldError(f: string) {.compilerproc, noreturn, noinline.} =
|
||||
raise newException(EInvalidField, f & " is not accessible")
|
||||
|
||||
proc chckIndx(i, a, b: int): int =
|
||||
|
|
|
|||
39
lib/gc.nim
39
lib/gc.nim
|
|
@ -13,11 +13,10 @@
|
|||
# * incremental
|
||||
# * non-recursive
|
||||
# * generational
|
||||
# * excellent performance
|
||||
|
||||
# Future Improvements:
|
||||
# * Both dlmalloc and TLSF lack zero-overhead object allocation. Thus, for
|
||||
# small objects we will should use our own allocator.
|
||||
# small objects we should use our own allocator.
|
||||
# * Support for multi-threading. However, locks for the reference counting
|
||||
# might turn out to be too slow.
|
||||
|
||||
|
|
@ -332,7 +331,7 @@ proc CellSetPut(t: var TCellSet, key: TAddress): PPageDesc =
|
|||
|
||||
# ---------- slightly higher level procs --------------------------------------
|
||||
|
||||
proc in_Operator(s: TCellSet, cell: PCell): bool =
|
||||
proc contains(s: TCellSet, cell: PCell): bool =
|
||||
var u = cast[TAddress](cell)
|
||||
var t = CellSetGet(s, u shr PageShift)
|
||||
if t != nil:
|
||||
|
|
@ -468,23 +467,6 @@ proc prepareDealloc(cell: PCell) =
|
|||
proc setStackBottom(theStackBottom: pointer) {.compilerproc.} =
|
||||
stackBottom = theStackBottom
|
||||
|
||||
proc initGC() =
|
||||
when traceGC:
|
||||
for i in low(TCellState)..high(TCellState): CellSetInit(states[i])
|
||||
gch.stackScans = 0
|
||||
gch.cycleCollections = 0
|
||||
gch.maxThreshold = 0
|
||||
gch.maxStackSize = 0
|
||||
gch.maxStackPages = 0
|
||||
gch.cycleTableSize = 0
|
||||
# init the rt
|
||||
init(gch.zct)
|
||||
init(gch.tempStack)
|
||||
CellSetInit(gch.cycleRoots)
|
||||
CellSetInit(gch.stackCells)
|
||||
gch.mask = 0
|
||||
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
|
||||
|
||||
proc PossibleRoot(gch: var TGcHeap, c: PCell) {.inline.} =
|
||||
if canbeCycleRoot(c): incl(gch.cycleRoots, c)
|
||||
|
||||
|
|
@ -535,6 +517,23 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) =
|
|||
if dest^ != nil: decRef(usrToCell(dest^))
|
||||
dest^ = src
|
||||
|
||||
proc initGC() =
|
||||
when traceGC:
|
||||
for i in low(TCellState)..high(TCellState): CellSetInit(states[i])
|
||||
gch.stackScans = 0
|
||||
gch.cycleCollections = 0
|
||||
gch.maxThreshold = 0
|
||||
gch.maxStackSize = 0
|
||||
gch.maxStackPages = 0
|
||||
gch.cycleTableSize = 0
|
||||
# init the rt
|
||||
init(gch.zct)
|
||||
init(gch.tempStack)
|
||||
CellSetInit(gch.cycleRoots)
|
||||
CellSetInit(gch.stackCells)
|
||||
gch.mask = 0
|
||||
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
|
||||
|
||||
proc getDiscriminant(aa: Pointer, n: ptr TNimNode): int =
|
||||
assert(n.kind == nkCase)
|
||||
var d: int
|
||||
|
|
|
|||
|
|
@ -151,8 +151,7 @@ proc open(L: var TBaseLexer, input: PStream, bufLen: int = 8192) =
|
|||
skip_UTF_8_BOM(L)
|
||||
|
||||
proc getColNumber(L: TBaseLexer, pos: int): int =
|
||||
result = pos - L.lineStart
|
||||
assert(result >= 0)
|
||||
result = abs(pos - L.lineStart)
|
||||
|
||||
proc getCurrentLine(L: TBaseLexer, marker: bool = true): string =
|
||||
var i: int
|
||||
|
|
|
|||
|
|
@ -31,6 +31,40 @@
|
|||
# cog.out(toEnum(key, val))
|
||||
#]]]
|
||||
type
|
||||
TNimrodNodeKind* = enum
|
||||
nnkNone, nnkEmpty, nnkIdent, nnkSym,
|
||||
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
|
||||
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit,
|
||||
nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit,
|
||||
nnkTripleStrLit, nnkMetaNode, nnkNilLit, nnkDotCall,
|
||||
nnkCommand, nnkCall, nnkGenericCall, nnkExplicitTypeListCall,
|
||||
nnkExprEqExpr, nnkExprColonExpr, nnkIdentDefs, nnkVarTuple,
|
||||
nnkInfix, nnkPrefix, nnkPostfix, nnkPar,
|
||||
nnkCurly, nnkBracket, nnkBracketExpr, nnkPragmaExpr,
|
||||
nnkRange, nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr,
|
||||
nnkIfExpr, nnkElifExpr, nnkElseExpr, nnkLambda,
|
||||
nnkAccQuoted, nnkHeaderQuoted, nnkTableConstr, nnkQualified,
|
||||
nnkHiddenStdConv, nnkHiddenSubConv, nnkHiddenCallConv, nnkConv,
|
||||
nnkCast, nnkAddr, nnkHiddenAddr, nnkHiddenDeref,
|
||||
nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64,
|
||||
nnkChckRange, nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray,
|
||||
nnkAsgn, nnkFastAsgn, nnkDefaultTypeParam, nnkGenericParams,
|
||||
nnkFormalParams, nnkOfInherit, nnkModule, nnkProcDef,
|
||||
nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef,
|
||||
nnkOfBranch, nnkElifBranch, nnkExceptBranch, nnkElse,
|
||||
nnkMacroStmt, nnkAsmStmt, nnkPragma, nnkIfStmt,
|
||||
nnkWhenStmt, nnkForStmt, nnkWhileStmt, nnkCaseStmt,
|
||||
nnkVarSection, nnkConstSection, nnkConstDef, nnkTypeSection,
|
||||
nnkTypeDef, nnkYieldStmt, nnkTryStmt, nnkFinally,
|
||||
nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt,
|
||||
nnkBlockStmt, nnkDiscardStmt, nnkStmtList, nnkImportStmt,
|
||||
nnkFromStmt, nnkImportAs, nnkIncludeStmt, nnkAccessStmt,
|
||||
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkStmtListType,
|
||||
nnkBlockType, nnkVm, nnkTypeOfExpr, nnkObjectTy,
|
||||
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
|
||||
nnkRefTy, nnkPtrTy, nnkVarTy, nnkProcTy,
|
||||
nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
|
||||
TNimNodeKinds* = set[TNimrodNodeKind]
|
||||
TNimrodTypeKind* = enum
|
||||
ntyNone, ntyBool, ntyChar, ntyEmpty,
|
||||
ntyArrayConstr, ntyNil, ntyGeneric, ntyGenericInst,
|
||||
|
|
@ -49,40 +83,6 @@ type
|
|||
nskMacro, nskTemplate, nskField, nskEnumField,
|
||||
nskForVar, nskModule, nskLabel, nskStub
|
||||
TNimSymKinds* = set[TNimrodSymKind]
|
||||
TNimrodNodeKind* = enum
|
||||
nnkNone, nnkEmpty, nnkIdent, nnkSym,
|
||||
nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
|
||||
nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkFloatLit,
|
||||
nnkFloat32Lit, nnkFloat64Lit, nnkStrLit, nnkRStrLit,
|
||||
nnkTripleStrLit, nnkMetaNode, nnkNilLit, nnkDotCall,
|
||||
nnkCommand, nnkCall, nnkGenericCall, nnkExplicitTypeListCall,
|
||||
nnkExprEqExpr, nnkExprColonExpr, nnkIdentDefs, nnkInfix,
|
||||
nnkPrefix, nnkPostfix, nnkPar, nnkCurly,
|
||||
nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange,
|
||||
nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr,
|
||||
nnkElifExpr, nnkElseExpr, nnkLambda, nnkAccQuoted,
|
||||
nnkHeaderQuoted, nnkTableConstr, nnkQualified, nnkHiddenStdConv,
|
||||
nnkHiddenSubConv, nnkHiddenCallConv, nnkConv, nnkCast,
|
||||
nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv,
|
||||
nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange,
|
||||
nnkStringToCString, nnkCStringToString, nnkPassAsOpenArray, nnkAsgn,
|
||||
nnkDefaultTypeParam, nnkGenericParams, nnkFormalParams, nnkOfInherit,
|
||||
nnkModule, nnkProcDef, nnkConverterDef, nnkMacroDef,
|
||||
nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkElifBranch,
|
||||
nnkExceptBranch, nnkElse, nnkMacroStmt, nnkAsmStmt,
|
||||
nnkPragma, nnkIfStmt, nnkWhenStmt, nnkForStmt,
|
||||
nnkWhileStmt, nnkCaseStmt, nnkVarSection, nnkConstSection,
|
||||
nnkConstDef, nnkTypeSection, nnkTypeDef, nnkYieldStmt,
|
||||
nnkTryStmt, nnkFinally, nnkRaiseStmt, nnkReturnStmt,
|
||||
nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkDiscardStmt,
|
||||
nnkStmtList, nnkImportStmt, nnkFromStmt, nnkImportAs,
|
||||
nnkIncludeStmt, nnkAccessStmt, nnkCommentStmt, nnkStmtListExpr,
|
||||
nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkVm,
|
||||
nnkTypeOfExpr, nnkObjectTy, nnkTupleTy, nnkRecList,
|
||||
nnkRecCase, nnkRecWhen, nnkRefTy, nnkPtrTy,
|
||||
nnkVarTy, nnkProcTy, nnkEnumTy, nnkEnumFieldDef,
|
||||
nnkReturnToken
|
||||
TNimNodeKinds* = set[TNimrodNodeKind]
|
||||
#[[[end]]]
|
||||
|
||||
type
|
||||
|
|
|
|||
|
|
@ -421,18 +421,4 @@ struct NimException {
|
|||
};
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
typedef struct TStringDesc {
|
||||
NI len;
|
||||
NI space;
|
||||
NIM_CHAR data[1]; /* SEQ_DECL_SIZE]; */
|
||||
} TStringDesc;
|
||||
|
||||
typedef struct {
|
||||
NI len, space;
|
||||
} TGenericSeq;
|
||||
|
||||
typedef TGenericSeq* PGenericSeq;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
## retrieving environment variables, reading command line arguments,
|
||||
## working with directories, running shell commands, etc.
|
||||
## This module is -- like any other basic library -- platform independant.
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
{.push debugger: off.}
|
||||
|
||||
|
|
@ -863,9 +864,9 @@ var
|
|||
when defined(windows):
|
||||
# because we support Windows GUI applications, things get really
|
||||
# messy here...
|
||||
proc GetEnvironmentStringsA*(): cstring {.
|
||||
proc GetEnvironmentStringsA(): cstring {.
|
||||
stdcall, dynlib: "kernel32", importc.}
|
||||
proc FreeEnvironmentStringsA*(para1: cstring): int32 {.
|
||||
proc FreeEnvironmentStringsA(para1: cstring): int32 {.
|
||||
stdcall, dynlib: "kernel32", importc.}
|
||||
|
||||
proc strEnd(cstr: CString, c = 0): CString {.importc: "strchr", nodecl.}
|
||||
|
|
@ -1091,8 +1092,8 @@ proc expandFilename(filename: string): string =
|
|||
|
||||
proc parseCmdLine*(c: string): seq[string] =
|
||||
## Splits a command line into several components; components are separated by
|
||||
## whitespace or are quoted with the ``"`` or ``'`` characters. This proc is
|
||||
## only occassionally useful, better use the `parseopt` module.
|
||||
## whitespace unless the whitespace occurs within ``"`` or ``'`` quotes.
|
||||
## This proc is only occassionally useful, better use the `parseopt` module.
|
||||
result = @[]
|
||||
var i = 0
|
||||
while c[i] != '\0':
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ type
|
|||
TTokKind = enum
|
||||
tkInvalid, tkEof,
|
||||
tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash
|
||||
TToken{.final.} = object # a token
|
||||
TToken {.final.} = object # a token
|
||||
kind: TTokKind # the type of the token
|
||||
literal: string # the parsed (string) literal
|
||||
|
||||
|
|
@ -139,7 +139,7 @@ proc getEscapedChar(c: var TCfgParser, tok: var TToken) =
|
|||
inc(c.bufpos) # skip '\'
|
||||
case c.buf[c.bufpos]
|
||||
of 'n', 'N':
|
||||
tok.literal = tok.literal & nl
|
||||
add(tok.literal, nl)
|
||||
Inc(c.bufpos)
|
||||
of 'r', 'R', 'c', 'C':
|
||||
add(tok.literal, '\c')
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ proc init(cmdline: string = ""): TOptParser =
|
|||
else:
|
||||
result.cmd = ""
|
||||
for i in countup(1, ParamCount()):
|
||||
result.cmd = result.cmd & quoteIfSpaceExists(paramStr(i)) & ' '
|
||||
result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i)) & ' '
|
||||
result.kind = cmdEnd
|
||||
result.key = ""
|
||||
result.val = ""
|
||||
|
|
|
|||
|
|
@ -22,12 +22,12 @@ proc reprPointer(x: pointer): string {.compilerproc.} =
|
|||
|
||||
proc reprStrAux(result: var string, s: string) =
|
||||
if cast[pointer](s) == nil:
|
||||
add result "nil"
|
||||
add result, "nil"
|
||||
return
|
||||
add result, reprPointer(cast[pointer](s)) & "\""
|
||||
for c in items(s):
|
||||
case c
|
||||
of '"': add result "\\\""
|
||||
of '"': add result, "\\\""
|
||||
of '\\': add result, "\\\\" # BUGFIX: forgotten
|
||||
of '\10': add result, "\\10\"\n\"" # " \n " # better readability
|
||||
of '\128' .. '\255', '\0'..'\9', '\11'..'\31':
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
## All the routines here are avaiable for the EMCAScript target
|
||||
## too!
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
{.push debugger:off .} # the user does not want to trace a part
|
||||
# of the standard library!
|
||||
|
||||
|
|
@ -41,19 +43,19 @@ proc strip*(s: string): string {.noSideEffect.}
|
|||
|
||||
proc toLower*(s: string): string {.noSideEffect.}
|
||||
## Converts `s` into lower case. This works only for the letters A-Z.
|
||||
## See `charsets.nativeToLower` for a version that is locale-dependant.
|
||||
## See `unicode.toLower` for a version that works for any Unicode character.
|
||||
|
||||
proc toLower*(c: Char): Char {.noSideEffect.}
|
||||
## Converts `c` into lower case. This works only for the letters A-Z.
|
||||
## See `charsets.nativeToLower()` for a version that is locale-dependant.
|
||||
## See `unicode.toLower` for a version that works for any Unicode character.
|
||||
|
||||
proc toUpper*(s: string): string {.noSideEffect.}
|
||||
## Converts `s` into upper case. This works only for the letters a-z.
|
||||
## See `charsets.nativeToUpper()` for a version that is locale-dependant.
|
||||
## See `unicode.toUpper` for a version that works for any Unicode character.
|
||||
|
||||
proc toUpper*(c: Char): Char {.noSideEffect.}
|
||||
## Converts `c` into upper case. This works only for the letters a-z.
|
||||
## See `charsets.nativeToUpper()` for a version that is locale-dependant.
|
||||
## See `unicode.toUpper` for a version that works for any Unicode character.
|
||||
|
||||
proc capitalize*(s: string): string {.noSideEffect.}
|
||||
## Converts the first character of `s` into upper case.
|
||||
|
|
@ -71,6 +73,10 @@ proc findSubStr*(sub: char, s: string, start: int = 0): int {.noSideEffect.}
|
|||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||
|
||||
proc findChars*(chars: set[char], s: string, start: int = 0): int {.noSideEffect.}
|
||||
## Searches for `chars` in `s` starting at position `start`. If `s` contains
|
||||
## none of the characters in `chars`, -1 is returned.
|
||||
|
||||
proc replaceStr*(s, sub, by: string): string {.noSideEffect.}
|
||||
## Replaces `sub` in `s` by the string `by`.
|
||||
|
||||
|
|
@ -173,11 +179,14 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect.}
|
|||
## | < 0 iff a < b
|
||||
## | > 0 iff a > b
|
||||
|
||||
proc in_Operator*(s: string, c: char): bool {.noSideEffect.}
|
||||
## Same as `findSubStr(c, s) >= 0`.
|
||||
proc contains*(s: string, c: char): bool {.noSideEffect.}
|
||||
## Same as ``findSubStr(c, s) >= 0``.
|
||||
|
||||
proc in_Operator*(s, sub: string): bool {.noSideEffect.}
|
||||
## Same as `findSubStr(sub, s) >= 0`.
|
||||
proc contains*(s, sub: string): bool {.noSideEffect.}
|
||||
## Same as ``findSubStr(sub, s) >= 0``.
|
||||
|
||||
proc contains*(s: string, chars: set[char]): bool {.noSideEffect.}
|
||||
## Same as ``findChars(s, chars) >= 0``.
|
||||
|
||||
proc toHex*(x: BiggestInt, len: int): string {.noSideEffect.}
|
||||
## Converts `x` to its hexadecimal representation. The resulting string
|
||||
|
|
@ -259,10 +268,10 @@ proc allCharsInSet*(s: string, theSet: TCharSet): bool =
|
|||
if not (c in theSet): return false
|
||||
return true
|
||||
|
||||
proc quoteIfSpaceExists*(s: string): string =
|
||||
proc quoteIfContainsWhite*(s: string): string =
|
||||
## returns ``'"' & s & '"'`` if `s` contains a space and does not
|
||||
## start with a quote, else returns `s`
|
||||
if findSubStr(' ', s) >= 0 and s[0] != '"':
|
||||
if findChars({' ', '\t'}, s) >= 0 and s[0] != '"':
|
||||
result = '"' & s & '"'
|
||||
else:
|
||||
result = s
|
||||
|
|
@ -489,10 +498,18 @@ proc findSubStr(sub: char, s: string, start: int = 0): int =
|
|||
if sub == s[i]: return i
|
||||
return -1
|
||||
|
||||
proc in_Operator(s: string, c: char): bool =
|
||||
proc findChars(chars: set[char], s: string, start: int = 0): int =
|
||||
for i in start..s.len-1:
|
||||
if s[i] in chars: return i
|
||||
return -1
|
||||
|
||||
proc contains(s: string, chars: set[char]): bool =
|
||||
return findChars(chars, s) >= 0
|
||||
|
||||
proc contains(s: string, c: char): bool =
|
||||
return findSubStr(c, s) >= 0
|
||||
|
||||
proc in_Operator(s, sub: string): bool =
|
||||
proc contains(s, sub: string): bool =
|
||||
return findSubStr(sub, s) >= 0
|
||||
|
||||
proc replaceStr(s, sub, by: string): string =
|
||||
|
|
|
|||
|
|
@ -91,10 +91,13 @@ proc writeln[Ty](f: TFile, x: Ty) =
|
|||
write(f, "\n")
|
||||
|
||||
proc writeln[Ty](f: TFile, x: openArray[Ty]) =
|
||||
write(f, x)
|
||||
for i in items(x): write(f, i)
|
||||
write(f, "\n")
|
||||
|
||||
proc echo[Ty](x: Ty) = writeln(stdout, x)
|
||||
proc echo[Ty](x: openArray[Ty]) =
|
||||
for i in items(x): write(stdout, i)
|
||||
write(stdout, "\n")
|
||||
|
||||
# interface to the C procs:
|
||||
proc fopen(filename, mode: CString): pointer {.importc: "fopen", noDecl.}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,6 @@
|
|||
# we don't use refcounts because that's a behaviour
|
||||
# the programmer may not want
|
||||
|
||||
type
|
||||
# len and space without counting the terminating zero:
|
||||
NimStringDesc {.compilerproc, final.} = object of TGenericSeq
|
||||
data: array[0..100_000_000, char] # for the '\0' character
|
||||
|
||||
NimString = ptr NimStringDesc
|
||||
|
||||
# implementation:
|
||||
|
||||
proc resize(old: int): int {.inline.} =
|
||||
|
|
|
|||
|
|
@ -58,6 +58,17 @@ proc defined*[T] (x: T): bool {.magic: "Defined", noSideEffect.}
|
|||
proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.}
|
||||
## Boolean not; returns true iff ``x == false``.
|
||||
|
||||
proc `and`*(x, y: bool): bool {.magic: "And", noSideEffect.}
|
||||
## Boolean ``and``; returns true iff ``x == y == true``.
|
||||
## Evaluation is short-circuited: This means that if ``x`` is false,
|
||||
## ``y`` will not even be evaluated.
|
||||
proc `or`*(x, y: bool): bool {.magic: "Or", noSideEffect.}
|
||||
## Boolean ``or``; returns true iff ``not (not x and not y)``.
|
||||
## Evaluation is short-circuited: This means that if ``x`` is true,
|
||||
## ``y`` will not even be evaluated.
|
||||
proc `xor`*(x, y: bool): bool {.magic: "Xor", noSideEffect.}
|
||||
## Boolean `exclusive or`; returns true iff ``x != y``.
|
||||
|
||||
proc new*[T](a: var ref T) {.magic: "New".}
|
||||
## creates a new object of type ``T`` and returns a safe (traced)
|
||||
## reference to it in ``a``.
|
||||
|
|
@ -94,6 +105,19 @@ type
|
|||
seq*{.magic: "Seq".}[T] ## Generic type to construct sequences.
|
||||
set*{.magic: "Set".}[T] ## Generic type to construct bit sets.
|
||||
|
||||
when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
type
|
||||
TGenericSeq {.compilerproc, pure.} = object
|
||||
len, space: int
|
||||
PGenericSeq {.exportc.} = ptr TGenericSeq
|
||||
# len and space without counting the terminating zero:
|
||||
NimStringDesc {.compilerproc, final.} = object of TGenericSeq
|
||||
data: array[0..100_000_000, char]
|
||||
NimString = ptr NimStringDesc
|
||||
|
||||
include hti
|
||||
|
||||
type
|
||||
Byte* = Int8 ## this is an alias for ``int8``, that is a signed
|
||||
## int 8 bits wide.
|
||||
|
||||
|
|
@ -403,8 +427,8 @@ proc abs*(x: int8): int8 {.magic: "AbsI", noSideEffect.}
|
|||
proc abs*(x: int16): int16 {.magic: "AbsI", noSideEffect.}
|
||||
proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.}
|
||||
proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.}
|
||||
## returns the absolute value of `x`. If `x` is ``low(x)`` (that is
|
||||
## -MININT for its type), an overflow exception is thrown (if overflow
|
||||
## returns the absolute value of `x`. If `x` is ``low(x)`` (that
|
||||
## is -MININT for its type), an overflow exception is thrown (if overflow
|
||||
## checking is turned on).
|
||||
|
||||
proc min*(x, y: int): int {.magic: "MinI", noSideEffect.}
|
||||
|
|
@ -500,18 +524,6 @@ proc abs*(x: float): float {.magic: "AbsF64", noSideEffect.}
|
|||
proc min*(x, y: float): float {.magic: "MinF64", noSideEffect.}
|
||||
proc max*(x, y: float): float {.magic: "MaxF64", noSideEffect.}
|
||||
|
||||
# boolean operators:
|
||||
proc `and`*(x, y: bool): bool {.magic: "And", noSideEffect.}
|
||||
## Boolean ``and``; returns true iff ``x == y == true``.
|
||||
## Evaluation is short-circuited: This means that if ``x`` is false,
|
||||
## ``y`` will not even be evaluated.
|
||||
proc `or`*(x, y: bool): bool {.magic: "Or", noSideEffect.}
|
||||
## Boolean ``or``; returns true iff ``not (not x and not y)``.
|
||||
## Evaluation is short-circuited: This means that if ``x`` is true,
|
||||
## ``y`` will not even be evaluated.
|
||||
proc `xor`*(x, y: bool): bool {.magic: "Xor", noSideEffect.}
|
||||
## Boolean `exclusive or`; returns true iff ``x != y``.
|
||||
|
||||
# set operators
|
||||
proc `*` *[T](x, y: set[T]): set[T] {.magic: "MulSet", noSideEffect.}
|
||||
## This operator computes the intersection of two sets.
|
||||
|
|
@ -563,11 +575,12 @@ template `>` * (x, y: expr): expr =
|
|||
## "is greater" operator. This is the same as ``y < x``.
|
||||
y < x
|
||||
|
||||
proc in_Operator*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.}
|
||||
proc contains*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.}
|
||||
## One should overload this proc if one wants to overload the ``in`` operator.
|
||||
## The parameters are in reverse order! This is because the unification
|
||||
## algorithm that Nimrod uses for overload resolution works from left to
|
||||
## right.
|
||||
## The parameters are in reverse order! ``a in b`` is a template for
|
||||
## ``contains(b, a)``.
|
||||
## This is because the unification algorithm that Nimrod uses for overload
|
||||
## resolution works from left to right.
|
||||
## But for the ``in`` operator that would be the wrong direction for this
|
||||
## piece of code:
|
||||
##
|
||||
|
|
@ -578,11 +591,11 @@ proc in_Operator*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.}
|
|||
## If ``in`` had been declared as ``[T](elem: T, s: set[T])`` then ``T`` would
|
||||
## have been bound to ``char``. But ``s`` is not compatible to type
|
||||
## ``set[char]``! The solution is to bind ``T`` to ``range['a'..'z']``. This
|
||||
## is achieved by reversing the parameters for ``in_operator``; ``in`` then
|
||||
## is achieved by reversing the parameters for ``contains``; ``in`` then
|
||||
## passes its arguments in reverse order.
|
||||
|
||||
template `in` * (x, y: expr): expr = in_Operator(y, x)
|
||||
template `not_in` * (x, y: expr): expr = not in_Operator(y, x)
|
||||
template `in` * (x, y: expr): expr = contains(y, x)
|
||||
template `not_in` * (x, y: expr): expr = not contains(y, x)
|
||||
|
||||
proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
|
||||
template `is_not` *(x, y: expr): expr = not (x is y)
|
||||
|
|
@ -616,15 +629,17 @@ proc `&` * (x: char, y: string): string {.
|
|||
magic: "ConStrStr", noSideEffect, merge.}
|
||||
## is the `concatenation operator`. It concatenates `x` and `y`.
|
||||
|
||||
proc add * (x: var string, y: char) {.magic: "AppendStrCh".}
|
||||
proc add * (x: var string, y: string) {.magic: "AppendStrStr".}
|
||||
proc add*(x: var string, y: char) {.magic: "AppendStrCh".}
|
||||
proc add*(x: var string, y: string) {.magic: "AppendStrStr".}
|
||||
|
||||
when not defined(ECMAScript):
|
||||
{.push overflow_checks:off}
|
||||
proc add* (x: var string, y: cstring) =
|
||||
var i = 0
|
||||
while y[i] != '\0':
|
||||
add(x, y[i])
|
||||
inc(i)
|
||||
{.pop.}
|
||||
else:
|
||||
proc add* (x: var string, y: cstring) {.pure.} =
|
||||
asm """
|
||||
|
|
@ -646,7 +661,7 @@ proc add *[T](x: var seq[T], y: seq[T]) {.magic: "AppendSeqSeq".}
|
|||
|
||||
proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.}
|
||||
## takes any Nimrod variable and returns its string representation. It
|
||||
## works even for complex data graphs with cycles. This is an invaluable
|
||||
## works even for complex data graphs with cycles. This is a great
|
||||
## debugging tool.
|
||||
|
||||
type
|
||||
|
|
@ -730,6 +745,15 @@ const
|
|||
## is the endianness of the target CPU. This is a valuable piece of
|
||||
## information for low-level code only. This works thanks to compiler magic.
|
||||
|
||||
hostOS* {.magic: "HostOS"}: string = ""
|
||||
## a string that describes the host operating system. Possible values:
|
||||
## "windows", "macosx", "linux", "netbsd", "freebsd", "openbsd", "solaris",
|
||||
## "aix"
|
||||
|
||||
hostCPU* {.magic: "HostCPU"}: string = ""
|
||||
## a string that describes the host CPU. Possible values:
|
||||
## "i386", "alpha", "powerpc", "sparc", "amd64", "mips", "arm"
|
||||
|
||||
proc toFloat*(i: int): float {.
|
||||
magic: "ToFloat", noSideEffect, importc: "toFloat".}
|
||||
## converts an integer `i` into a ``float``. If the conversion
|
||||
|
|
@ -896,6 +920,12 @@ proc `$` *(x: string): string {.magic: "StrToStr", noSideEffect.}
|
|||
## as it is. This operator is useful for generic code, so
|
||||
## that ``$expr`` also works if ``expr`` is already a string.
|
||||
|
||||
proc `$` *(x: TAnyEnum): string {.magic: "EnumToStr", noSideEffect.}
|
||||
## The stingify operator for an enumeration argument. This works for
|
||||
## any enumeration type thanks to compiler magic. If a
|
||||
## a ``$`` operator for a concrete enumeration is provided, this is
|
||||
## used instead. (In other words: *Overwriting* is possible.)
|
||||
|
||||
# undocumented:
|
||||
proc getRefcount*[T](x: ref T): int {.importc: "getRefcount".}
|
||||
## retrieves the reference count of an heap-allocated object. The
|
||||
|
|
@ -1136,6 +1166,10 @@ proc echo*[Ty](x: Ty) {.inline.}
|
|||
## equivalent to ``writeln(stdout, x); flush(stdout)``. BUT: This is
|
||||
## available for the ECMAScript target too!
|
||||
|
||||
proc echo*[Ty](x: openarray[Ty]) {.inline.}
|
||||
## equivalent to ``writeln(stdout, x); flush(stdout)``. BUT: This is
|
||||
## available for the ECMAScript target too!
|
||||
|
||||
|
||||
template newException(exceptn, message: expr): expr =
|
||||
block: # open a new scope
|
||||
|
|
@ -1169,8 +1203,6 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
|||
|
||||
when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
|
||||
include hti
|
||||
|
||||
proc initGC()
|
||||
|
||||
var
|
||||
|
|
@ -1370,13 +1402,6 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
|||
include arithm
|
||||
{.pop.} # stack trace
|
||||
|
||||
# sequence type declarations here because the GC needs them too:
|
||||
type
|
||||
TGenericSeq {.compilerproc, pure.} = object
|
||||
len, space: int
|
||||
|
||||
PGenericSeq {.exportc.} = ptr TGenericSeq
|
||||
|
||||
const
|
||||
GenericSeqSize = (2 * sizeof(int))
|
||||
|
||||
|
|
@ -1417,13 +1442,12 @@ elif defined(NimrodVM):
|
|||
proc getFreeMem(): int = return -1
|
||||
proc getTotalMem(): int = return -1
|
||||
proc echo[Ty](x: Ty) = nil
|
||||
proc echo[Ty](x: openarray[Ty]) = nil
|
||||
|
||||
proc cmp(x, y: string): int =
|
||||
if x == y: return 0
|
||||
if x < y: return -1
|
||||
return 1
|
||||
|
||||
include macros
|
||||
|
||||
{.pop.} # checks
|
||||
{.pop.} # hints
|
||||
|
|
|
|||
|
|
@ -276,8 +276,8 @@ else:
|
|||
|
||||
proc getDateStr(): string =
|
||||
var ti = getLocalTime(getTime())
|
||||
result = $ti.year & "-" & intToStr(ord(ti.month)+1, 2) &
|
||||
"-" & intToStr(ti.monthDay, 2)
|
||||
result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) &
|
||||
'-' & intToStr(ti.monthDay, 2)
|
||||
|
||||
proc getClockStr(): string =
|
||||
var ti = getLocalTime(getTime())
|
||||
|
|
|
|||
1215
lib/unicode.nim
1215
lib/unicode.nim
File diff suppressed because it is too large
Load diff
|
|
@ -10,6 +10,8 @@
|
|||
## Define ``winUnicode`` before importing this module for the
|
||||
## unicode version.
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
type
|
||||
ATOM* = int16
|
||||
TAtom* = ATOM
|
||||
|
|
|
|||
2
makefile
2
makefile
|
|
@ -7,7 +7,7 @@ all:
|
|||
|
||||
.PHONY : install
|
||||
install:
|
||||
sh install.sh /usr/bin
|
||||
sh build.sh
|
||||
|
||||
.PHONY : clean
|
||||
clean:
|
||||
|
|
|
|||
277
nim/ast.pas
277
nim/ast.pas
|
|
@ -66,12 +66,93 @@ for key, val in enums.items():
|
|||
cog.out(b)
|
||||
]]]*)
|
||||
type
|
||||
TTypeFlag = (
|
||||
tfVarargs, tfFinal, tfAcyclic, tfEnumHasWholes);
|
||||
TTypeFlags = set of TTypeFlag;
|
||||
TNodeKind = (
|
||||
nkNone, nkEmpty, nkIdent, nkSym,
|
||||
nkType, nkCharLit, nkIntLit, nkInt8Lit,
|
||||
nkInt16Lit, nkInt32Lit, nkInt64Lit, nkFloatLit,
|
||||
nkFloat32Lit, nkFloat64Lit, nkStrLit, nkRStrLit,
|
||||
nkTripleStrLit, nkMetaNode, nkNilLit, nkDotCall,
|
||||
nkCommand, nkCall, nkGenericCall, nkExplicitTypeListCall,
|
||||
nkExprEqExpr, nkExprColonExpr, nkIdentDefs, nkVarTuple,
|
||||
nkInfix, nkPrefix, nkPostfix, nkPar,
|
||||
nkCurly, nkBracket, nkBracketExpr, nkPragmaExpr,
|
||||
nkRange, nkDotExpr, nkCheckedFieldExpr, nkDerefExpr,
|
||||
nkIfExpr, nkElifExpr, nkElseExpr, nkLambda,
|
||||
nkAccQuoted, nkHeaderQuoted, nkTableConstr, nkQualified,
|
||||
nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkConv,
|
||||
nkCast, nkAddr, nkHiddenAddr, nkHiddenDeref,
|
||||
nkObjDownConv, nkObjUpConv, nkChckRangeF, nkChckRange64,
|
||||
nkChckRange, nkStringToCString, nkCStringToString, nkPassAsOpenArray,
|
||||
nkAsgn, nkFastAsgn, nkDefaultTypeParam, nkGenericParams,
|
||||
nkFormalParams, nkOfInherit, nkModule, nkProcDef,
|
||||
nkConverterDef, nkMacroDef, nkTemplateDef, nkIteratorDef,
|
||||
nkOfBranch, nkElifBranch, nkExceptBranch, nkElse,
|
||||
nkMacroStmt, nkAsmStmt, nkPragma, nkIfStmt,
|
||||
nkWhenStmt, nkForStmt, nkWhileStmt, nkCaseStmt,
|
||||
nkVarSection, nkConstSection, nkConstDef, nkTypeSection,
|
||||
nkTypeDef, nkYieldStmt, nkTryStmt, nkFinally,
|
||||
nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt,
|
||||
nkBlockStmt, nkDiscardStmt, nkStmtList, nkImportStmt,
|
||||
nkFromStmt, nkImportAs, nkIncludeStmt, nkAccessStmt,
|
||||
nkCommentStmt, nkStmtListExpr, nkBlockExpr, nkStmtListType,
|
||||
nkBlockType, nkVm, nkTypeOfExpr, nkObjectTy,
|
||||
nkTupleTy, nkRecList, nkRecCase, nkRecWhen,
|
||||
nkRefTy, nkPtrTy, nkVarTy, nkProcTy,
|
||||
nkEnumTy, nkEnumFieldDef, nkReturnToken);
|
||||
TNodeKinds = set of TNodeKind;
|
||||
const
|
||||
TypeFlagToStr: array [TTypeFlag] of string = (
|
||||
'tfVarargs', 'tfFinal', 'tfAcyclic', 'tfEnumHasWholes');
|
||||
NodeKindToStr: array [TNodeKind] of string = (
|
||||
'nkNone', 'nkEmpty', 'nkIdent', 'nkSym',
|
||||
'nkType', 'nkCharLit', 'nkIntLit', 'nkInt8Lit',
|
||||
'nkInt16Lit', 'nkInt32Lit', 'nkInt64Lit', 'nkFloatLit',
|
||||
'nkFloat32Lit', 'nkFloat64Lit', 'nkStrLit', 'nkRStrLit',
|
||||
'nkTripleStrLit', 'nkMetaNode', 'nkNilLit', 'nkDotCall',
|
||||
'nkCommand', 'nkCall', 'nkGenericCall', 'nkExplicitTypeListCall',
|
||||
'nkExprEqExpr', 'nkExprColonExpr', 'nkIdentDefs', 'nkVarTuple',
|
||||
'nkInfix', 'nkPrefix', 'nkPostfix', 'nkPar',
|
||||
'nkCurly', 'nkBracket', 'nkBracketExpr', 'nkPragmaExpr',
|
||||
'nkRange', 'nkDotExpr', 'nkCheckedFieldExpr', 'nkDerefExpr',
|
||||
'nkIfExpr', 'nkElifExpr', 'nkElseExpr', 'nkLambda',
|
||||
'nkAccQuoted', 'nkHeaderQuoted', 'nkTableConstr', 'nkQualified',
|
||||
'nkHiddenStdConv', 'nkHiddenSubConv', 'nkHiddenCallConv', 'nkConv',
|
||||
'nkCast', 'nkAddr', 'nkHiddenAddr', 'nkHiddenDeref',
|
||||
'nkObjDownConv', 'nkObjUpConv', 'nkChckRangeF', 'nkChckRange64',
|
||||
'nkChckRange', 'nkStringToCString', 'nkCStringToString', 'nkPassAsOpenArray',
|
||||
'nkAsgn', 'nkFastAsgn', 'nkDefaultTypeParam', 'nkGenericParams',
|
||||
'nkFormalParams', 'nkOfInherit', 'nkModule', 'nkProcDef',
|
||||
'nkConverterDef', 'nkMacroDef', 'nkTemplateDef', 'nkIteratorDef',
|
||||
'nkOfBranch', 'nkElifBranch', 'nkExceptBranch', 'nkElse',
|
||||
'nkMacroStmt', 'nkAsmStmt', 'nkPragma', 'nkIfStmt',
|
||||
'nkWhenStmt', 'nkForStmt', 'nkWhileStmt', 'nkCaseStmt',
|
||||
'nkVarSection', 'nkConstSection', 'nkConstDef', 'nkTypeSection',
|
||||
'nkTypeDef', 'nkYieldStmt', 'nkTryStmt', 'nkFinally',
|
||||
'nkRaiseStmt', 'nkReturnStmt', 'nkBreakStmt', 'nkContinueStmt',
|
||||
'nkBlockStmt', 'nkDiscardStmt', 'nkStmtList', 'nkImportStmt',
|
||||
'nkFromStmt', 'nkImportAs', 'nkIncludeStmt', 'nkAccessStmt',
|
||||
'nkCommentStmt', 'nkStmtListExpr', 'nkBlockExpr', 'nkStmtListType',
|
||||
'nkBlockType', 'nkVm', 'nkTypeOfExpr', 'nkObjectTy',
|
||||
'nkTupleTy', 'nkRecList', 'nkRecCase', 'nkRecWhen',
|
||||
'nkRefTy', 'nkPtrTy', 'nkVarTy', 'nkProcTy',
|
||||
'nkEnumTy', 'nkEnumFieldDef', 'nkReturnToken');
|
||||
type
|
||||
TSymFlag = (
|
||||
sfUsed, sfStar, sfMinus, sfInInterface,
|
||||
sfFromGeneric, sfGlobal, sfForward, sfImportc,
|
||||
sfExportc, sfVolatile, sfRegister, sfPure,
|
||||
sfResult, sfNoSideEffect, sfMainModule, sfSystemModule,
|
||||
sfNoReturn, sfAddrTaken, sfCompilerProc, sfCppMethod,
|
||||
sfDiscriminant, sfDeprecated, sfInClosure, sfTypeCheck,
|
||||
sfCompileTime, sfThreadVar, sfMerge, sfDeadCodeElim);
|
||||
TSymFlags = set of TSymFlag;
|
||||
const
|
||||
SymFlagToStr: array [TSymFlag] of string = (
|
||||
'sfUsed', 'sfStar', 'sfMinus', 'sfInInterface',
|
||||
'sfFromGeneric', 'sfGlobal', 'sfForward', 'sfImportc',
|
||||
'sfExportc', 'sfVolatile', 'sfRegister', 'sfPure',
|
||||
'sfResult', 'sfNoSideEffect', 'sfMainModule', 'sfSystemModule',
|
||||
'sfNoReturn', 'sfAddrTaken', 'sfCompilerProc', 'sfCppMethod',
|
||||
'sfDiscriminant', 'sfDeprecated', 'sfInClosure', 'sfTypeCheck',
|
||||
'sfCompileTime', 'sfThreadVar', 'sfMerge', 'sfDeadCodeElim');
|
||||
type
|
||||
TTypeKind = (
|
||||
tyNone, tyBool, tyChar, tyEmpty,
|
||||
|
|
@ -95,25 +176,6 @@ const
|
|||
'tyCString', 'tyForward', 'tyInt', 'tyInt8',
|
||||
'tyInt16', 'tyInt32', 'tyInt64', 'tyFloat',
|
||||
'tyFloat32', 'tyFloat64', 'tyFloat128');
|
||||
type
|
||||
TSymFlag = (
|
||||
sfUsed, sfStar, sfMinus, sfInInterface,
|
||||
sfFromGeneric, sfGlobal, sfForward, sfImportc,
|
||||
sfExportc, sfVolatile, sfRegister, sfPure,
|
||||
sfResult, sfNoSideEffect, sfMainModule, sfSystemModule,
|
||||
sfNoReturn, sfAddrTaken, sfCompilerProc, sfCppMethod,
|
||||
sfDiscriminant, sfDeprecated, sfInClosure, sfTypeCheck,
|
||||
sfCompileTime, sfThreadVar, sfMerge);
|
||||
TSymFlags = set of TSymFlag;
|
||||
const
|
||||
SymFlagToStr: array [TSymFlag] of string = (
|
||||
'sfUsed', 'sfStar', 'sfMinus', 'sfInInterface',
|
||||
'sfFromGeneric', 'sfGlobal', 'sfForward', 'sfImportc',
|
||||
'sfExportc', 'sfVolatile', 'sfRegister', 'sfPure',
|
||||
'sfResult', 'sfNoSideEffect', 'sfMainModule', 'sfSystemModule',
|
||||
'sfNoReturn', 'sfAddrTaken', 'sfCompilerProc', 'sfCppMethod',
|
||||
'sfDiscriminant', 'sfDeprecated', 'sfInClosure', 'sfTypeCheck',
|
||||
'sfCompileTime', 'sfThreadVar', 'sfMerge');
|
||||
type
|
||||
TNodeFlag = (
|
||||
nfNone, nfBase2, nfBase8, nfBase16,
|
||||
|
|
@ -123,6 +185,13 @@ const
|
|||
NodeFlagToStr: array [TNodeFlag] of string = (
|
||||
'nfNone', 'nfBase2', 'nfBase8', 'nfBase16',
|
||||
'nfAllConst', 'nfTransf', 'nfSem');
|
||||
type
|
||||
TTypeFlag = (
|
||||
tfVarargs, tfFinal, tfAcyclic, tfEnumHasWholes);
|
||||
TTypeFlags = set of TTypeFlag;
|
||||
const
|
||||
TypeFlagToStr: array [TTypeFlag] of string = (
|
||||
'tfVarargs', 'tfFinal', 'tfAcyclic', 'tfEnumHasWholes');
|
||||
type
|
||||
TSymKind = (
|
||||
skUnknownSym, skConditional, skDynLib, skParam,
|
||||
|
|
@ -138,75 +207,6 @@ const
|
|||
'skVar', 'skProc', 'skIterator', 'skConverter',
|
||||
'skMacro', 'skTemplate', 'skField', 'skEnumField',
|
||||
'skForVar', 'skModule', 'skLabel', 'skStub');
|
||||
type
|
||||
TNodeKind = (
|
||||
nkNone, nkEmpty, nkIdent, nkSym,
|
||||
nkType, nkCharLit, nkIntLit, nkInt8Lit,
|
||||
nkInt16Lit, nkInt32Lit, nkInt64Lit, nkFloatLit,
|
||||
nkFloat32Lit, nkFloat64Lit, nkStrLit, nkRStrLit,
|
||||
nkTripleStrLit, nkMetaNode, nkNilLit, nkDotCall,
|
||||
nkCommand, nkCall, nkGenericCall, nkExplicitTypeListCall,
|
||||
nkExprEqExpr, nkExprColonExpr, nkIdentDefs, nkInfix,
|
||||
nkPrefix, nkPostfix, nkPar, nkCurly,
|
||||
nkBracket, nkBracketExpr, nkPragmaExpr, nkRange,
|
||||
nkDotExpr, nkCheckedFieldExpr, nkDerefExpr, nkIfExpr,
|
||||
nkElifExpr, nkElseExpr, nkLambda, nkAccQuoted,
|
||||
nkHeaderQuoted, nkTableConstr, nkQualified, nkHiddenStdConv,
|
||||
nkHiddenSubConv, nkHiddenCallConv, nkConv, nkCast,
|
||||
nkAddr, nkHiddenAddr, nkHiddenDeref, nkObjDownConv,
|
||||
nkObjUpConv, nkChckRangeF, nkChckRange64, nkChckRange,
|
||||
nkStringToCString, nkCStringToString, nkPassAsOpenArray, nkAsgn,
|
||||
nkDefaultTypeParam, nkGenericParams, nkFormalParams, nkOfInherit,
|
||||
nkModule, nkProcDef, nkConverterDef, nkMacroDef,
|
||||
nkTemplateDef, nkIteratorDef, nkOfBranch, nkElifBranch,
|
||||
nkExceptBranch, nkElse, nkMacroStmt, nkAsmStmt,
|
||||
nkPragma, nkIfStmt, nkWhenStmt, nkForStmt,
|
||||
nkWhileStmt, nkCaseStmt, nkVarSection, nkConstSection,
|
||||
nkConstDef, nkTypeSection, nkTypeDef, nkYieldStmt,
|
||||
nkTryStmt, nkFinally, nkRaiseStmt, nkReturnStmt,
|
||||
nkBreakStmt, nkContinueStmt, nkBlockStmt, nkDiscardStmt,
|
||||
nkStmtList, nkImportStmt, nkFromStmt, nkImportAs,
|
||||
nkIncludeStmt, nkAccessStmt, nkCommentStmt, nkStmtListExpr,
|
||||
nkBlockExpr, nkStmtListType, nkBlockType, nkVm,
|
||||
nkTypeOfExpr, nkObjectTy, nkTupleTy, nkRecList,
|
||||
nkRecCase, nkRecWhen, nkRefTy, nkPtrTy,
|
||||
nkVarTy, nkProcTy, nkEnumTy, nkEnumFieldDef,
|
||||
nkReturnToken);
|
||||
TNodeKinds = set of TNodeKind;
|
||||
const
|
||||
NodeKindToStr: array [TNodeKind] of string = (
|
||||
'nkNone', 'nkEmpty', 'nkIdent', 'nkSym',
|
||||
'nkType', 'nkCharLit', 'nkIntLit', 'nkInt8Lit',
|
||||
'nkInt16Lit', 'nkInt32Lit', 'nkInt64Lit', 'nkFloatLit',
|
||||
'nkFloat32Lit', 'nkFloat64Lit', 'nkStrLit', 'nkRStrLit',
|
||||
'nkTripleStrLit', 'nkMetaNode', 'nkNilLit', 'nkDotCall',
|
||||
'nkCommand', 'nkCall', 'nkGenericCall', 'nkExplicitTypeListCall',
|
||||
'nkExprEqExpr', 'nkExprColonExpr', 'nkIdentDefs', 'nkInfix',
|
||||
'nkPrefix', 'nkPostfix', 'nkPar', 'nkCurly',
|
||||
'nkBracket', 'nkBracketExpr', 'nkPragmaExpr', 'nkRange',
|
||||
'nkDotExpr', 'nkCheckedFieldExpr', 'nkDerefExpr', 'nkIfExpr',
|
||||
'nkElifExpr', 'nkElseExpr', 'nkLambda', 'nkAccQuoted',
|
||||
'nkHeaderQuoted', 'nkTableConstr', 'nkQualified', 'nkHiddenStdConv',
|
||||
'nkHiddenSubConv', 'nkHiddenCallConv', 'nkConv', 'nkCast',
|
||||
'nkAddr', 'nkHiddenAddr', 'nkHiddenDeref', 'nkObjDownConv',
|
||||
'nkObjUpConv', 'nkChckRangeF', 'nkChckRange64', 'nkChckRange',
|
||||
'nkStringToCString', 'nkCStringToString', 'nkPassAsOpenArray', 'nkAsgn',
|
||||
'nkDefaultTypeParam', 'nkGenericParams', 'nkFormalParams', 'nkOfInherit',
|
||||
'nkModule', 'nkProcDef', 'nkConverterDef', 'nkMacroDef',
|
||||
'nkTemplateDef', 'nkIteratorDef', 'nkOfBranch', 'nkElifBranch',
|
||||
'nkExceptBranch', 'nkElse', 'nkMacroStmt', 'nkAsmStmt',
|
||||
'nkPragma', 'nkIfStmt', 'nkWhenStmt', 'nkForStmt',
|
||||
'nkWhileStmt', 'nkCaseStmt', 'nkVarSection', 'nkConstSection',
|
||||
'nkConstDef', 'nkTypeSection', 'nkTypeDef', 'nkYieldStmt',
|
||||
'nkTryStmt', 'nkFinally', 'nkRaiseStmt', 'nkReturnStmt',
|
||||
'nkBreakStmt', 'nkContinueStmt', 'nkBlockStmt', 'nkDiscardStmt',
|
||||
'nkStmtList', 'nkImportStmt', 'nkFromStmt', 'nkImportAs',
|
||||
'nkIncludeStmt', 'nkAccessStmt', 'nkCommentStmt', 'nkStmtListExpr',
|
||||
'nkBlockExpr', 'nkStmtListType', 'nkBlockType', 'nkVm',
|
||||
'nkTypeOfExpr', 'nkObjectTy', 'nkTupleTy', 'nkRecList',
|
||||
'nkRecCase', 'nkRecWhen', 'nkRefTy', 'nkPtrTy',
|
||||
'nkVarTy', 'nkProcTy', 'nkEnumTy', 'nkEnumFieldDef',
|
||||
'nkReturnToken');
|
||||
{[[[end]]]}
|
||||
|
||||
type
|
||||
|
|
@ -240,22 +240,23 @@ type
|
|||
mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16,
|
||||
mToU32, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr,
|
||||
mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr,
|
||||
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet,
|
||||
mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet, mSymDiffSet,
|
||||
mConStrStr, mConArrArr, mConArrT, mConTArr, mConTT, mSlice,
|
||||
mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq, mInRange, mInSet,
|
||||
mAsgn, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mAssert,
|
||||
mSwap, mIsNil, mArrToSeq, mArray, mOpenArray, mRange,
|
||||
mSet, mSeq, mInt, mInt8, mInt16, mInt32,
|
||||
mInt64, mFloat, mFloat32, mFloat64, mBool, mChar,
|
||||
mString, mCstring, mPointer, mAnyEnum, mEmptySet, mIntSetBaseType,
|
||||
mNil, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion, mNimrodMajor,
|
||||
mNimrodMinor, mNimrodPatch, mCpuEndian, mNaN, mInf, mNegInf,
|
||||
mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel,
|
||||
mNKind, mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType,
|
||||
mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType,
|
||||
mNSetStrVal, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
|
||||
mEqIdent, mNHint, mNWarning, mNError
|
||||
mEnumToStr, mAnd, mOr, mEqStr, mLeStr, mLtStr,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
||||
mSymDiffSet, mConStrStr, mConArrArr, mConArrT, mConTArr, mConTT,
|
||||
mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mAppendSeqSeq, mInRange,
|
||||
mInSet, mAsgn, mRepr, mExit, mSetLengthStr, mSetLengthSeq,
|
||||
mAssert, mSwap, mIsNil, mArrToSeq, mArray, mOpenArray,
|
||||
mRange, mSet, mSeq, mInt, mInt8, mInt16,
|
||||
mInt32, mInt64, mFloat, mFloat32, mFloat64, mBool,
|
||||
mChar, mString, mCstring, mPointer, mAnyEnum, mEmptySet,
|
||||
mIntSetBaseType, mNil, mIsMainModule, mCompileDate, mCompileTime, mNimrodVersion,
|
||||
mNimrodMajor, mNimrodMinor, mNimrodPatch, mCpuEndian, mHostOS, mHostCPU,
|
||||
mNaN, mInf, mNegInf, mNLen, mNChild, mNSetChild,
|
||||
mNAdd, mNAddMultiple, mNDel, mNKind, mNIntVal, mNFloatVal,
|
||||
mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal,
|
||||
mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNNewNimNode, mNCopyNimNode,
|
||||
mNCopyNimTree, mStrToIdent, mIdentToStr, mEqIdent, mNHint, mNWarning,
|
||||
mNError
|
||||
//[[[end]]]
|
||||
);
|
||||
|
||||
|
|
@ -322,7 +323,6 @@ type
|
|||
locProc, // location is a proc (an address of a procedure)
|
||||
locData, // location is a constant
|
||||
locCall, // location is a call expression
|
||||
locImmediate, // location is an immediate value
|
||||
locOther // location is something other
|
||||
);
|
||||
|
||||
|
|
@ -498,22 +498,23 @@ const // "MagicToStr" array:
|
|||
'Ze16ToI', 'Ze16ToI64', 'Ze32ToI64', 'ZeIToI64', 'ToU8', 'ToU16',
|
||||
'ToU32', 'ToFloat', 'ToBiggestFloat', 'ToInt', 'ToBiggestInt', 'CharToStr',
|
||||
'BoolToStr', 'IntToStr', 'Int64ToStr', 'FloatToStr', 'CStrToStr', 'StrToStr',
|
||||
'And', 'Or', 'EqStr', 'LeStr', 'LtStr', 'EqSet',
|
||||
'LeSet', 'LtSet', 'MulSet', 'PlusSet', 'MinusSet', 'SymDiffSet',
|
||||
'ConStrStr', 'ConArrArr', 'ConArrT', 'ConTArr', 'ConTT', 'Slice',
|
||||
'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'AppendSeqSeq', 'InRange', 'InSet',
|
||||
'Asgn', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq', 'Assert',
|
||||
'Swap', 'IsNil', 'ArrToSeq', 'Array', 'OpenArray', 'Range',
|
||||
'Set', 'Seq', 'Int', 'Int8', 'Int16', 'Int32',
|
||||
'Int64', 'Float', 'Float32', 'Float64', 'Bool', 'Char',
|
||||
'String', 'Cstring', 'Pointer', 'AnyEnum', 'EmptySet', 'IntSetBaseType',
|
||||
'Nil', 'IsMainModule', 'CompileDate', 'CompileTime', 'NimrodVersion', 'NimrodMajor',
|
||||
'NimrodMinor', 'NimrodPatch', 'CpuEndian', 'NaN', 'Inf', 'NegInf',
|
||||
'NLen', 'NChild', 'NSetChild', 'NAdd', 'NAddMultiple', 'NDel',
|
||||
'NKind', 'NIntVal', 'NFloatVal', 'NSymbol', 'NIdent', 'NGetType',
|
||||
'NStrVal', 'NSetIntVal', 'NSetFloatVal', 'NSetSymbol', 'NSetIdent', 'NSetType',
|
||||
'NSetStrVal', 'NNewNimNode', 'NCopyNimNode', 'NCopyNimTree', 'StrToIdent', 'IdentToStr',
|
||||
'EqIdent', 'NHint', 'NWarning', 'NError'
|
||||
'EnumToStr', 'And', 'Or', 'EqStr', 'LeStr', 'LtStr',
|
||||
'EqSet', 'LeSet', 'LtSet', 'MulSet', 'PlusSet', 'MinusSet',
|
||||
'SymDiffSet', 'ConStrStr', 'ConArrArr', 'ConArrT', 'ConTArr', 'ConTT',
|
||||
'Slice', 'AppendStrCh', 'AppendStrStr', 'AppendSeqElem', 'AppendSeqSeq', 'InRange',
|
||||
'InSet', 'Asgn', 'Repr', 'Exit', 'SetLengthStr', 'SetLengthSeq',
|
||||
'Assert', 'Swap', 'IsNil', 'ArrToSeq', 'Array', 'OpenArray',
|
||||
'Range', 'Set', 'Seq', 'Int', 'Int8', 'Int16',
|
||||
'Int32', 'Int64', 'Float', 'Float32', 'Float64', 'Bool',
|
||||
'Char', 'String', 'Cstring', 'Pointer', 'AnyEnum', 'EmptySet',
|
||||
'IntSetBaseType', 'Nil', 'IsMainModule', 'CompileDate', 'CompileTime', 'NimrodVersion',
|
||||
'NimrodMajor', 'NimrodMinor', 'NimrodPatch', 'CpuEndian', 'HostOS', 'HostCPU',
|
||||
'NaN', 'Inf', 'NegInf', 'NLen', 'NChild', 'NSetChild',
|
||||
'NAdd', 'NAddMultiple', 'NDel', 'NKind', 'NIntVal', 'NFloatVal',
|
||||
'NSymbol', 'NIdent', 'NGetType', 'NStrVal', 'NSetIntVal', 'NSetFloatVal',
|
||||
'NSetSymbol', 'NSetIdent', 'NSetType', 'NSetStrVal', 'NNewNimNode', 'NCopyNimNode',
|
||||
'NCopyNimTree', 'StrToIdent', 'IdentToStr', 'EqIdent', 'NHint', 'NWarning',
|
||||
'NError'
|
||||
//[[[end]]]
|
||||
);
|
||||
|
||||
|
|
@ -1342,13 +1343,13 @@ end;
|
|||
|
||||
function IntSetContains(const s: TIntSet; key: int): bool;
|
||||
var
|
||||
u: int;
|
||||
u: TBitScalar;
|
||||
t: PTrunk;
|
||||
begin
|
||||
t := IntSetGet(s, key shr TrunkShift);
|
||||
t := IntSetGet(s, shru(key, TrunkShift));
|
||||
if t <> nil then begin
|
||||
u := key and TrunkMask;
|
||||
result := (t.bits[u shr IntShift] and (1 shl (u and IntMask))) <> 0
|
||||
result := (t.bits[shru(u, IntShift)] and shlu(1, u and IntMask)) <> 0
|
||||
end
|
||||
else
|
||||
result := false
|
||||
|
|
@ -1356,27 +1357,27 @@ end;
|
|||
|
||||
procedure IntSetIncl(var s: TIntSet; key: int);
|
||||
var
|
||||
u: int;
|
||||
u: TBitScalar;
|
||||
t: PTrunk;
|
||||
begin
|
||||
t := IntSetPut(s, key shr TrunkShift);
|
||||
t := IntSetPut(s, shru(key, TrunkShift));
|
||||
u := key and TrunkMask;
|
||||
t.bits[u shr IntShift] := t.bits[u shr IntShift]
|
||||
or (1 shl (u and IntMask));
|
||||
t.bits[shru(u, IntShift)] := t.bits[shru(u, IntShift)]
|
||||
or shlu(1, u and IntMask);
|
||||
end;
|
||||
|
||||
function IntSetContainsOrIncl(var s: TIntSet; key: int): bool;
|
||||
var
|
||||
u: int;
|
||||
u: TBitScalar;
|
||||
t: PTrunk;
|
||||
begin
|
||||
t := IntSetGet(s, key shr TrunkShift);
|
||||
t := IntSetGet(s, shru(key, TrunkShift));
|
||||
if t <> nil then begin
|
||||
u := key and TrunkMask;
|
||||
result := (t.bits[u shr IntShift] and (1 shl (u and IntMask))) <> 0;
|
||||
result := (t.bits[shru(u, IntShift)] and shlu(1, u and IntMask)) <> 0;
|
||||
if not result then
|
||||
t.bits[u shr IntShift] := t.bits[u shr IntShift]
|
||||
or (1 shl (u and IntMask));
|
||||
t.bits[shru(u, IntShift)] := t.bits[shru(u, IntShift)]
|
||||
or shlu(1, u and IntMask);
|
||||
end
|
||||
else begin
|
||||
IntSetIncl(s, key);
|
||||
|
|
|
|||
|
|
@ -450,29 +450,27 @@ begin
|
|||
else begin
|
||||
istr := spaces(indent+2);
|
||||
result := ropef('{$n$1"kind": $2',
|
||||
[istr, makeYamlString(nodeKindToStr[n.kind])]);
|
||||
[istr, makeYamlString(nodeKindToStr[n.kind])]);
|
||||
if maxRecDepth <> 0 then begin
|
||||
appf(result, ',$n$1"info": $2',
|
||||
[istr, lineInfoToStr(n.info)]);
|
||||
case n.kind of
|
||||
nkCharLit..nkInt64Lit:
|
||||
appf(result, '$n$1"intVal": $2', [istr, toRope(n.intVal)]);
|
||||
appf(result, ',$n$1"intVal": $2', [istr, toRope(n.intVal)]);
|
||||
nkFloatLit, nkFloat32Lit, nkFloat64Lit:
|
||||
appf(result, '$n$1"floatVal": $2',
|
||||
[istr, toRopeF(n.floatVal)]);
|
||||
appf(result, ',$n$1"floatVal": $2', [istr, toRopeF(n.floatVal)]);
|
||||
nkStrLit..nkTripleStrLit:
|
||||
appf(result, '$n$1"strVal": $2',
|
||||
[istr, makeYamlString(n.strVal)]);
|
||||
appf(result, ',$n$1"strVal": $2', [istr, makeYamlString(n.strVal)]);
|
||||
nkSym:
|
||||
appf(result, ',$n$1"sym": $2',
|
||||
[istr, symToYamlAux(n.sym, marker, indent+2, maxRecDepth)]);
|
||||
|
||||
nkIdent: begin
|
||||
if n.ident <> nil then
|
||||
appf(result, '$n$1"ident": $2',
|
||||
appf(result, ',$n$1"ident": $2',
|
||||
[istr, makeYamlString(n.ident.s)])
|
||||
else
|
||||
appf(result, '$n$1"ident": null', [istr])
|
||||
appf(result, ',$n$1"ident": null', [istr])
|
||||
end
|
||||
else begin
|
||||
if sonsLen(n) > 0 then begin
|
||||
|
|
@ -552,12 +550,12 @@ begin
|
|||
if maxRecDepth <> 0 then begin
|
||||
case n.kind of
|
||||
nkCharLit..nkInt64Lit:
|
||||
appf(result, '$n$1"intVal": $2', [istr, toRope(n.intVal)]);
|
||||
appf(result, ',$n$1"intVal": $2', [istr, toRope(n.intVal)]);
|
||||
nkFloatLit, nkFloat32Lit, nkFloat64Lit:
|
||||
appf(result, '$n$1"floatVal": $2',
|
||||
appf(result, ',$n$1"floatVal": $2',
|
||||
[istr, toRopeF(n.floatVal)]);
|
||||
nkStrLit..nkTripleStrLit:
|
||||
appf(result, '$n$1"strVal": $2',
|
||||
appf(result, ',$n$1"strVal": $2',
|
||||
[istr, makeYamlString(n.strVal)]);
|
||||
nkSym:
|
||||
appf(result, ',$n$1"sym": $2_$3',
|
||||
|
|
@ -565,10 +563,10 @@ begin
|
|||
|
||||
nkIdent: begin
|
||||
if n.ident <> nil then
|
||||
appf(result, '$n$1"ident": $2',
|
||||
appf(result, ',$n$1"ident": $2',
|
||||
[istr, makeYamlString(n.ident.s)])
|
||||
else
|
||||
appf(result, '$n$1"ident": null', [istr])
|
||||
appf(result, ',$n$1"ident": null', [istr])
|
||||
end
|
||||
else begin
|
||||
if sonsLen(n) > 0 then begin
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ var
|
|||
j: int;
|
||||
begin
|
||||
result := 0;
|
||||
if CPU[hostCPU].endian = CPU[targetCPU].endian then begin
|
||||
if CPU[platform.hostCPU].endian = CPU[targetCPU].endian then begin
|
||||
for j := 0 to size-1 do
|
||||
if j < length(s) then
|
||||
result := result or shlu(Ze64(s[j]), j * 8)
|
||||
|
|
@ -773,6 +773,34 @@ begin
|
|||
putIntoDest(p, d, field.typ, r);
|
||||
end;
|
||||
|
||||
procedure genTupleElem(p: BProc; e: PNode; var d: TLoc);
|
||||
var
|
||||
a: TLoc;
|
||||
field: PSym;
|
||||
ty: PType;
|
||||
r: PRope;
|
||||
i: int;
|
||||
begin
|
||||
initLocExpr(p, e.sons[0], a);
|
||||
if d.k = locNone then d.s := a.s;
|
||||
{@discard} getTypeDesc(p.module, a.t); // fill the record's fields.loc
|
||||
ty := getUniqueType(a.t);
|
||||
r := rdLoc(a);
|
||||
case e.sons[1].kind of
|
||||
nkIntLit..nkInt64Lit: i := int(e.sons[1].intVal);
|
||||
else internalError(e.info, 'genTupleElem');
|
||||
end;
|
||||
if ty.n <> nil then begin
|
||||
field := ty.n.sons[i].sym;
|
||||
if field = nil then InternalError(e.info, 'genTupleElem');
|
||||
if field.loc.r = nil then InternalError(e.info, 'genTupleElem');
|
||||
appf(r, '.$1', [field.loc.r]);
|
||||
end
|
||||
else
|
||||
appf(r, '.Field$1', [toRope(i)]);
|
||||
putIntoDest(p, d, ty.sons[i], r);
|
||||
end;
|
||||
|
||||
procedure genInExprAux(p: BProc; e: PNode; var a, b, d: TLoc); forward;
|
||||
|
||||
procedure genCheckedRecordField(p: BProc; e: PNode; var d: TLoc);
|
||||
|
|
@ -848,10 +876,11 @@ begin
|
|||
first := intLiteral(firstOrd(ty));
|
||||
// emit range check:
|
||||
if (optBoundsCheck in p.options) then begin
|
||||
if b.k <> locImmediate then begin // semantic pass has already checked:
|
||||
if not isConstExpr(e.sons[1]) then begin
|
||||
// semantic pass has already checked for const index expressions
|
||||
useMagic(p.module, 'raiseIndexError');
|
||||
if firstOrd(ty) = 0 then begin
|
||||
if lastOrd(b.t) > lastOrd(ty) then
|
||||
if (firstOrd(b.t) < firstOrd(ty)) or (lastOrd(b.t) > lastOrd(ty)) then
|
||||
appf(p.s[cpsStmts],
|
||||
'if ((NU)($1) > (NU)($2)) raiseIndexError();$n',
|
||||
[rdCharLoc(b), intLiteral(lastOrd(ty))])
|
||||
|
|
@ -1289,14 +1318,22 @@ var
|
|||
a, b, f: TLoc;
|
||||
refType, bt: PType;
|
||||
ti: PRope;
|
||||
oldModule: BModule;
|
||||
begin
|
||||
useMagic(p.module, 'newObj');
|
||||
refType := skipVarGenericRange(e.sons[1].typ);
|
||||
InitLocExpr(p, e.sons[1], a);
|
||||
|
||||
// This is a little hack:
|
||||
oldModule := p.module;
|
||||
p.module := gmti;
|
||||
InitLocExpr(p, e.sons[2], f);
|
||||
p.module := oldModule;
|
||||
|
||||
initLoc(b, locExpr, a.t, OnHeap);
|
||||
ti := genTypeInfo(p.module, refType);
|
||||
appf(p.module.s[cfsTypeInit3], '$1->finalizer = (void*)$2;$n', [
|
||||
|
||||
appf(gmti.s[cfsTypeInit3], '$1->finalizer = (void*)$2;$n', [
|
||||
ti, rdLoc(f)]);
|
||||
b.r := ropef('($1) newObj($2, sizeof($3))',
|
||||
[getTypeDesc(p.module, refType), ti,
|
||||
|
|
@ -1331,7 +1368,7 @@ begin
|
|||
UseMagic(p.module, 'reprChar');
|
||||
putIntoDest(p, d, e.typ, ropef('reprChar($1)', [rdLoc(a)]))
|
||||
end;
|
||||
tyEnum: begin
|
||||
tyEnum, tyAnyEnum: begin
|
||||
UseMagic(p.module, 'reprEnum');
|
||||
putIntoDest(p, d, e.typ,
|
||||
ropef('reprEnum($1, $2)', [rdLoc(a), genTypeInfo(p.module, t)]))
|
||||
|
|
@ -1853,6 +1890,7 @@ begin
|
|||
mFloatToStr: genDollar(p, e, d, 'nimFloatToStr', 'nimFloatToStr($1)');
|
||||
mCStrToStr: genDollar(p, e, d, 'cstrToNimstr', 'cstrToNimstr($1)');
|
||||
mStrToStr: expr(p, e.sons[1], d);
|
||||
mEnumToStr: genRepr(p, e, d);
|
||||
mAssert: begin
|
||||
if (optAssert in p.Options) then begin
|
||||
useMagic(p.module, 'internalAssert');
|
||||
|
|
@ -1996,23 +2034,31 @@ var
|
|||
it: PNode;
|
||||
t: PType;
|
||||
begin
|
||||
// the code generator assumes that there are only tuple constructors with
|
||||
// field names!
|
||||
if not handleConstExpr(p, n, d) then begin
|
||||
t := getUniqueType(n.typ);
|
||||
{@discard} getTypeDesc(p.module, t); // so that any fields are initialized
|
||||
if d.k = locNone then getTemp(p, t, d);
|
||||
if t.n = nil then InternalError(n.info, 'genTupleConstr');
|
||||
if sonsLen(t.n) <> sonsLen(n) then
|
||||
InternalError(n.info, 'genTupleConstr');
|
||||
for i := 0 to sonsLen(n)-1 do begin
|
||||
it := n.sons[i];
|
||||
if it.kind <> nkExprColonExpr then InternalError(n.info, 'genTupleConstr');
|
||||
initLoc(rec, locExpr, it.sons[1].typ, d.s);
|
||||
if (t.n.sons[i].kind <> nkSym) then
|
||||
InternalError(n.info, 'genTupleConstr');
|
||||
rec.r := ropef('$1.$2', [rdLoc(d), mangleRecFieldName(t.n.sons[i].sym, t)]);
|
||||
expr(p, it.sons[1], rec);
|
||||
if it.kind = nkExprColonExpr then begin
|
||||
initLoc(rec, locExpr, it.sons[1].typ, d.s);
|
||||
if (t.n.sons[i].kind <> nkSym) then
|
||||
InternalError(n.info, 'genTupleConstr');
|
||||
rec.r := ropef('$1.$2', [rdLoc(d), mangleRecFieldName(t.n.sons[i].sym, t)]);
|
||||
expr(p, it.sons[1], rec);
|
||||
end
|
||||
else if t.n = nil then begin
|
||||
initLoc(rec, locExpr, it.typ, d.s);
|
||||
rec.r := ropef('$1.Field$2', [rdLoc(d), toRope(i)]);
|
||||
expr(p, it, rec);
|
||||
end
|
||||
else begin
|
||||
initLoc(rec, locExpr, it.typ, d.s);
|
||||
if (t.n.sons[i].kind <> nkSym) then
|
||||
InternalError(n.info, 'genTupleConstr: 2');
|
||||
rec.r := ropef('$1.$2', [rdLoc(d), mangleRecFieldName(t.n.sons[i].sym, t)]);
|
||||
expr(p, it, rec);
|
||||
end
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
|
@ -2123,11 +2169,10 @@ begin
|
|||
sym := e.sym;
|
||||
case sym.Kind of
|
||||
skProc, skConverter: begin
|
||||
// generate prototype if not already declared in this translation unit
|
||||
genProcPrototype(p.module, sym);
|
||||
genProc(p.module, sym);
|
||||
if ((sym.loc.r = nil) or (sym.loc.t = nil)) then
|
||||
InternalError(e.info, 'expr: proc not init ' + sym.name.s);
|
||||
putLocIntoDest(p, d, sym.loc)
|
||||
putLocIntoDest(p, d, sym.loc);
|
||||
end;
|
||||
skConst:
|
||||
if isSimpleConst(sym.typ) then
|
||||
|
|
@ -2160,8 +2205,6 @@ begin
|
|||
nkStrLit..nkTripleStrLit, nkIntLit..nkInt64Lit,
|
||||
nkFloatLit..nkFloat64Lit, nkNilLit, nkCharLit: begin
|
||||
putIntoDest(p, d, e.typ, genLiteral(p, e));
|
||||
if d.k in [locNone, locExpr] then
|
||||
d.k := locImmediate // for removal of index checks
|
||||
end;
|
||||
nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand: begin
|
||||
if (e.sons[0].kind = nkSym) and
|
||||
|
|
@ -2189,6 +2232,7 @@ begin
|
|||
tyOpenArray: genOpenArrayElem(p, e, d);
|
||||
tySequence, tyString: genSeqElem(p, e, d);
|
||||
tyCString: genCStringElem(p, e, d);
|
||||
tyTuple: genTupleElem(p, e, d);
|
||||
else InternalError(e.info,
|
||||
'expr(nkBracketExpr, ' + typeKindToStr[ty.kind] + ')');
|
||||
end
|
||||
|
|
|
|||
|
|
@ -801,9 +801,16 @@ begin
|
|||
assert(key.kind = nkIdent);
|
||||
case whichKeyword(key.ident) of
|
||||
wBreakpoint: genBreakPoint(p, it);
|
||||
wDeadCodeElim: begin
|
||||
if not (optDeadCodeElim in gGlobalOptions) then begin
|
||||
// we need to keep track of ``deadCodeElim`` pragma
|
||||
if (sfDeadCodeElim in p.module.module.flags) then
|
||||
addPendingModule(p.module)
|
||||
end
|
||||
end
|
||||
else begin end
|
||||
end
|
||||
end
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure genAsgn(p: BProc; e: PNode);
|
||||
|
|
@ -816,6 +823,17 @@ begin
|
|||
expr(p, e.sons[1], a);
|
||||
end;
|
||||
|
||||
procedure genFastAsgn(p: BProc; e: PNode);
|
||||
var
|
||||
a: TLoc;
|
||||
begin
|
||||
genLineDir(p, e); // BUGFIX
|
||||
InitLocExpr(p, e.sons[0], a);
|
||||
include(a.flags, lfNoDeepCopy);
|
||||
assert(a.t <> nil);
|
||||
expr(p, e.sons[1], a);
|
||||
end;
|
||||
|
||||
procedure genStmts(p: BProc; t: PNode);
|
||||
var
|
||||
a: TLoc;
|
||||
|
|
@ -844,6 +862,7 @@ begin
|
|||
initLocExpr(p, t, a);
|
||||
end;
|
||||
nkAsgn: genAsgn(p, t);
|
||||
nkFastAsgn: genFastAsgn(p, t);
|
||||
nkDiscardStmt: begin
|
||||
genLineDir(p, t);
|
||||
initLocExpr(p, t.sons[0], a);
|
||||
|
|
@ -865,17 +884,13 @@ begin
|
|||
nkProcDef, nkConverterDef: begin
|
||||
if (t.sons[genericParamsPos] = nil) then begin
|
||||
prc := t.sons[namePos].sym;
|
||||
if (t.sons[codePos] <> nil)
|
||||
or (lfDynamicLib in prc.loc.flags) then begin // BUGFIX
|
||||
if IntSetContainsOrIncl(p.module.debugDeclared, prc.id) then begin
|
||||
internalError(t.info, 'genStmts(): ' + toString(prc.id));
|
||||
// XXX: remove this check!
|
||||
end;
|
||||
//if IntSetContains(p.module.debugDeclared, 2642) then
|
||||
// InternalError(t.info, 'this sucks ' + toString(prc.id));
|
||||
genProc(p.module, prc)
|
||||
if not (optDeadCodeElim in gGlobalOptions) and
|
||||
not (sfDeadCodeElim in getModule(prc).flags)
|
||||
or ([sfExportc, sfCompilerProc] * prc.flags = [sfExportc]) then begin
|
||||
if (t.sons[codePos] <> nil) or (lfDynamicLib in prc.loc.flags) then begin
|
||||
genProc(p.module, prc)
|
||||
end
|
||||
end
|
||||
//else if sfCompilerProc in prc.flags then genProcPrototype(prc);
|
||||
end
|
||||
end;
|
||||
else
|
||||
|
|
|
|||
170
nim/ccgtypes.pas
170
nim/ccgtypes.pas
|
|
@ -8,7 +8,7 @@
|
|||
//
|
||||
|
||||
//var
|
||||
// newDummyVar: int; // just to check the rodgen mechanism
|
||||
// newDummyVar: int; // just to check the symbol file mechanism
|
||||
|
||||
// ------------------------- Name Mangling --------------------------------
|
||||
|
||||
|
|
@ -461,6 +461,22 @@ begin
|
|||
app(result, '};' + tnl);
|
||||
end;
|
||||
|
||||
function getTupleDesc(m: BModule; typ: PType; name: PRope;
|
||||
var check: TIntSet): PRope;
|
||||
var
|
||||
desc: PRope;
|
||||
i: int;
|
||||
begin
|
||||
result := ropef('struct $1 {$n', [name]);
|
||||
desc := nil;
|
||||
for i := 0 to sonsLen(typ)-1 do
|
||||
appf(desc, '$1 Field$2;$n',
|
||||
[getTypeDescAux(m, typ.sons[i], check), toRope(i)]);
|
||||
if (desc = nil) then app(result, 'char dummy;' + tnl)
|
||||
else app(result, desc);
|
||||
app(result, '};' + tnl);
|
||||
end;
|
||||
|
||||
procedure pushType(m: BModule; typ: PType);
|
||||
var
|
||||
L: int;
|
||||
|
|
@ -571,13 +587,15 @@ begin
|
|||
if result = nil then begin
|
||||
result := getTypeName(t);
|
||||
if not isImportedType(t) then
|
||||
appf(m.s[cfsForwardTypes],
|
||||
getForwardStructFormat(), [result]);
|
||||
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]);
|
||||
IdTablePut(m.forwTypeCache, t, result)
|
||||
end;
|
||||
IdTablePut(m.typeCache, t, result);
|
||||
// always call for sideeffects:
|
||||
recdesc := getRecordDesc(m, t, result, check);
|
||||
if t.n <> nil then
|
||||
recdesc := getRecordDesc(m, t, result, check)
|
||||
else
|
||||
recdesc := getTupleDesc(m, t, result, check);
|
||||
if not isImportedType(t) then app(m.s[cfsTypes], recdesc);
|
||||
end;
|
||||
tySet: begin
|
||||
|
|
@ -735,9 +753,11 @@ begin
|
|||
assert(n.sons[0].kind = nkSym);
|
||||
field := n.sons[0].sym;
|
||||
tmp := getTempName();
|
||||
useMagic(m, 'chckNil');
|
||||
appf(m.s[cfsTypeInit3], '$1.kind = 3;$n' +
|
||||
'$1.offset = offsetof($2, $3);$n' +
|
||||
'$1.typ = $4;$n' +
|
||||
'chckNil($1.typ);$n' +
|
||||
'$1.name = $5;$n' +
|
||||
'$1.sons = &$6[0];$n' +
|
||||
'$1.len = $7;$n',
|
||||
|
|
@ -746,7 +766,7 @@ begin
|
|||
makeCString(field.name.s), tmp,
|
||||
toRope(lengthOrd(field.typ))]);
|
||||
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n',
|
||||
[tmp, toRope(lengthOrd(field.typ)+1)]);
|
||||
[tmp, toRope(lengthOrd(field.typ)+1)]);
|
||||
for i := 1 to len-1 do begin
|
||||
b := n.sons[i]; // branch
|
||||
tmp2 := getNimNode(m);
|
||||
|
|
@ -761,18 +781,18 @@ begin
|
|||
y := int(getOrdValue(b.sons[j].sons[1]));
|
||||
while x <= y do begin
|
||||
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n',
|
||||
[tmp, toRope(x), tmp2]);
|
||||
[tmp, toRope(x), tmp2]);
|
||||
inc(x);
|
||||
end;
|
||||
end
|
||||
else
|
||||
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n',
|
||||
[tmp, toRope(getOrdValue(b.sons[j])), tmp2])
|
||||
[tmp, toRope(getOrdValue(b.sons[j])), tmp2])
|
||||
end
|
||||
end;
|
||||
nkElse: begin
|
||||
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n',
|
||||
[tmp, toRope(lengthOrd(field.typ)), tmp2]);
|
||||
[tmp, toRope(lengthOrd(field.typ)), tmp2]);
|
||||
end
|
||||
else
|
||||
internalError(n.info, 'genObjectFields(nkRecCase)');
|
||||
|
|
@ -781,9 +801,11 @@ begin
|
|||
end;
|
||||
nkSym: begin
|
||||
field := n.sym;
|
||||
useMagic(m, 'chckNil');
|
||||
appf(m.s[cfsTypeInit3], '$1.kind = 1;$n' +
|
||||
'$1.offset = offsetof($2, $3);$n' +
|
||||
'$1.typ = $4;$n' +
|
||||
'chckNil($1.typ);$n' +
|
||||
'$1.name = $5;$n',
|
||||
[expr, getTypeDesc(m, typ), field.loc.r,
|
||||
genTypeInfo(m, field.typ),
|
||||
|
|
@ -804,6 +826,41 @@ begin
|
|||
appf(m.s[cfsTypeInit3], '$1->node = &$2;$n', [name, tmp]);
|
||||
end;
|
||||
|
||||
procedure genTupleInfo(m: BModule; typ: PType; name: PRope);
|
||||
var
|
||||
tmp, expr, tmp2: PRope;
|
||||
i, len: int;
|
||||
a: PType;
|
||||
begin
|
||||
genTypeInfoAuxBase(m, typ, name, toRope('0'+''));
|
||||
expr := getNimNode(m);
|
||||
len := sonsLen(typ);
|
||||
if len > 0 then begin
|
||||
tmp := getTempName();
|
||||
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n', [tmp, toRope(len)]);
|
||||
for i := 0 to len-1 do begin
|
||||
a := typ.sons[i];
|
||||
tmp2 := getNimNode(m);
|
||||
appf(m.s[cfsTypeInit3], '$1[$2] = &$3;$n', [tmp, toRope(i), tmp2]);
|
||||
useMagic(m, 'chckNil');
|
||||
appf(m.s[cfsTypeInit3], '$1.kind = 1;$n' +
|
||||
'$1.offset = offsetof($2, Field$3);$n' +
|
||||
'$1.typ = $4;$n' +
|
||||
'chckNil($1.typ);$n' +
|
||||
'$1.name = "Field$3";$n',
|
||||
[tmp2, getTypeDesc(m, typ), toRope(i),
|
||||
genTypeInfo(m, a)]);
|
||||
end;
|
||||
appf(m.s[cfsTypeInit3],
|
||||
'$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n', [
|
||||
expr, toRope(len), tmp]);
|
||||
end
|
||||
else
|
||||
appf(m.s[cfsTypeInit3],
|
||||
'$1.len = $2; $1.kind = 2;$n', [expr, toRope(len)]);
|
||||
appf(m.s[cfsTypeInit3], '$1->node = &$2;$n', [name, tmp]);
|
||||
end;
|
||||
|
||||
procedure genEnumInfo(m: BModule; typ: PType; name: PRope);
|
||||
var
|
||||
nodePtrs, elemNode, enumNames, enumArray, counter, specialCases: PRope;
|
||||
|
|
@ -817,7 +874,8 @@ begin
|
|||
genTypeInfoAux(m, typ, name);
|
||||
nodePtrs := getTempName();
|
||||
len := sonsLen(typ.n);
|
||||
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n', [nodePtrs, toRope(len)]);
|
||||
appf(m.s[cfsTypeInit1], 'static TNimNode* $1[$2];$n',
|
||||
[nodePtrs, toRope(len)]);
|
||||
enumNames := nil;
|
||||
specialCases := nil;
|
||||
firstNimNode := m.typeNodes;
|
||||
|
|
@ -879,83 +937,79 @@ function genTypeInfo(m: BModule; typ: PType): PRope;
|
|||
var
|
||||
t: PType;
|
||||
id: int;
|
||||
dataGen: bool;
|
||||
dataGenerated: bool;
|
||||
begin
|
||||
t := getUniqueType(typ);
|
||||
id := IiTableGet(gToTypeInfoId, t.id);
|
||||
if id = invalidKey then begin
|
||||
dataGen := false;
|
||||
case t.kind of
|
||||
tyEnum, tyBool: begin
|
||||
id := t.id;
|
||||
dataGen := true
|
||||
end;
|
||||
tyObject: begin
|
||||
if isPureObject(t) then
|
||||
id := getID()
|
||||
else begin
|
||||
id := t.id;
|
||||
dataGen := true
|
||||
end
|
||||
end
|
||||
else
|
||||
id := getID();
|
||||
end;
|
||||
dataGenerated := false;
|
||||
id := t.id; // getID();
|
||||
IiTablePut(gToTypeInfoId, t.id, id);
|
||||
end
|
||||
else
|
||||
dataGen := true;
|
||||
dataGenerated := true;
|
||||
result := ropef('NTI$1', [toRope(id)]);
|
||||
if not IntSetContainsOrIncl(m.typeInfoMarker, t.id) then begin
|
||||
if not IntSetContainsOrIncl(m.typeInfoMarker, id) then begin
|
||||
// declare type information structures:
|
||||
useMagic(m, 'TNimType');
|
||||
useMagic(m, 'TNimNode');
|
||||
if dataGen then
|
||||
appf(m.s[cfsVars], 'extern TNimType* $1; /* $2 */$n',
|
||||
[result, toRope(typeToString(t))]);
|
||||
appf(m.s[cfsVars], 'extern TNimType* $1; /* $2 */$n',
|
||||
[result, toRope(typeToString(t))]);
|
||||
end;
|
||||
if dataGen then exit;
|
||||
if dataGenerated then exit;
|
||||
case t.kind of
|
||||
tyPointer, tyProc, tyBool, tyChar, tyCString, tyString,
|
||||
tyInt..tyFloat128, tyVar:
|
||||
genTypeInfoAuxBase(m, t, result, toRope('0'+''));
|
||||
tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(m, t, result);
|
||||
tyArrayConstr, tyArray: genArrayInfo(m, t, result);
|
||||
tySet: genSetInfo(m, t, result);
|
||||
tyEnum: genEnumInfo(m, t, result);
|
||||
tyObject, tyTuple: genObjectInfo(m, t, result);
|
||||
genTypeInfoAuxBase(gmti, t, result, toRope('0'+''));
|
||||
tyRef, tyPtr, tySequence, tyRange: genTypeInfoAux(gmti, t, result);
|
||||
tyArrayConstr, tyArray: genArrayInfo(gmti, t, result);
|
||||
tySet: genSetInfo(gmti, t, result);
|
||||
tyEnum: genEnumInfo(gmti, t, result);
|
||||
tyObject: genObjectInfo(gmti, t, result);
|
||||
tyTuple: begin
|
||||
if t.n <> nil then genObjectInfo(gmti, t, result)
|
||||
else genTupleInfo(gmti, t, result);
|
||||
end;
|
||||
else InternalError('genTypeInfo(' + typekindToStr[t.kind] + ')');
|
||||
end
|
||||
end;
|
||||
|
||||
procedure genTypeSection(m: BModule; n: PNode);
|
||||
begin
|
||||
end;
|
||||
|
||||
(*
|
||||
procedure genTypeSection(m: BModule; n: PNode);
|
||||
var
|
||||
i: int;
|
||||
a: PNode;
|
||||
t: PType;
|
||||
begin
|
||||
for i := 0 to sonsLen(n)-1 do begin
|
||||
a := n.sons[i];
|
||||
if a.kind = nkCommentStmt then continue;
|
||||
if (a.sons[0].kind <> nkSym) then InternalError(a.info, 'genTypeSection');
|
||||
t := a.sons[0].sym.typ;
|
||||
if (a.sons[2] = nil)
|
||||
or not (a.sons[2].kind in [nkSym, nkIdent, nkAccQuoted]) then
|
||||
if t <> nil then
|
||||
case t.kind of
|
||||
tyEnum, tyBool: begin
|
||||
useMagic(m, 'TNimType');
|
||||
useMagic(m, 'TNimNode');
|
||||
genEnumInfo(m, t, ropef('NTI$1', [toRope(t.id)]));
|
||||
end;
|
||||
tyObject: begin
|
||||
if not isPureObject(t) then begin
|
||||
if not (optDeadCodeElim in gGlobalOptions) then begin
|
||||
for i := 0 to sonsLen(n)-1 do begin
|
||||
a := n.sons[i];
|
||||
if a.kind = nkCommentStmt then continue;
|
||||
if (a.sons[0].kind <> nkSym) then InternalError(a.info, 'genTypeSection');
|
||||
t := a.sons[0].sym.typ;
|
||||
if (a.sons[2] = nil)
|
||||
or not (a.sons[2].kind in [nkSym, nkIdent, nkAccQuoted]) then
|
||||
if t <> nil then
|
||||
case t.kind of
|
||||
tyEnum, tyBool: begin
|
||||
useMagic(m, 'TNimType');
|
||||
useMagic(m, 'TNimNode');
|
||||
genObjectInfo(m, t, ropef('NTI$1', [toRope(t.id)]));
|
||||
genEnumInfo(m, t, ropef('NTI$1', [toRope(t.id)]));
|
||||
end;
|
||||
tyObject: begin
|
||||
if not isPureObject(t) then begin
|
||||
useMagic(m, 'TNimType');
|
||||
useMagic(m, 'TNimNode');
|
||||
genObjectInfo(m, t, ropef('NTI$1', [toRope(t.id)]));
|
||||
end
|
||||
end
|
||||
else begin end
|
||||
end
|
||||
else begin end
|
||||
end
|
||||
end
|
||||
end
|
||||
end;
|
||||
*)
|
||||
|
|
@ -27,16 +27,48 @@ function GetUniqueType(key: PType): PType;
|
|||
implementation
|
||||
|
||||
var
|
||||
gTypeTable: TIdTable;
|
||||
gTypeTable: array [TTypeKind] of TIdTable;
|
||||
|
||||
procedure initTypeTables();
|
||||
var
|
||||
i: TTypeKind;
|
||||
begin
|
||||
for i := low(TTypeKind) to high(TTypeKind) do
|
||||
InitIdTable(gTypeTable[i]);
|
||||
end;
|
||||
|
||||
function GetUniqueType(key: PType): PType;
|
||||
var
|
||||
t: PType;
|
||||
h: THash;
|
||||
k: TTypeKind;
|
||||
begin
|
||||
// this was a hotspot in the compiler!
|
||||
// this is a hotspot in the compiler!
|
||||
result := key;
|
||||
if key = nil then exit;
|
||||
k := key.kind;
|
||||
case k of
|
||||
tyObject, tyEnum: begin
|
||||
result := PType(IdTableGet(gTypeTable[k], key));
|
||||
if result = nil then begin
|
||||
IdTablePut(gTypeTable[k], key, key);
|
||||
result := key;
|
||||
end
|
||||
end;
|
||||
tyGenericInst: result := GetUniqueType(lastSon(key));
|
||||
tyProc: begin end;
|
||||
else begin
|
||||
// we have to do a slow linear search because types may need
|
||||
// to be compared by their structure:
|
||||
if IdTableHasObjectAsKey(gTypeTable[k], key) then exit;
|
||||
for h := 0 to high(gTypeTable[k].data) do begin
|
||||
t := PType(gTypeTable[k].data[h].key);
|
||||
if (t <> nil) and sameType(t, key) then begin result := t; exit end
|
||||
end;
|
||||
IdTablePut(gTypeTable[k], key, key);
|
||||
end;
|
||||
end;
|
||||
(*
|
||||
case key.Kind of
|
||||
tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
|
||||
tyInt..tyFloat128, tyProc, tyAnyEnum: begin end;
|
||||
|
|
@ -62,7 +94,7 @@ begin
|
|||
end
|
||||
end;
|
||||
tyGenericInst: result := GetUniqueType(lastSon(key));
|
||||
end;
|
||||
end; *)
|
||||
end;
|
||||
|
||||
function TableGetType(const tab: TIdTable; key: PType): PObject;
|
||||
|
|
@ -122,6 +154,6 @@ begin
|
|||
app(result, toRope(res));
|
||||
end;
|
||||
|
||||
initialization
|
||||
InitIdTable(gTypeTable);
|
||||
begin
|
||||
InitTypeTables();
|
||||
end.
|
||||
|
|
|
|||
457
nim/cgen.pas
457
nim/cgen.pas
|
|
@ -73,7 +73,6 @@ type
|
|||
cpsStmts // section of local statements for C proc
|
||||
);
|
||||
|
||||
|
||||
TCProcSections = array [TCProcSection] of PRope;
|
||||
// TCProcSections represents a generated C proc
|
||||
|
||||
|
|
@ -112,21 +111,64 @@ type
|
|||
typeCache: TIdTable; // cache the generated types
|
||||
forwTypeCache: TIdTable; // cache for forward declarations of types
|
||||
declaredThings: TIntSet; // things we have declared in this .c file
|
||||
debugDeclared: TIntSet; // for debugging purposes
|
||||
declaredProtos: TIntSet; // prototypes we have declared in this .c file
|
||||
headerFiles: TLinkedList; // needed headers to include
|
||||
typeInfoMarker: TIntSet; // needed for generating type information
|
||||
initProc: BProc; // code for init procedure
|
||||
typeStack: TTypeSeq; // used for type generation
|
||||
dataCache: TNodeTable;
|
||||
forwardedProcs: TSymSeq; // keep forwarded procs here
|
||||
typeNodes, nimTypes: int;// used for type info generation
|
||||
typeNodesName, nimTypesName: PRope; // used for type info generation
|
||||
typeNodesName, nimTypesName: PRope; // used for type info generation
|
||||
end;
|
||||
|
||||
var
|
||||
mainModProcs, mainModInit: PRope; // parts of the main module
|
||||
gMapping: PRope; // the generated mapping file (if requested)
|
||||
gProcProfile: Natural; // proc profile counter
|
||||
gGeneratedSyms: TIntSet; // set of ID's of generated symbols
|
||||
gPendingModules: array of BModule = {@ignore} nil {@emit @[]};
|
||||
// list of modules that are not finished with code generation
|
||||
gForwardedProcsCounter: int = 0;
|
||||
gmti: BModule; // generated type info: no need to initialize: defaults fit
|
||||
|
||||
procedure addForwardedProc(m: BModule; prc: PSym);
|
||||
var
|
||||
L: int;
|
||||
begin
|
||||
L := length(m.forwardedProcs);
|
||||
setLength(m.forwardedProcs, L+1);
|
||||
m.forwardedProcs[L] := prc;
|
||||
inc(gForwardedProcsCounter);
|
||||
end;
|
||||
|
||||
procedure addPendingModule(m: BModule);
|
||||
var
|
||||
L, i: int;
|
||||
begin
|
||||
for i := 0 to high(gPendingModules) do
|
||||
if gPendingModules[i] = m then
|
||||
InternalError('module already pending: ' + m.module.name.s);
|
||||
L := length(gPendingModules);
|
||||
setLength(gPendingModules, L+1);
|
||||
gPendingModules[L] := m;
|
||||
end;
|
||||
|
||||
function findPendingModule(m: BModule; s: PSym): BModule;
|
||||
var
|
||||
ms: PSym;
|
||||
i: int;
|
||||
begin
|
||||
ms := getModule(s);
|
||||
if ms.id = m.module.id then begin
|
||||
result := m; exit
|
||||
end;
|
||||
for i := 0 to high(gPendingModules) do begin
|
||||
result := gPendingModules[i];
|
||||
if result.module.id = ms.id then exit;
|
||||
end;
|
||||
InternalError(s.info, 'no pending module found for: ' + s.name.s);
|
||||
end;
|
||||
|
||||
procedure initLoc(var result: TLoc; k: TLocKind; typ: PType; s: TStorageLoc);
|
||||
begin
|
||||
|
|
@ -209,26 +251,13 @@ end;
|
|||
|
||||
// -------------------------- Variable manager ----------------------------
|
||||
|
||||
procedure declareGlobalVar(m: BModule; s: PSym);
|
||||
begin
|
||||
if not IntSetContainsOrIncl(m.declaredThings, s.id) then begin
|
||||
app(m.s[cfsVars], getTypeDesc(m, s.loc.t));
|
||||
if sfRegister in s.flags then
|
||||
app(m.s[cfsVars], ' register');
|
||||
if sfVolatile in s.flags then
|
||||
app(m.s[cfsVars], ' volatile');
|
||||
if sfThreadVar in s.flags then
|
||||
app(m.s[cfsVars], ' NIM_THREADVAR');
|
||||
appf(m.s[cfsVars], ' $1;$n', [s.loc.r])
|
||||
end
|
||||
end;
|
||||
|
||||
procedure assignLocalVar(p: BProc; s: PSym);
|
||||
begin
|
||||
//assert(s.loc.k == locNone) // not yet assigned
|
||||
// this need not be fullfilled for inline procs; they are regenerated
|
||||
// for each module that uses them!
|
||||
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack);
|
||||
if s.loc.k = locNone then
|
||||
fillLoc(s.loc, locLocalVar, s.typ, mangleName(s), OnStack);
|
||||
app(p.s[cpsLocals], getTypeDesc(p.module, s.loc.t));
|
||||
if sfRegister in s.flags then
|
||||
app(p.s[cpsLocals], ' register');
|
||||
|
|
@ -248,11 +277,19 @@ end;
|
|||
|
||||
procedure assignGlobalVar(m: BModule; s: PSym);
|
||||
begin
|
||||
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap);
|
||||
if s.loc.k = locNone then
|
||||
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap);
|
||||
useHeader(m, s);
|
||||
if lfNoDecl in s.loc.flags then exit;
|
||||
if sfImportc in s.flags then app(m.s[cfsVars], 'extern ');
|
||||
declareGlobalVar(m, s);
|
||||
app(m.s[cfsVars], getTypeDesc(m, s.loc.t));
|
||||
if sfRegister in s.flags then
|
||||
app(m.s[cfsVars], ' register');
|
||||
if sfVolatile in s.flags then
|
||||
app(m.s[cfsVars], ' volatile');
|
||||
if sfThreadVar in s.flags then
|
||||
app(m.s[cfsVars], ' NIM_THREADVAR');
|
||||
appf(m.s[cfsVars], ' $1;$n', [s.loc.r]);
|
||||
if [optStackTrace, optEndb] * m.module.options =
|
||||
[optStackTrace, optEndb] then begin
|
||||
useMagic(m, 'dbgRegisterGlobal');
|
||||
|
|
@ -282,6 +319,12 @@ begin
|
|||
end
|
||||
end;
|
||||
|
||||
procedure fillProcLoc(sym: PSym);
|
||||
begin
|
||||
if sym.loc.k = locNone then
|
||||
fillLoc(sym.loc, locProc, sym.typ, mangleName(sym), OnStack);
|
||||
end;
|
||||
|
||||
// -------------------------- label manager -------------------------------
|
||||
|
||||
// note that a label is a location too
|
||||
|
|
@ -296,11 +339,11 @@ begin
|
|||
appf(p.s[cpsStmts], '$1: ;$n', [labl])
|
||||
end;
|
||||
|
||||
procedure genProcPrototype(m: BModule; sym: PSym); forward;
|
||||
procedure genVarPrototype(m: BModule; sym: PSym); forward;
|
||||
procedure genConstPrototype(m: BModule; sym: PSym); forward;
|
||||
procedure genProc(m: BModule; prc: PSym); forward;
|
||||
procedure genStmts(p: BProc; t: PNode); forward;
|
||||
procedure genProcPrototype(m: BModule; sym: PSym); forward;
|
||||
|
||||
{$include 'ccgexprs.pas'}
|
||||
{$include 'ccgstmts.pas'}
|
||||
|
|
@ -343,11 +386,11 @@ begin
|
|||
tmp := ropef('Dl_$1', [toRope(sym.id)]);
|
||||
sym.loc.r := tmp; // from now on we only need the internal name
|
||||
sym.typ.sym := nil; // generate a new name
|
||||
appf(m.s[cfsDynLibInit],
|
||||
'$1 = ($2) nimGetProcAddr($3, $4);$n',
|
||||
[tmp, getTypeDesc(m, sym.typ), lib.name,
|
||||
makeCString(ropeToStr(extname))]);
|
||||
declareGlobalVar(m, sym)
|
||||
appf(m.s[cfsDynLibInit], '$1 = ($2) nimGetProcAddr($3, $4);$n',
|
||||
[tmp, getTypeDesc(m, sym.typ), lib.name, makeCString(ropeToStr(extname))]);
|
||||
|
||||
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t));
|
||||
appf(m.s[cfsVars], ' $1;$n', [sym.loc.r]);
|
||||
end;
|
||||
|
||||
// ----------------------------- sections ---------------------------------
|
||||
|
|
@ -356,16 +399,16 @@ procedure UseMagic(m: BModule; const name: string);
|
|||
var
|
||||
sym: PSym;
|
||||
begin
|
||||
if (sfSystemModule in m.module.flags) then exit;
|
||||
// we don't know the magic symbols in the system module, but they will be
|
||||
// there anyway, because that is the way the code generator works
|
||||
sym := magicsys.getCompilerProc(name);
|
||||
case sym.kind of
|
||||
skProc, skConverter: genProcPrototype(m, sym);
|
||||
skVar: genVarPrototype(m, sym);
|
||||
skType: {@discard} getTypeDesc(m, sym.typ);
|
||||
else InternalError('useMagic: ' + name)
|
||||
end
|
||||
if sym <> nil then
|
||||
case sym.kind of
|
||||
skProc, skConverter: genProc(m, sym);
|
||||
skVar: genVarPrototype(m, sym);
|
||||
skType: {@discard} getTypeDesc(m, sym.typ);
|
||||
else InternalError('useMagic: ' + name)
|
||||
end
|
||||
else if not (sfSystemModule in m.module.flags) then
|
||||
rawMessage(errSystemNeeds, name); // don't be too picky here
|
||||
end;
|
||||
|
||||
procedure generateHeaders(m: BModule);
|
||||
|
|
@ -405,95 +448,131 @@ begin
|
|||
result := (s.typ.sons[0] <> nil) and not isInvalidReturnType(s.typ.sons[0])
|
||||
end;
|
||||
|
||||
procedure genProc(m: BModule; prc: PSym);
|
||||
procedure genProcAux(m: BModule; prc: PSym);
|
||||
var
|
||||
p: BProc;
|
||||
generatedProc, header, returnStmt: PRope;
|
||||
i, profileId: int;
|
||||
i: int;
|
||||
res, param: PSym;
|
||||
begin
|
||||
useHeader(m, prc);
|
||||
fillLoc(prc.loc, locProc, prc.typ, mangleName(prc), OnStack);
|
||||
if (lfNoDecl in prc.loc.Flags) then exit;
|
||||
if lfDynamicLib in prc.loc.flags then
|
||||
SymInDynamicLib(m, prc)
|
||||
else if not (sfImportc in prc.flags) then begin
|
||||
// we have a real proc here:
|
||||
p := newProc(prc, m);
|
||||
header := genProcHeader(m, prc);
|
||||
if (sfCompilerProc in prc.flags)
|
||||
and (sfSystemModule in m.module.flags)
|
||||
and not IntSetContains(m.declaredThings, prc.id) then
|
||||
appf(m.s[cfsProcHeaders], '$1;$n', [header]);
|
||||
intSetIncl(m.declaredThings, prc.id);
|
||||
returnStmt := nil;
|
||||
assert(prc.ast <> nil);
|
||||
p := newProc(prc, m);
|
||||
header := genProcHeader(m, prc);
|
||||
returnStmt := nil;
|
||||
assert(prc.ast <> nil);
|
||||
|
||||
if not (sfPure in prc.flags) and (prc.typ.sons[0] <> nil) then begin
|
||||
res := prc.ast.sons[resultPos].sym; // get result symbol
|
||||
if not isInvalidReturnType(prc.typ.sons[0]) then begin
|
||||
// declare the result symbol:
|
||||
assignLocalVar(p, res);
|
||||
assert(res.loc.r <> nil);
|
||||
returnStmt := ropef('return $1;$n', [rdLoc(res.loc)]);
|
||||
end
|
||||
else begin
|
||||
fillResult(res);
|
||||
assignParam(p, res);
|
||||
end;
|
||||
initVariable(p, res);
|
||||
genObjectInit(p, res.typ, res.loc, true);
|
||||
end;
|
||||
for i := 1 to sonsLen(prc.typ.n)-1 do begin
|
||||
param := prc.typ.n.sons[i].sym;
|
||||
assignParam(p, param)
|
||||
end;
|
||||
|
||||
genStmts(p, prc.ast.sons[codePos]); // modifies p.locals, p.init, etc.
|
||||
if sfPure in prc.flags then
|
||||
generatedProc := ropef('$1 {$n$2$3$4}$n',
|
||||
[header, p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]])
|
||||
if not (sfPure in prc.flags) and (prc.typ.sons[0] <> nil) then begin
|
||||
res := prc.ast.sons[resultPos].sym; // get result symbol
|
||||
if not isInvalidReturnType(prc.typ.sons[0]) then begin
|
||||
// declare the result symbol:
|
||||
assignLocalVar(p, res);
|
||||
assert(res.loc.r <> nil);
|
||||
returnStmt := ropef('return $1;$n', [rdLoc(res.loc)]);
|
||||
end
|
||||
else begin
|
||||
generatedProc := con(header, '{' + tnl);
|
||||
if optStackTrace in prc.options then begin
|
||||
getFrameDecl(p);
|
||||
prepend(p.s[cpsInit], ropef(
|
||||
'F.procname = $1;$n' +
|
||||
'F.prev = framePtr;$n' +
|
||||
'F.filename = $2;$n' +
|
||||
'F.line = 0;$n' +
|
||||
'framePtr = (TFrame*)&F;$n',
|
||||
[makeCString(prc.owner.name.s +{&} '.' +{&} prc.name.s),
|
||||
makeCString(toFilename(prc.info))]));
|
||||
end;
|
||||
if optProfiler in prc.options then begin
|
||||
if gProcProfile >= 64*1024 then // XXX: hard coded value!
|
||||
InternalError(prc.info, 'too many procedures for profiling');
|
||||
useMagic(m, 'profileData');
|
||||
app(p.s[cpsLocals], 'ticks NIM_profilingStart;'+tnl);
|
||||
if prc.loc.a < 0 then begin
|
||||
appf(m.s[cfsDebugInit], 'profileData[$1].procname = $2;$n',
|
||||
[toRope(gProcProfile),
|
||||
makeCString(prc.owner.name.s +{&} '.' +{&} prc.name.s)]);
|
||||
prc.loc.a := gProcProfile;
|
||||
inc(gProcProfile);
|
||||
end;
|
||||
prepend(p.s[cpsInit], toRope('NIM_profilingStart = getticks();' + tnl));
|
||||
end;
|
||||
app(generatedProc, con(p.s));
|
||||
if p.beforeRetNeeded then
|
||||
app(generatedProc, 'BeforeRet: ;' + tnl);
|
||||
if optStackTrace in prc.options then
|
||||
app(generatedProc, 'framePtr = framePtr->prev;' + tnl);
|
||||
if optProfiler in prc.options then
|
||||
appf(generatedProc,
|
||||
'profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n',
|
||||
[toRope(prc.loc.a)]);
|
||||
app(generatedProc, returnStmt);
|
||||
app(generatedProc, '}' + tnl);
|
||||
fillResult(res);
|
||||
assignParam(p, res);
|
||||
end;
|
||||
app(m.s[cfsProcs], generatedProc);
|
||||
initVariable(p, res);
|
||||
genObjectInit(p, res.typ, res.loc, true);
|
||||
end;
|
||||
for i := 1 to sonsLen(prc.typ.n)-1 do begin
|
||||
param := prc.typ.n.sons[i].sym;
|
||||
assignParam(p, param)
|
||||
end;
|
||||
|
||||
genStmts(p, prc.ast.sons[codePos]); // modifies p.locals, p.init, etc.
|
||||
if sfPure in prc.flags then
|
||||
generatedProc := ropef('$1 {$n$2$3$4}$n',
|
||||
[header, p.s[cpsLocals], p.s[cpsInit], p.s[cpsStmts]])
|
||||
else begin
|
||||
generatedProc := con(header, '{' + tnl);
|
||||
if optStackTrace in prc.options then begin
|
||||
getFrameDecl(p);
|
||||
prepend(p.s[cpsInit], ropef(
|
||||
'F.procname = $1;$n' +
|
||||
'F.prev = framePtr;$n' +
|
||||
'F.filename = $2;$n' +
|
||||
'F.line = 0;$n' +
|
||||
'framePtr = (TFrame*)&F;$n',
|
||||
[makeCString(prc.owner.name.s +{&} '.' +{&} prc.name.s),
|
||||
makeCString(toFilename(prc.info))]));
|
||||
end;
|
||||
if optProfiler in prc.options then begin
|
||||
if gProcProfile >= 64*1024 then // XXX: hard coded value!
|
||||
InternalError(prc.info, 'too many procedures for profiling');
|
||||
useMagic(m, 'profileData');
|
||||
app(p.s[cpsLocals], 'ticks NIM_profilingStart;'+tnl);
|
||||
if prc.loc.a < 0 then begin
|
||||
appf(m.s[cfsDebugInit], 'profileData[$1].procname = $2;$n',
|
||||
[toRope(gProcProfile),
|
||||
makeCString(prc.owner.name.s +{&} '.' +{&} prc.name.s)]);
|
||||
prc.loc.a := gProcProfile;
|
||||
inc(gProcProfile);
|
||||
end;
|
||||
prepend(p.s[cpsInit], toRope('NIM_profilingStart = getticks();' + tnl));
|
||||
end;
|
||||
app(generatedProc, con(p.s));
|
||||
if p.beforeRetNeeded then
|
||||
app(generatedProc, 'BeforeRet: ;' + tnl);
|
||||
if optStackTrace in prc.options then
|
||||
app(generatedProc, 'framePtr = framePtr->prev;' + tnl);
|
||||
if optProfiler in prc.options then
|
||||
appf(generatedProc,
|
||||
'profileData[$1].total += elapsed(getticks(), NIM_profilingStart);$n',
|
||||
[toRope(prc.loc.a)]);
|
||||
app(generatedProc, returnStmt);
|
||||
app(generatedProc, '}' + tnl);
|
||||
end;
|
||||
app(m.s[cfsProcs], generatedProc);
|
||||
end;
|
||||
|
||||
procedure genProcPrototype(m: BModule; sym: PSym);
|
||||
begin
|
||||
useHeader(m, sym);
|
||||
if (lfNoDecl in sym.loc.Flags) then exit;
|
||||
if lfDynamicLib in sym.loc.Flags then begin
|
||||
if (sym.owner.id <> m.module.id) and
|
||||
not intSetContainsOrIncl(m.declaredThings, sym.id) then begin
|
||||
appf(m.s[cfsVars], 'extern $1 Dl_$2;$n',
|
||||
[getTypeDesc(m, sym.loc.t), toRope(sym.id)])
|
||||
end
|
||||
end
|
||||
else begin
|
||||
if not IntSetContainsOrIncl(m.declaredProtos, sym.id) then
|
||||
appf(m.s[cfsProcHeaders], '$1;$n', [genProcHeader(m, sym)]);
|
||||
end
|
||||
end;
|
||||
|
||||
procedure genProcNoForward(m: BModule; prc: PSym);
|
||||
begin
|
||||
fillProcLoc(prc);
|
||||
useHeader(m, prc);
|
||||
genProcPrototype(m, prc);
|
||||
if (lfNoDecl in prc.loc.Flags) then exit;
|
||||
if prc.typ.callConv = ccInline then begin
|
||||
// We add inline procs to the calling module to enable C based inlining.
|
||||
// This also means that a check with ``gGeneratedSyms`` is wrong, we need
|
||||
// a check for ``m.declaredThings``.
|
||||
if not intSetContainsOrIncl(m.declaredThings, prc.id) then
|
||||
genProcAux(m, prc);
|
||||
end
|
||||
else if lfDynamicLib in prc.loc.flags then begin
|
||||
if not IntSetContainsOrIncl(gGeneratedSyms, prc.id) then
|
||||
SymInDynamicLib(findPendingModule(m, prc), prc);
|
||||
end
|
||||
else if not (sfImportc in prc.flags) then begin
|
||||
if not IntSetContainsOrIncl(gGeneratedSyms, prc.id) then
|
||||
genProcAux(findPendingModule(m, prc), prc);
|
||||
end
|
||||
end;
|
||||
|
||||
procedure genProc(m: BModule; prc: PSym);
|
||||
begin
|
||||
fillProcLoc(prc);
|
||||
if [sfForward, sfFromGeneric] * prc.flags <> [] then
|
||||
addForwardedProc(m, prc)
|
||||
else
|
||||
genProcNoForward(m, prc)
|
||||
end;
|
||||
|
||||
procedure genVarPrototype(m: BModule; sym: PSym);
|
||||
|
|
@ -522,7 +601,8 @@ end;
|
|||
procedure genConstPrototype(m: BModule; sym: PSym);
|
||||
begin
|
||||
useHeader(m, sym);
|
||||
fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnUnknown);
|
||||
if sym.loc.k = locNone then
|
||||
fillLoc(sym.loc, locData, sym.typ, mangleName(sym), OnUnknown);
|
||||
if (lfNoDecl in sym.loc.Flags) or
|
||||
intSetContainsOrIncl(m.declaredThings, sym.id) then
|
||||
exit;
|
||||
|
|
@ -535,32 +615,6 @@ begin
|
|||
end
|
||||
end;
|
||||
|
||||
procedure genProcPrototype(m: BModule; sym: PSym);
|
||||
begin
|
||||
useHeader(m, sym);
|
||||
fillLoc(sym.loc, locProc, sym.typ, mangleName(sym), OnStack);
|
||||
if lfDynamicLib in sym.loc.Flags then begin
|
||||
// it is a proc variable!
|
||||
if (sym.owner.id <> m.module.id) and
|
||||
not intSetContainsOrIncl(m.declaredThings, sym.id) then begin
|
||||
app(m.s[cfsVars], 'extern ');
|
||||
// BUGFIX: declareGlobalVar() inlined, because of intSetContainsOrIncl
|
||||
// check
|
||||
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t));
|
||||
appf(m.s[cfsVars], ' $1;$n', [sym.loc.r])
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// it is a proc:
|
||||
if (lfNoDecl in sym.loc.Flags) then exit;
|
||||
if intSetContainsOrIncl(m.declaredThings, sym.id) then exit;
|
||||
appf(m.s[cfsProcHeaders], '$1;$n', [genProcHeader(m, sym)]);
|
||||
if (sym.typ.callConv = ccInline)
|
||||
and (sym.owner.id <> m.module.id) then
|
||||
genProc(m, sym) // generate the code again!
|
||||
end
|
||||
end;
|
||||
|
||||
function getFileHeader(const cfilenoext: string): PRope;
|
||||
begin
|
||||
if optCompileOnly in gGlobalOptions then
|
||||
|
|
@ -593,6 +647,7 @@ procedure genMainProc(m: BModule);
|
|||
const
|
||||
CommonMainBody =
|
||||
' setStackBottom(dummy);$n' +
|
||||
' nim__datInit();$n' +
|
||||
' systemInit();$n' +
|
||||
'$1' +
|
||||
'$2';
|
||||
|
|
@ -652,8 +707,7 @@ var
|
|||
initname: PRope;
|
||||
begin
|
||||
initname := getInitName(m);
|
||||
appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n',
|
||||
[initname]);
|
||||
appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', [initname]);
|
||||
if not (sfSystemModule in m.flags) then
|
||||
appf(mainModInit, '$1();$n', [initname]);
|
||||
end;
|
||||
|
|
@ -669,14 +723,18 @@ begin
|
|||
{@discard} lists.IncludeStr(m.headerFiles, '<cycle.h>');
|
||||
end;
|
||||
initname := getInitName(m.module);
|
||||
registerModuleToMain(m.module);
|
||||
prc := ropef('N_NOINLINE(void, $1)(void) {$n', [initname]);
|
||||
if m.typeNodes > 0 then
|
||||
|
||||
if m.typeNodes > 0 then begin
|
||||
useMagic(m, 'TNimNode');
|
||||
appf(m.s[cfsTypeInit1], 'static TNimNode $1[$2];$n',
|
||||
[m.typeNodesName, toRope(m.typeNodes)]);
|
||||
if m.nimTypes > 0 then
|
||||
end;
|
||||
if m.nimTypes > 0 then begin
|
||||
useMagic(m, 'TNimType');
|
||||
appf(m.s[cfsTypeInit1], 'static TNimType $1[$2];$n',
|
||||
[m.nimTypesName, toRope(m.nimTypes)]);
|
||||
end;
|
||||
if optStackTrace in m.initProc.options then begin
|
||||
getFrameDecl(m.initProc);
|
||||
app(prc, m.initProc.s[cpsLocals]);
|
||||
|
|
@ -716,7 +774,7 @@ begin
|
|||
for i := low(TCFileSection) to cfsProcs do app(result, m.s[i])
|
||||
end;
|
||||
|
||||
function newModule(module: PSym; const filename: string): BModule;
|
||||
function rawNewModule(module: PSym; const filename: string): BModule;
|
||||
begin
|
||||
new(result);
|
||||
{@ignore}
|
||||
|
|
@ -724,7 +782,7 @@ begin
|
|||
{@emit}
|
||||
InitLinkedList(result.headerFiles);
|
||||
intSetInit(result.declaredThings);
|
||||
intSetInit(result.debugDeclared);
|
||||
intSetInit(result.declaredProtos);
|
||||
result.cfilename := filename;
|
||||
result.filename := filename;
|
||||
initIdTable(result.typeCache);
|
||||
|
|
@ -735,12 +793,36 @@ begin
|
|||
result.initProc.options := gOptions;
|
||||
initNodeTable(result.dataCache);
|
||||
{@emit result.typeStack := @[];}
|
||||
{@emit result.forwardedProcs := @[];}
|
||||
result.typeNodesName := getTempName();
|
||||
result.nimTypesName := getTempName();
|
||||
end;
|
||||
|
||||
function newModule(module: PSym; const filename: string): BModule;
|
||||
begin
|
||||
result := rawNewModule(module, filename);
|
||||
if (optDeadCodeElim in gGlobalOptions) then begin
|
||||
if (sfDeadCodeElim in module.flags) then
|
||||
InternalError('added pending module twice: ' + filename);
|
||||
addPendingModule(result)
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure registerTypeInfoModule();
|
||||
const
|
||||
moduleName = 'nim__dat';
|
||||
var
|
||||
s: PSym;
|
||||
begin
|
||||
s := NewSym(skModule, getIdent(moduleName), nil);
|
||||
gmti := rawNewModule(s, joinPath(options.projectPath, moduleName)+'.nim');
|
||||
addPendingModule(gmti);
|
||||
appf(mainModProcs, 'N_NOINLINE(void, $1)(void);$n', [getInitName(s)]);
|
||||
end;
|
||||
|
||||
function myOpen(module: PSym; const filename: string): PPassContext;
|
||||
begin
|
||||
if gmti = nil then registerTypeInfoModule();
|
||||
result := newModule(module, filename);
|
||||
end;
|
||||
|
||||
|
|
@ -749,6 +831,7 @@ function myOpenCached(module: PSym; const filename: string;
|
|||
var
|
||||
cfile, cfilenoext, objFile: string;
|
||||
begin
|
||||
if gmti = nil then registerTypeInfoModule();
|
||||
//MessageOut('cgen.myOpenCached has been called ' + filename);
|
||||
cfile := changeFileExt(completeCFilePath(filename), cExt);
|
||||
cfilenoext := changeFileExt(cfile, '');
|
||||
|
|
@ -761,6 +844,8 @@ begin
|
|||
end; *)
|
||||
addFileToLink(cfilenoext);
|
||||
registerModuleToMain(module);
|
||||
// XXX: this cannot be right here, initalization has to be appended during
|
||||
// the ``myClose`` call
|
||||
result := nil;
|
||||
end;
|
||||
|
||||
|
|
@ -790,11 +875,52 @@ begin
|
|||
genStmts(m.initProc, n);
|
||||
end;
|
||||
|
||||
function myClose(b: PPassContext; n: PNode): PNode;
|
||||
procedure finishModule(m: BModule);
|
||||
var
|
||||
i: int;
|
||||
prc: PSym;
|
||||
begin
|
||||
i := 0;
|
||||
while i <= high(m.forwardedProcs) do begin
|
||||
// Note: ``genProc`` may add to ``m.forwardedProcs``, so we cannot use
|
||||
// a for loop here
|
||||
prc := m.forwardedProcs[i];
|
||||
if sfForward in prc.flags then InternalError(prc.info, 'still forwarded');
|
||||
genProcNoForward(m, prc);
|
||||
inc(i);
|
||||
end;
|
||||
assert(gForwardedProcsCounter >= i);
|
||||
dec(gForwardedProcsCounter, i);
|
||||
setLength(m.forwardedProcs, 0);
|
||||
end;
|
||||
|
||||
procedure writeModule(m: BModule);
|
||||
var
|
||||
cfile, cfilenoext: string;
|
||||
m: BModule;
|
||||
code: PRope;
|
||||
begin
|
||||
// generate code for the init statements of the module:
|
||||
genInitCode(m);
|
||||
finishTypeDescriptions(m);
|
||||
|
||||
cfile := completeCFilePath(m.cfilename);
|
||||
cfilenoext := changeFileExt(cfile, '');
|
||||
if sfMainModule in m.module.flags then begin
|
||||
// generate main file:
|
||||
app(m.s[cfsProcHeaders], mainModProcs);
|
||||
end;
|
||||
code := genModule(m, cfilenoext);
|
||||
if shouldRecompile(code, changeFileExt(cfile, cExt), cfilenoext) then begin
|
||||
addFileToCompile(cfilenoext);
|
||||
end;
|
||||
addFileToLink(cfilenoext);
|
||||
if sfMainModule in m.module.flags then writeMapping(cfile, gMapping);
|
||||
end;
|
||||
|
||||
function myClose(b: PPassContext; n: PNode): PNode;
|
||||
var
|
||||
m: BModule;
|
||||
i: int;
|
||||
begin
|
||||
result := n;
|
||||
if b = nil then exit;
|
||||
|
|
@ -803,22 +929,24 @@ begin
|
|||
m.initProc.options := gOptions;
|
||||
genStmts(m.initProc, n);
|
||||
end;
|
||||
// generate code for the init statements of the module:
|
||||
genInitCode(m);
|
||||
finishTypeDescriptions(m);
|
||||
cfile := completeCFilePath(m.cfilename);
|
||||
cfilenoext := changeFileExt(cfile, '');
|
||||
registerModuleToMain(m.module);
|
||||
if not (optDeadCodeElim in gGlobalOptions) and
|
||||
not (sfDeadCodeElim in m.module.flags) then
|
||||
finishModule(m);
|
||||
if sfMainModule in m.module.flags then begin
|
||||
// generate main file:
|
||||
app(m.s[cfsProcHeaders], mainModProcs);
|
||||
genMainProc(m);
|
||||
// we need to process the transitive closure because recursive module
|
||||
// deps are allowed (and the system module is processed in the wrong
|
||||
// order anyway)
|
||||
while gForwardedProcsCounter > 0 do
|
||||
for i := 0 to high(gPendingModules) do
|
||||
finishModule(gPendingModules[i]);
|
||||
for i := 0 to high(gPendingModules) do writeModule(gPendingModules[i]);
|
||||
setLength(gPendingModules, 0);
|
||||
end;
|
||||
code := genModule(m, cfilenoext);
|
||||
if shouldRecompile(code, changeFileExt(cfile, cExt), cfilenoext) then begin
|
||||
addFileToCompile(cfilenoext); // is to compile
|
||||
end;
|
||||
addFileToLink(cfilenoext);
|
||||
if sfMainModule in m.module.flags then writeMapping(cfile, gMapping);
|
||||
if not (optDeadCodeElim in gGlobalOptions) and
|
||||
not (sfDeadCodeElim in m.module.flags) then
|
||||
writeModule(m);
|
||||
end;
|
||||
|
||||
function cgenPass(): TPass;
|
||||
|
|
@ -832,4 +960,5 @@ end;
|
|||
|
||||
initialization
|
||||
InitIiTable(gToTypeInfoId);
|
||||
IntSetInit(gGeneratedSyms);
|
||||
end.
|
||||
|
|
|
|||
|
|
@ -39,14 +39,14 @@ const
|
|||
{$ifdef fpc}
|
||||
compileDate = {$I %date%};
|
||||
{$else}
|
||||
compileDate = '2008-0-0';
|
||||
compileDate = '2009-0-0';
|
||||
{$endif}
|
||||
{@emit}
|
||||
|
||||
const
|
||||
HelpMessage = 'Nimrod Compiler Version $1 (' +{&}
|
||||
compileDate +{&} ') [$2: $3]' +{&} nl +{&}
|
||||
'Copyright (c) 2004-2008 by Andreas Rumpf' +{&} nl;
|
||||
'Copyright (c) 2004-2009 by Andreas Rumpf' +{&} nl;
|
||||
|
||||
const
|
||||
Usage = ''
|
||||
|
|
@ -82,6 +82,7 @@ const
|
|||
+{&} ' --bound_checks:on|off code generation for bound checks ON|OFF' +{&} nl
|
||||
+{&} ' --overflow_checks:on|off code generation for over-/underflow checks ON|OFF' +{&} nl
|
||||
+{&} ' -a, --assertions:on|off code generation for assertions ON|OFF' +{&} nl
|
||||
+{&} ' --dead_code_elim:on|off whole program dead code elimination ON|OFF' +{&} nl
|
||||
+{&} ' --opt:none|speed|size optimize not at all or for speed|size' +{&} nl
|
||||
+{&} ' --app:console|gui|lib generate a console|GUI application or a shared lib' +{&} nl
|
||||
+{&} ' -r, --run run the compiled program with given arguments' +{&} nl
|
||||
|
|
@ -96,7 +97,7 @@ const
|
|||
// cog.outl(f(line))
|
||||
//]]]
|
||||
+{&} 'Advanced commands::' +{&} nl
|
||||
+{&} ' pas convert a Pascal file to Nimrod standard syntax' +{&} nl
|
||||
+{&} ' pas convert a Pascal file to Nimrod syntax' +{&} nl
|
||||
+{&} ' pretty pretty print the inputfile' +{&} nl
|
||||
+{&} ' gen_depend generate a DOT file containing the' +{&} nl
|
||||
+{&} ' module dependency graph' +{&} nl
|
||||
|
|
@ -135,8 +136,8 @@ const
|
|||
|
||||
function getCommandLineDesc: string;
|
||||
begin
|
||||
result := format(HelpMessage, [VersionAsString, platform.os[hostOS].name,
|
||||
cpu[hostCPU].name]) +{&} Usage
|
||||
result := format(HelpMessage, [VersionAsString,
|
||||
platform.os[platform.hostOS].name, cpu[platform.hostCPU].name]) +{&} Usage
|
||||
end;
|
||||
|
||||
var
|
||||
|
|
@ -157,8 +158,10 @@ procedure writeAdvancedUsage(pass: TCmdLinePass);
|
|||
begin
|
||||
if (pass = passCmd1) and not advHelpWritten then begin
|
||||
// BUGFIX 19
|
||||
MessageOut(format(HelpMessage, [VersionAsString, platform.os[hostOS].name,
|
||||
cpu[hostCPU].name]) +{&} AdvancedUsage);
|
||||
MessageOut(format(HelpMessage, [VersionAsString,
|
||||
platform.os[platform.hostOS].name,
|
||||
cpu[platform.hostCPU].name]) +{&}
|
||||
AdvancedUsage);
|
||||
advHelpWritten := true;
|
||||
helpWritten := true;
|
||||
halt(0);
|
||||
|
|
@ -170,8 +173,9 @@ begin
|
|||
if (pass = passCmd1) and not versionWritten then begin
|
||||
versionWritten := true;
|
||||
helpWritten := true;
|
||||
messageOut(format(HelpMessage, [VersionAsString, platform.os[hostOS].name,
|
||||
cpu[hostCPU].name]))
|
||||
messageOut(format(HelpMessage, [VersionAsString,
|
||||
platform.os[platform.hostOS].name,
|
||||
cpu[platform.hostCPU].name]))
|
||||
end
|
||||
end;
|
||||
|
||||
|
|
@ -388,6 +392,7 @@ begin
|
|||
wOverflowChecks: ProcessOnOffSwitch({@set}[optOverflowCheck], arg, pass, info);
|
||||
wLineDir: ProcessOnOffSwitch({@set}[optLineDir], arg, pass, info);
|
||||
wAssertions, wA: ProcessOnOffSwitch({@set}[optAssert], arg, pass, info);
|
||||
wDeadCodeElim: ProcessOnOffSwitchG({@set}[optDeadCodeElim], arg, pass, info);
|
||||
wOpt: begin
|
||||
case whichKeyword(arg) of
|
||||
wSpeed: begin
|
||||
|
|
@ -453,7 +458,7 @@ begin
|
|||
theOS := platform.NameToOS(arg);
|
||||
if theOS = osNone then
|
||||
liMessage(info, errUnknownOS, arg);
|
||||
if theOS <> hostOS then begin
|
||||
if theOS <> platform.hostOS then begin
|
||||
setTarget(theOS, targetCPU);
|
||||
include(gGlobalOptions, optCompileOnly);
|
||||
condsyms.InitDefines()
|
||||
|
|
@ -465,7 +470,7 @@ begin
|
|||
cpu := platform.NameToCPU(arg);
|
||||
if cpu = cpuNone then
|
||||
liMessage(info, errUnknownCPU, arg);
|
||||
if cpu <> hostCPU then begin
|
||||
if cpu <> platform.hostCPU then begin
|
||||
setTarget(targetOS, cpu);
|
||||
include(gGlobalOptions, optCompileOnly);
|
||||
condsyms.InitDefines()
|
||||
|
|
|
|||
|
|
@ -218,8 +218,6 @@ begin
|
|||
end;
|
||||
|
||||
function nextSplitPoint(const s: string; start: int): int;
|
||||
var
|
||||
i: int;
|
||||
begin
|
||||
result := start;
|
||||
while result < length(s)+strStart do begin
|
||||
|
|
@ -393,7 +391,8 @@ begin
|
|||
fillChar(r, sizeof(r), 0);
|
||||
{@emit}
|
||||
comm := genRecComment(d, n); // call this here for the side-effect!
|
||||
initTokRender(r, n, {@set}[renderNoPragmas, renderNoBody]);
|
||||
initTokRender(r, n, {@set}[renderNoPragmas, renderNoBody, renderNoComments,
|
||||
renderDocComments]);
|
||||
while true do begin
|
||||
getNextTok(r, kind, literal);
|
||||
case kind of
|
||||
|
|
@ -579,7 +578,7 @@ begin
|
|||
end;
|
||||
rnHyperlink: begin
|
||||
result := ropef('`$1 <$2>`_', [renderRstToRst(d, n.sons[0]),
|
||||
renderRstToRst(d, n.sons[1])]);
|
||||
renderRstToRst(d, n.sons[1])]);
|
||||
end;
|
||||
rnGeneralRole: begin
|
||||
result := renderRstToRst(d, n.sons[0]);
|
||||
|
|
@ -661,21 +660,26 @@ begin
|
|||
result := ropef('<ul class="simple">$1</ul>', [result]);
|
||||
end;
|
||||
|
||||
function fieldAux(const s: string): PRope;
|
||||
begin
|
||||
result := toRope(strip(s))
|
||||
end;
|
||||
|
||||
function renderImage(d: PDoc; n: PRstNode): PRope;
|
||||
var
|
||||
s: string;
|
||||
begin
|
||||
result := ropef('<img src="$1"', [toRope(getArgument(n))]);
|
||||
s := getFieldValue(n, 'height');
|
||||
if s <> '' then appf(result, ' height="$1"', [toRope(s)]);
|
||||
if s <> '' then appf(result, ' height="$1"', [fieldAux(s)]);
|
||||
s := getFieldValue(n, 'width');
|
||||
if s <> '' then appf(result, ' width="$1"', [toRope(s)]);
|
||||
if s <> '' then appf(result, ' width="$1"', [fieldAux(s)]);
|
||||
s := getFieldValue(n, 'scale');
|
||||
if s <> '' then appf(result, ' scale="$1"', [toRope(s)]);
|
||||
if s <> '' then appf(result, ' scale="$1"', [fieldAux(s)]);
|
||||
s := getFieldValue(n, 'alt');
|
||||
if s <> '' then appf(result, ' alt="$1"', [toRope(s)]);
|
||||
if s <> '' then appf(result, ' alt="$1"', [fieldAux(s)]);
|
||||
s := getFieldValue(n, 'align');
|
||||
if s <> '' then appf(result, ' align="$1"', [toRope(s)]);
|
||||
if s <> '' then appf(result, ' align="$1"', [fieldAux(s)]);
|
||||
app(result, ' />');
|
||||
if rsonsLen(n) >= 3 then app(result, renderRstToHtml(d, n.sons[2]))
|
||||
end;
|
||||
|
|
@ -863,15 +867,18 @@ begin
|
|||
nkConverterDef: genItem(d, n, n.sons[namePos], skConverter);
|
||||
nkVarSection: begin
|
||||
for i := 0 to sonsLen(n)-1 do
|
||||
genItem(d, n.sons[i], n.sons[i].sons[0], skVar);
|
||||
if n.sons[i].kind <> nkCommentStmt then
|
||||
genItem(d, n.sons[i], n.sons[i].sons[0], skVar);
|
||||
end;
|
||||
nkConstSection: begin
|
||||
for i := 0 to sonsLen(n)-1 do
|
||||
genItem(d, n.sons[i], n.sons[i].sons[0], skConst);
|
||||
if n.sons[i].kind <> nkCommentStmt then
|
||||
genItem(d, n.sons[i], n.sons[i].sons[0], skConst);
|
||||
end;
|
||||
nkTypeSection: begin
|
||||
for i := 0 to sonsLen(n)-1 do
|
||||
genItem(d, n.sons[i], n.sons[i].sons[0], skType);
|
||||
if n.sons[i].kind <> nkCommentStmt then
|
||||
genItem(d, n.sons[i], n.sons[i].sons[0], skType);
|
||||
end;
|
||||
nkStmtList: begin
|
||||
for i := 0 to sonsLen(n)-1 do generateDoc(d, n.sons[i]);
|
||||
|
|
|
|||
|
|
@ -896,7 +896,8 @@ begin
|
|||
or (skipGeneric(y.typ).kind in [tyRef, tyPtr, tyVar])
|
||||
end;
|
||||
|
||||
procedure genAsgnAux(var p: TProc; x, y: PNode; var r: TCompRes);
|
||||
procedure genAsgnAux(var p: TProc; x, y: PNode; var r: TCompRes;
|
||||
noCopyNeeded: bool);
|
||||
var
|
||||
a, b: TCompRes;
|
||||
begin
|
||||
|
|
@ -906,7 +907,7 @@ begin
|
|||
etyObject: begin
|
||||
if a.com <> nil then appf(r.com, '$1;$n', [a.com]);
|
||||
if b.com <> nil then appf(r.com, '$1;$n', [b.com]);
|
||||
if needsNoCopy(y) then
|
||||
if needsNoCopy(y) or noCopyNeeded then
|
||||
appf(r.com, '$1 = $2;$n', [a.res, b.res])
|
||||
else begin
|
||||
useMagic(p, 'NimCopy');
|
||||
|
|
@ -930,7 +931,13 @@ end;
|
|||
procedure genAsgn(var p: TProc; n: PNode; var r: TCompRes);
|
||||
begin
|
||||
genLineDir(p, n, r);
|
||||
genAsgnAux(p, n.sons[0], n.sons[1], r);
|
||||
genAsgnAux(p, n.sons[0], n.sons[1], r, false);
|
||||
end;
|
||||
|
||||
procedure genFastAsgn(var p: TProc; n: PNode; var r: TCompRes);
|
||||
begin
|
||||
genLineDir(p, n, r);
|
||||
genAsgnAux(p, n.sons[0], n.sons[1], r, true);
|
||||
end;
|
||||
|
||||
procedure genSwap(var p: TProc; n: PNode; var r: TCompRes);
|
||||
|
|
@ -1692,6 +1699,7 @@ begin
|
|||
nkReturnStmt: genReturnStmt(p, n, r);
|
||||
nkBreakStmt: genBreakStmt(p, n, r);
|
||||
nkAsgn: genAsgn(p, n, r);
|
||||
nkFastAsgn: genFastAsgn(p, n, r);
|
||||
nkDiscardStmt: begin
|
||||
genLineDir(p, n, r);
|
||||
gen(p, n.sons[0], r);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ type
|
|||
TStackFrame = record
|
||||
mapping: TIdNodeTable; // mapping from symbols to nodes
|
||||
prc: PSym; // current prc; proc that is evaluated
|
||||
call: PNode; // current for stmt
|
||||
call: PNode;
|
||||
next: PStackFrame; // for stacking
|
||||
params: TNodeSeq; // parameters passed to the proc
|
||||
end;
|
||||
|
|
@ -127,7 +127,7 @@ begin
|
|||
inc(i)
|
||||
end;
|
||||
if (i < len) and (sonsLen(n.sons[i]) < 2) then // eval else-part
|
||||
result := evalAux(c, n.sons[0])
|
||||
result := evalAux(c, n.sons[i].sons[0])
|
||||
else
|
||||
result := emptyNode
|
||||
end;
|
||||
|
|
@ -1227,7 +1227,7 @@ begin
|
|||
nkDerefExpr, nkHiddenDeref: result := evalDeref(c, n);
|
||||
nkAddr, nkHiddenAddr: result := evalAddr(c, n);
|
||||
nkHiddenStdConv, nkHiddenSubConv, nkConv: result := evalConv(c, n);
|
||||
nkAsgn: result := evalAsgn(c, n);
|
||||
nkAsgn, nkFastAsgn: result := evalAsgn(c, n);
|
||||
nkWhenStmt, nkIfStmt, nkIfExpr: result := evalIf(c, n);
|
||||
nkWhileStmt: result := evalWhile(c, n);
|
||||
nkCaseStmt: result := evalCase(c, n);
|
||||
|
|
@ -1259,7 +1259,7 @@ begin
|
|||
nkTemplateDef, nkConstSection, nkIteratorDef, nkConverterDef,
|
||||
nkIncludeStmt, nkImportStmt, nkFromStmt: begin end;
|
||||
nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
|
||||
nkQualified, nkLambda, nkContinueStmt:
|
||||
nkQualified, nkLambda, nkContinueStmt, nkIdent:
|
||||
stackTrace(c, n, errCannotInterpretNodeX, nodeKindToStr[n.kind]);
|
||||
else InternalError(n.info, 'evalAux: ' + nodekindToStr[n.kind]);
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -343,17 +343,11 @@ begin
|
|||
result := ccNone
|
||||
end;
|
||||
|
||||
|
||||
procedure addStr(var dest: string; const src: string);
|
||||
begin
|
||||
dest := dest +{&} src;
|
||||
end;
|
||||
|
||||
procedure addOpt(var dest: string; const src: string);
|
||||
begin
|
||||
if (length(dest) = 0) or (dest[length(dest)-1+strStart] <> ' ') then
|
||||
addStr(dest, ' '+'');
|
||||
addStr(dest, src);
|
||||
add(dest, ' '+'');
|
||||
add(dest, src);
|
||||
end;
|
||||
|
||||
procedure addCompileOption(const option: string);
|
||||
|
|
@ -473,40 +467,52 @@ begin
|
|||
key := cc[c].name + '.exe';
|
||||
if existsConfigVar(key) then
|
||||
exe := getConfigVar(key);
|
||||
if targetOS = osWindows then exe := appendFileExt(exe, 'exe');
|
||||
|
||||
if (optGenDynLib in gGlobalOptions)
|
||||
and (ospNeedsPIC in platform.OS[targetOS].props) then
|
||||
addStr(options, ' ' + cc[c].pic);
|
||||
add(options, ' ' + cc[c].pic);
|
||||
|
||||
if targetOS = hostOS then begin
|
||||
if targetOS = platform.hostOS then begin
|
||||
// compute include paths:
|
||||
includeCmd := cc[c].includeCmd; // this is more complex than needed, but
|
||||
// a workaround of a FPC bug...
|
||||
addStr(includeCmd, libpath);
|
||||
compilePattern := quoteIfSpaceExists(JoinPath(ccompilerpath, exe));
|
||||
add(includeCmd, quoteIfContainsWhite(libpath));
|
||||
compilePattern := JoinPath(ccompilerpath, exe);
|
||||
end
|
||||
else begin
|
||||
includeCmd := '';
|
||||
compilePattern := cc[c].compilerExe
|
||||
end;
|
||||
if targetOS = hostOS then
|
||||
if targetOS = platform.hostOS then
|
||||
cfile := cfilename
|
||||
else
|
||||
cfile := extractFileName(cfilename);
|
||||
|
||||
if not isExternal or (targetOS <> hostOS) then
|
||||
if not isExternal or (targetOS <> platform.hostOS) then
|
||||
objfile := toObjFile(cfile)
|
||||
else
|
||||
objfile := completeCFilePath(toObjFile(cfile));
|
||||
cfile := quoteIfContainsWhite(AppendFileExt(cfile, cExt));
|
||||
objfile := quoteIfContainsWhite(objfile);
|
||||
|
||||
result := format(compilePattern +{&} ' ' +{&} cc[c].compileTmpl,
|
||||
['file', AppendFileExt(cfile, cExt),
|
||||
result := quoteIfContainsWhite(format(compilePattern,
|
||||
['file', cfile,
|
||||
'objfile', objfile,
|
||||
'options', options,
|
||||
'include', includeCmd,
|
||||
'nimrod', getPrefixDir(),
|
||||
'lib', libpath
|
||||
]);
|
||||
]));
|
||||
add(result, ' ');
|
||||
add(result, format(cc[c].compileTmpl,
|
||||
['file', cfile,
|
||||
'objfile', objfile,
|
||||
'options', options,
|
||||
'include', includeCmd,
|
||||
'nimrod', quoteIfContainsWhite(getPrefixDir()),
|
||||
'lib', quoteIfContainsWhite(libpath)
|
||||
]));
|
||||
end;
|
||||
|
||||
procedure CompileCFile(const list: TLinkedList;
|
||||
|
|
@ -551,11 +557,12 @@ begin
|
|||
// call the linker:
|
||||
linkerExe := getConfigVar(cc[c].name + '.linkerexe');
|
||||
if length(linkerExe) = 0 then linkerExe := cc[c].linkerExe;
|
||||
if targetOS = osWindows then linkerExe := appendFileExt(linkerExe, 'exe');
|
||||
|
||||
if (hostOS <> targetOS) then
|
||||
linkCmd := linkerExe
|
||||
if (platform.hostOS <> targetOS) then
|
||||
linkCmd := quoteIfContainsWhite(linkerExe)
|
||||
else
|
||||
linkCmd := quoteIfSpaceExists(JoinPath(ccompilerpath, linkerExe));
|
||||
linkCmd := quoteIfContainsWhite(JoinPath(ccompilerpath, linkerExe));
|
||||
|
||||
if optGenDynLib in gGlobalOptions then
|
||||
buildDll := cc[c].buildDll
|
||||
|
|
@ -570,27 +577,29 @@ begin
|
|||
exefile := platform.os[targetOS].dllPrefix
|
||||
else
|
||||
exefile := '';
|
||||
if targetOS = hostOS then
|
||||
addStr(exefile, projectFile)
|
||||
if targetOS = platform.hostOS then
|
||||
add(exefile, projectFile)
|
||||
else
|
||||
addStr(exefile, extractFileName(projectFile));
|
||||
add(exefile, extractFileName(projectFile));
|
||||
if optGenDynLib in gGlobalOptions then
|
||||
addStr(exefile, platform.os[targetOS].dllExt)
|
||||
add(exefile, platform.os[targetOS].dllExt)
|
||||
else
|
||||
addStr(exefile, platform.os[targetOS].exeExt);
|
||||
add(exefile, platform.os[targetOS].exeExt);
|
||||
exefile := quoteIfContainsWhite(exefile);
|
||||
|
||||
it := PStrEntry(toLink.head);
|
||||
objfiles := '';
|
||||
while it <> nil do begin
|
||||
addStr(objfiles, ' '+'');
|
||||
if targetOS = hostOS then
|
||||
addStr(objfiles, toObjfile(it.data))
|
||||
add(objfiles, ' '+'');
|
||||
if targetOS = platform.hostOS then
|
||||
add(objfiles, quoteIfContainsWhite(toObjfile(it.data)))
|
||||
else
|
||||
addStr(objfiles, toObjfile(extractFileName(it.data)));
|
||||
add(objfiles, quoteIfContainsWhite(
|
||||
toObjfile(extractFileName(it.data))));
|
||||
it := PStrEntry(it.next);
|
||||
end;
|
||||
|
||||
linkCmd := format(linkCmd +{&} ' ' +{&} cc[c].linkTmpl, [
|
||||
linkCmd := quoteIfContainsWhite(format(linkCmd, [
|
||||
'builddll', builddll,
|
||||
'buildgui', buildgui,
|
||||
'options', linkOptions,
|
||||
|
|
@ -598,7 +607,18 @@ begin
|
|||
'exefile', exefile,
|
||||
'nimrod', getPrefixDir(),
|
||||
'lib', libpath
|
||||
]);
|
||||
]));
|
||||
add(linkCmd, ' ');
|
||||
add(linkCmd, format(cc[c].linkTmpl, [
|
||||
'builddll', builddll,
|
||||
'buildgui', buildgui,
|
||||
'options', linkOptions,
|
||||
'objfiles', objfiles,
|
||||
'exefile', exefile,
|
||||
'nimrod', quoteIfContainsWhite(getPrefixDir()),
|
||||
'lib', quoteIfContainsWhite(libpath)
|
||||
]));
|
||||
|
||||
if not (optCompileOnly in gGlobalOptions) then
|
||||
execExternalProgram(linkCmd);
|
||||
end // end if not noLinking
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ begin
|
|||
result := x -{%} 1;
|
||||
// complicated, to make it a nop if sizeof(int) == 4,
|
||||
// because shifting more than 31 bits is undefined in C
|
||||
result := result or (result shr ((sizeof(int)-4)* 32));
|
||||
result := result or (result shr ((sizeof(int)-4)* 8));
|
||||
result := result or (result shr 16);
|
||||
result := result or (result shr 8);
|
||||
result := result or (result shr 4);
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ function getCurrentLine(const L: TBaseLexer; marker: boolean = true): string;
|
|||
function getColNumber(const L: TBaseLexer; pos: int): int;
|
||||
|
||||
function HandleCR(var L: TBaseLexer; pos: int): int;
|
||||
// Call this if you scanned over CR in the buffer; it returns the the
|
||||
// Call this if you scanned over CR in the buffer; it returns the
|
||||
// position to continue the scanning from. `pos` must be the position
|
||||
// of the CR.
|
||||
|
||||
|
|
@ -211,8 +211,7 @@ end;
|
|||
|
||||
function getColNumber(const L: TBaseLexer; pos: int): int;
|
||||
begin
|
||||
result := pos - L.lineStart;
|
||||
assert(result >= 0);
|
||||
result := abs(pos - L.lineStart);
|
||||
end;
|
||||
|
||||
function getCurrentLine(const L: TBaseLexer; marker: boolean = true): string;
|
||||
|
|
|
|||
|
|
@ -96,9 +96,10 @@ begin
|
|||
result := StrTableGet(compilerprocs, ident);
|
||||
if result = nil then begin
|
||||
result := StrTableGet(rodCompilerProcs, ident);
|
||||
if result = nil then rawMessage(errSystemNeeds, name);
|
||||
strTableAdd(compilerprocs, result);
|
||||
if result.kind = skStub then loadStub(result);
|
||||
if result <> nil then begin
|
||||
strTableAdd(compilerprocs, result);
|
||||
if result.kind = skStub then loadStub(result);
|
||||
end;
|
||||
// A bit hacky that this code is needed here, but it is the easiest
|
||||
// solution in order to avoid special cases for sfCompilerProc in the
|
||||
// rodgen module. Another solution would be to always recompile the system
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ type
|
|||
errSelectorMustBeOrdinal,
|
||||
errOrdXMustNotBeNegative,
|
||||
errLenXinvalid,
|
||||
errWrongNumberOfLoopVariables,
|
||||
errWrongNumberOfVariables,
|
||||
errExprCannotBeRaised,
|
||||
errBreakOnlyInLoop,
|
||||
errTypeXhasUnknownSize,
|
||||
|
|
@ -431,7 +431,7 @@ const
|
|||
'selector must be of an ordinal type',
|
||||
'ord($1) must not be negative',
|
||||
'len($1) must be less than 32768',
|
||||
'wrong number of loop variables',
|
||||
'wrong number of variables',
|
||||
'only objects can be raised',
|
||||
'''break'' only allowed in loop construct',
|
||||
'type ''$1'' has unknown size',
|
||||
|
|
|
|||
|
|
@ -93,7 +93,8 @@ begin
|
|||
}
|
||||
end;
|
||||
if optRun in gGlobalOptions then
|
||||
execExternalProgram(changeFileExt(filename, '') +{&} ' ' +{&} arguments)
|
||||
execExternalProgram(quoteIfContainsWhite(changeFileExt(filename, '')) +{&}
|
||||
' ' +{&} arguments)
|
||||
end
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,9 +35,11 @@ const
|
|||
{$ifdef mswindows}
|
||||
dirsep = '\'; // seperator within paths
|
||||
altsep = '/';
|
||||
exeExt = 'exe';
|
||||
{$else}
|
||||
dirsep = '/';
|
||||
altsep = #0; // work around fpc bug
|
||||
exeExt = '';
|
||||
{$endif}
|
||||
pathSep = ';'; // seperator between paths
|
||||
sep = dirsep; // alternative name
|
||||
|
|
|
|||
|
|
@ -149,8 +149,12 @@ function subU(a, b: biggestInt): biggestInt;
|
|||
function mulU(a, b: biggestInt): biggestInt;
|
||||
function divU(a, b: biggestInt): biggestInt;
|
||||
function modU(a, b: biggestInt): biggestInt;
|
||||
function shlU(a, b: biggestInt): biggestInt;
|
||||
function shrU(a, b: biggestInt): biggestInt;
|
||||
function shlU(a, b: biggestInt): biggestInt; overload;
|
||||
function shrU(a, b: biggestInt): biggestInt; overload;
|
||||
|
||||
function shlU(a, b: Int32): Int32;overload;
|
||||
function shrU(a, b: int32): int32;overload;
|
||||
|
||||
function ltU(a, b: biggestInt): bool;
|
||||
function leU(a, b: biggestInt): bool;
|
||||
|
||||
|
|
@ -281,6 +285,16 @@ begin
|
|||
result := biggestInt(biggestUInt(a) shr biggestUInt(b));
|
||||
end;
|
||||
|
||||
function shlU(a, b: Int32): Int32;
|
||||
begin
|
||||
result := Int32(UInt32(a) shl UInt32(b));
|
||||
end;
|
||||
|
||||
function shrU(a, b: int32): int32;
|
||||
begin
|
||||
result := Int32(UInt32(a) shr UInt32(b));
|
||||
end;
|
||||
|
||||
function ltU(a, b: biggestInt): bool;
|
||||
begin
|
||||
result := biggestUInt(a) < biggestUInt(b);
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ const
|
|||
//cog.outl('VersionMinor = %s;' % ver[1])
|
||||
//cog.outl('VersionPatch = %s;' % ver[2])
|
||||
//]]]
|
||||
VersionAsString = '0.7.2';
|
||||
VersionAsString = '0.7.4';
|
||||
VersionMajor = 0;
|
||||
VersionMinor = 7;
|
||||
VersionPatch = 2;
|
||||
VersionPatch = 4;
|
||||
//[[[[end]]]]
|
||||
|
||||
implementation
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
@ -201,6 +201,12 @@ begin
|
|||
if startsWith(dir, prefix) then begin
|
||||
result := ncopy(dir, length(prefix) + strStart); exit
|
||||
end;
|
||||
prefix := projectPath +{&} dirSep;
|
||||
//writeln(output, prefix);
|
||||
//writeln(output, dir);
|
||||
if startsWith(dir, prefix) then begin
|
||||
result := ncopy(dir, length(prefix) + strStart); exit
|
||||
end;
|
||||
result := dir
|
||||
end;
|
||||
|
||||
|
|
@ -209,7 +215,7 @@ var
|
|||
head, tail: string;
|
||||
begin
|
||||
splitPath(path, head, tail);
|
||||
result := joinPath([projectPath, genSubDir, shortenDir(head),
|
||||
result := joinPath([projectPath, genSubDir, shortenDir(head +{&} dirSep),
|
||||
changeFileExt(tail, ext)])
|
||||
end;
|
||||
|
||||
|
|
@ -219,9 +225,11 @@ var
|
|||
head, tail, subdir: string;
|
||||
begin
|
||||
splitPath(f, head, tail);
|
||||
subdir := joinPath([projectPath, genSubDir, shortenDir(head)]);
|
||||
if createSubDir then
|
||||
subdir := joinPath([projectPath, genSubDir, shortenDir(head +{&} dirSep)]);
|
||||
if createSubDir then begin
|
||||
//Writeln(output, subdir);
|
||||
createDir(subdir);
|
||||
end;
|
||||
result := joinPath(subdir, tail)
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ begin
|
|||
else begin
|
||||
result.cmd := '';
|
||||
for i := 1 to ParamCount() do
|
||||
result.cmd := result.cmd +{&} quoteIfSpaceExists(paramStr(i)) +{&} ' ';
|
||||
result.cmd := result.cmd +{&} quoteIfContainsWhite(paramStr(i)) +{&} ' ';
|
||||
{@ignore}
|
||||
result.cmd := result.cmd + #0;
|
||||
{@emit}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ unit passaux;
|
|||
interface
|
||||
|
||||
uses
|
||||
nsystem, strutils, ast, passes, msgs, options;
|
||||
nsystem, strutils, ast, astalgo, passes, msgs, options;
|
||||
|
||||
function verbosePass: TPass;
|
||||
function cleanupPass: TPass;
|
||||
|
|
@ -50,6 +50,8 @@ var
|
|||
s: PSym;
|
||||
begin
|
||||
result := n;
|
||||
// we cannot clean up if dead code elimination is activated
|
||||
if (optDeadCodeElim in gGlobalOptions) then exit;
|
||||
case n.kind of
|
||||
nkStmtList: begin
|
||||
for i := 0 to sonsLen(n)-1 do {@discard} cleanup(c, n.sons[i]);
|
||||
|
|
@ -57,7 +59,8 @@ begin
|
|||
nkProcDef: begin
|
||||
if (n.sons[namePos].kind = nkSym) then begin
|
||||
s := n.sons[namePos].sym;
|
||||
if not astNeeded(s) then s.ast.sons[codePos] := nil; // free the memory
|
||||
if not (sfDeadCodeElim in getModule(s).flags) and
|
||||
not astNeeded(s) then s.ast.sons[codePos] := nil; // free the memory
|
||||
end
|
||||
end
|
||||
else begin end;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ uses
|
|||
|
||||
type
|
||||
TSystemOS = (
|
||||
// Also add OS for in initialization section and alias conditionals to
|
||||
// Also add OS in initialization section and alias conditionals to
|
||||
// condsyms (end of module).
|
||||
osNone,
|
||||
osDos,
|
||||
|
|
|
|||
351
nim/pnimsyn.pas
351
nim/pnimsyn.pas
|
|
@ -9,7 +9,7 @@
|
|||
unit pnimsyn;
|
||||
|
||||
// This module implements the parser of the standard Nimrod representation.
|
||||
// The parser is strictly reflects the grammar ("doc/grammar.txt"); however
|
||||
// The parser strictly reflects the grammar ("doc/grammar.txt"); however
|
||||
// it uses several helper routines to keep the parser small. A special
|
||||
// efficient algorithm is used for the precedence levels. The parser here can
|
||||
// be seen as a refinement of the grammar, as it specifies how the AST is build
|
||||
|
|
@ -98,15 +98,21 @@ end;
|
|||
|
||||
// ---------------- parser helpers --------------------------------------------
|
||||
|
||||
procedure parMessage(const p: TParser; const msg: TMsgKind;
|
||||
const arg: string = '');
|
||||
begin
|
||||
lexMessage(p.lex^, msg, arg);
|
||||
end;
|
||||
|
||||
procedure skipComment(var p: TParser; node: PNode);
|
||||
begin
|
||||
if p.tok.tokType = tkComment then begin
|
||||
if node <> nil then begin
|
||||
if node.comment = snil then node.comment := '';
|
||||
node.comment := node.comment +{&} p.tok.literal;
|
||||
add(node.comment, p.tok.literal);
|
||||
end
|
||||
else
|
||||
assert(false);
|
||||
parMessage(p, errInternal, 'skipComment');
|
||||
getTok(p);
|
||||
end
|
||||
end;
|
||||
|
|
@ -116,6 +122,11 @@ begin
|
|||
if p.tok.tokType = tkInd then getTok(p)
|
||||
end;
|
||||
|
||||
procedure optSad(var p: TParser);
|
||||
begin
|
||||
if p.tok.tokType = tkSad then getTok(p)
|
||||
end;
|
||||
|
||||
procedure optInd(var p: TParser; n: PNode);
|
||||
begin
|
||||
skipComment(p, n);
|
||||
|
|
@ -146,12 +157,6 @@ begin
|
|||
else lexMessage(p.lex^, errTokenExpected, TokTypeToStr[tokType])
|
||||
end;
|
||||
|
||||
procedure parMessage(const p: TParser; const msg: TMsgKind;
|
||||
const arg: string = '');
|
||||
begin
|
||||
lexMessage(p.lex^, msg, arg);
|
||||
end;
|
||||
|
||||
function parLineInfo(const p: TParser): TLineInfo;
|
||||
begin
|
||||
result := getLineInfo(p.lex^)
|
||||
|
|
@ -385,7 +390,7 @@ begin
|
|||
result := newNodeP(nkExprEqExpr, p);
|
||||
addSon(result, a);
|
||||
getTok(p);
|
||||
optInd(p, result);
|
||||
//optInd(p, result);
|
||||
case p.tok.tokType of
|
||||
tkVar, tkRef, tkPtr, tkProc:
|
||||
addSon(result, parseTypeDescK(p));
|
||||
|
|
@ -413,21 +418,16 @@ begin
|
|||
addSon(result, first);
|
||||
getTok(p);
|
||||
optInd(p, result);
|
||||
while true do begin
|
||||
if p.tok.tokType = tkBracketRi then begin
|
||||
getTok(p); break
|
||||
end;
|
||||
if p.tok.tokType = tkEof then begin
|
||||
parMessage(p, errTokenExpected, TokTypeToStr[tkBracketRi]); break
|
||||
end;
|
||||
while (p.tok.tokType <> tkBracketRi) and (p.tok.tokType <> tkEof)
|
||||
and (p.tok.tokType <> tkSad) do begin
|
||||
a := namedTypeOrExpr(p);
|
||||
optInd(p, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
addSon(result, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
optSad(p);
|
||||
eat(p, tkBracketRi);
|
||||
end;
|
||||
|
||||
function exprColonEqExpr(var p: TParser; kind: TNodeKind;
|
||||
|
|
@ -439,7 +439,7 @@ begin
|
|||
if p.tok.tokType = tok then begin
|
||||
result := newNodeP(kind, p);
|
||||
getTok(p);
|
||||
optInd(p, result);
|
||||
//optInd(p, result);
|
||||
addSon(result, a);
|
||||
addSon(result, parseExpr(p));
|
||||
end
|
||||
|
|
@ -454,21 +454,14 @@ var
|
|||
begin
|
||||
getTok(p);
|
||||
optInd(p, result);
|
||||
while true do begin
|
||||
if p.tok.tokType = endTok then begin
|
||||
getTok(p); break
|
||||
end;
|
||||
if p.tok.tokType = tkEof then begin
|
||||
parMessage(p, errTokenExpected, TokTypeToStr[endtok]); break
|
||||
end;
|
||||
while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof) do begin
|
||||
a := exprColonEqExpr(p, elemKind, sepTok);
|
||||
optInd(p, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
addSon(result, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
eat(p, endTok);
|
||||
end;
|
||||
|
||||
function qualifiedIdent(var p: TParser): PNode;
|
||||
|
|
@ -476,7 +469,7 @@ var
|
|||
a: PNode;
|
||||
begin
|
||||
result := parseSymbol(p);
|
||||
optInd(p, result);
|
||||
//optInd(p, result);
|
||||
if p.tok.tokType = tkDot then begin
|
||||
getTok(p);
|
||||
optInd(p, result);
|
||||
|
|
@ -494,28 +487,41 @@ var
|
|||
begin
|
||||
getTok(p);
|
||||
optInd(p, result);
|
||||
while true do begin
|
||||
if p.tok.tokType = endTok then begin
|
||||
getTok(p); break
|
||||
end;
|
||||
if p.tok.tokType = tkEof then begin
|
||||
parMessage(p, errTokenExpected, TokTypeToStr[endtok]); break
|
||||
end;
|
||||
while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof) do begin
|
||||
a := qualifiedIdent(p);
|
||||
optInd(p, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
addSon(result, a);
|
||||
//optInd(p, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
eat(p, endTok);
|
||||
end;
|
||||
|
||||
procedure exprColonEqExprListAux(var p: TParser; elemKind: TNodeKind;
|
||||
endTok, sepTok: TTokType; result: PNode);
|
||||
var
|
||||
a: PNode;
|
||||
begin
|
||||
getTok(p);
|
||||
optInd(p, result);
|
||||
while (p.tok.tokType <> endTok) and (p.tok.tokType <> tkEof)
|
||||
and (p.tok.tokType <> tkSad) do begin
|
||||
a := exprColonEqExpr(p, elemKind, sepTok);
|
||||
addSon(result, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
optSad(p);
|
||||
eat(p, endTok);
|
||||
end;
|
||||
|
||||
function exprColonEqExprList(var p: TParser; kind, elemKind: TNodeKind;
|
||||
endTok, sepTok: TTokType): PNode;
|
||||
begin
|
||||
result := newNodeP(kind, p);
|
||||
exprListAux(p, elemKind, endTok, sepTok, result);
|
||||
exprColonEqExprListAux(p, elemKind, endTok, sepTok, result);
|
||||
end;
|
||||
|
||||
function parseCast(var p: TParser): PNode;
|
||||
|
|
@ -525,12 +531,12 @@ begin
|
|||
eat(p, tkBracketLe);
|
||||
optInd(p, result);
|
||||
addSon(result, parseTypeDesc(p));
|
||||
optInd(p, result);
|
||||
optSad(p);
|
||||
eat(p, tkBracketRi);
|
||||
eat(p, tkParLe);
|
||||
optInd(p, result);
|
||||
addSon(result, parseExpr(p));
|
||||
optInd(p, result);
|
||||
optSad(p);
|
||||
eat(p, tkParRi);
|
||||
end;
|
||||
|
||||
|
|
@ -541,7 +547,7 @@ begin
|
|||
eat(p, tkParLe);
|
||||
optInd(p, result);
|
||||
addSon(result, parseExpr(p));
|
||||
optInd(p, result);
|
||||
optSad(p);
|
||||
eat(p, tkParRi);
|
||||
end;
|
||||
|
||||
|
|
@ -667,7 +673,7 @@ begin
|
|||
a := result;
|
||||
result := newNodeP(nkCall, p);
|
||||
addSon(result, a);
|
||||
exprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result);
|
||||
exprColonEqExprListAux(p, nkExprEqExpr, tkParRi, tkEquals, result);
|
||||
end;
|
||||
tkDot: begin
|
||||
a := result;
|
||||
|
|
@ -709,8 +715,9 @@ begin
|
|||
opNode := newIdentNodeP(op.ident, p);
|
||||
// skip operator:
|
||||
getTok(p);
|
||||
skipComment(p, opNode);
|
||||
skipInd(p);
|
||||
//skipComment(p, opNode);
|
||||
//skipInd(p);
|
||||
optInd(p, opNode);
|
||||
|
||||
// read sub-expression with higher priority
|
||||
nextop := lowestExprAux(p, v2, opPred);
|
||||
|
|
@ -739,12 +746,12 @@ begin
|
|||
while true do begin
|
||||
getTok(p); // skip `if`, `elif`
|
||||
branch := newNodeP(nkElifExpr, p);
|
||||
optInd(p, branch);
|
||||
//optInd(p, branch);
|
||||
addSon(branch, parseExpr(p));
|
||||
eat(p, tkColon);
|
||||
optInd(p, branch);
|
||||
//optInd(p, branch);
|
||||
addSon(branch, parseExpr(p));
|
||||
optInd(p, branch);
|
||||
//optInd(p, branch);
|
||||
addSon(result, branch);
|
||||
if p.tok.tokType <> tkElif then break
|
||||
end;
|
||||
|
|
@ -770,31 +777,33 @@ var
|
|||
begin
|
||||
result := newNodeP(nkPragma, p);
|
||||
getTok(p);
|
||||
while true do begin
|
||||
skipComment(p, result);
|
||||
skipInd(p);
|
||||
case p.tok.TokType of
|
||||
tkCurlyDotRi, tkCurlyRi: begin
|
||||
getTok(p); // skip } or .}
|
||||
break
|
||||
end;
|
||||
tkEof: begin
|
||||
parMessage(p, errTokenExpected, '.}');
|
||||
break
|
||||
end
|
||||
else begin
|
||||
a := exprColonEqExpr(p, nkExprColonExpr, tkColon);
|
||||
addSon(result, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p);
|
||||
skipComment(p, a)
|
||||
end
|
||||
end
|
||||
optInd(p, result);
|
||||
while (p.tok.tokType <> tkCurlyDotRi) and (p.tok.tokType <> tkCurlyRi)
|
||||
and (p.tok.tokType <> tkEof) and (p.tok.tokType <> tkSad) do begin
|
||||
a := exprColonEqExpr(p, nkExprColonExpr, tkColon);
|
||||
addSon(result, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end
|
||||
end
|
||||
end;
|
||||
optSad(p);
|
||||
if (p.tok.tokType = tkCurlyDotRi) or (p.tok.tokType = tkCurlyRi) then
|
||||
getTok(p)
|
||||
else
|
||||
parMessage(p, errTokenExpected, '.}');
|
||||
end;
|
||||
|
||||
// ---------------------- statement parser ------------------------------------
|
||||
function isExprStart(const p: TParser): bool;
|
||||
begin
|
||||
case p.tok.tokType of
|
||||
tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkLambda,
|
||||
tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit: result := true;
|
||||
else result := false;
|
||||
end;
|
||||
end;
|
||||
|
||||
function parseExprStmt(var p: TParser): PNode;
|
||||
var
|
||||
a, b, e: PNode;
|
||||
|
|
@ -813,16 +822,16 @@ begin
|
|||
result.info := a.info;
|
||||
addSon(result, a);
|
||||
while true do begin
|
||||
case p.tok.tokType of
|
||||
(*case p.tok.tokType of
|
||||
tkColon, tkInd, tkSad, tkDed, tkEof, tkComment: break;
|
||||
else begin end
|
||||
end;
|
||||
end;*)
|
||||
if not isExprStart(p) then break;
|
||||
e := parseExpr(p);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p);
|
||||
skipComment(p, e)
|
||||
end;
|
||||
addSon(result, e);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a);
|
||||
end;
|
||||
if sonsLen(result) <= 1 then result := a
|
||||
else a := result;
|
||||
|
|
@ -897,7 +906,7 @@ begin
|
|||
break
|
||||
end;
|
||||
end;
|
||||
optInd(p, a);
|
||||
//optInd(p, a);
|
||||
if p.tok.tokType = tkAs then begin
|
||||
getTok(p);
|
||||
optInd(p, a);
|
||||
|
|
@ -906,11 +915,10 @@ begin
|
|||
addSon(a, b);
|
||||
addSon(a, parseSymbol(p));
|
||||
end;
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
addSon(result, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
@ -942,12 +950,11 @@ begin
|
|||
break
|
||||
end;
|
||||
end;
|
||||
optInd(p, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
addSon(result, a);
|
||||
//optInd(p, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
@ -977,7 +984,7 @@ begin
|
|||
end
|
||||
end;
|
||||
addSon(result, a);
|
||||
optInd(p, a);
|
||||
//optInd(p, a);
|
||||
eat(p, tkImport);
|
||||
optInd(p, result);
|
||||
while true do begin
|
||||
|
|
@ -989,12 +996,11 @@ begin
|
|||
break
|
||||
end;
|
||||
end;
|
||||
optInd(p, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
//optInd(p, a);
|
||||
addSon(result, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
@ -1074,6 +1080,7 @@ begin
|
|||
result := newNodeP(nkCaseStmt, p);
|
||||
getTok(p);
|
||||
addSon(result, parseExpr(p));
|
||||
if p.tok.tokType = tkColon then getTok(p);
|
||||
skipComment(p, result);
|
||||
inElif := false;
|
||||
while true do begin
|
||||
|
|
@ -1145,22 +1152,15 @@ begin
|
|||
result := newNodeP(nkForStmt, p);
|
||||
getTok(p);
|
||||
optInd(p, result);
|
||||
while true do begin
|
||||
if p.tok.tokType = tkIn then begin
|
||||
getTok(p); break
|
||||
end;
|
||||
if p.tok.tokType = tkEof then begin
|
||||
parMessage(p, errTokenExpected, TokTypeToStr[tkIn]); break
|
||||
end;
|
||||
|
||||
a := parseSymbol(p);
|
||||
if a = nil then break;
|
||||
a := parseSymbol(p);
|
||||
addSon(result, a);
|
||||
while p.tok.tokType = tkComma do begin
|
||||
getTok(p);
|
||||
optInd(p, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p); optInd(p, a)
|
||||
end;
|
||||
a := parseSymbol(p);
|
||||
addSon(result, a);
|
||||
end;
|
||||
eat(p, tkIn);
|
||||
addSon(result, exprColonEqExpr(p, nkRange, tkDotDot));
|
||||
eat(p, tkColon);
|
||||
skipComment(p, result);
|
||||
|
|
@ -1246,12 +1246,11 @@ begin
|
|||
if a = nil then exit;
|
||||
end
|
||||
end;
|
||||
optInd(p, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
addSon(result, a);
|
||||
//optInd(p, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
if p.tok.tokType = tkColon then begin
|
||||
getTok(p); optInd(p, result);
|
||||
|
|
@ -1282,15 +1281,17 @@ begin
|
|||
while true do begin
|
||||
case p.tok.tokType of
|
||||
tkSymbol, tkAccent: a := parseIdentColonEquals(p, false);
|
||||
tkParRi: begin getTok(p); break end;
|
||||
tkParRi: break;
|
||||
else begin parMessage(p, errTokenExpected, ')'+''); break; end;
|
||||
end;
|
||||
optInd(p, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p); optInd(p, a)
|
||||
end;
|
||||
//optInd(p, a);
|
||||
addSon(result, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
optSad(p);
|
||||
eat(p, tkParRi);
|
||||
end;
|
||||
if p.tok.tokType = tkColon then begin
|
||||
getTok(p);
|
||||
|
|
@ -1336,18 +1337,15 @@ begin
|
|||
getTok(p);
|
||||
eat(p, tkBracketLe);
|
||||
optInd(p, result);
|
||||
while true do begin
|
||||
case p.tok.tokType of
|
||||
tkSymbol, tkAccent: a := parseIdentColonEquals(p, false);
|
||||
tkBracketRi: begin getTok(p); break end;
|
||||
else begin parMessage(p, errTokenExpected, ']'+''); break; end;
|
||||
end;
|
||||
optInd(p, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p); optInd(p, a)
|
||||
end;
|
||||
while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
|
||||
a := parseIdentColonEquals(p, false);
|
||||
addSon(result, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
optSad(p);
|
||||
eat(p, tkBracketRi);
|
||||
end;
|
||||
else begin
|
||||
InternalError(parLineInfo(p), 'pnimsyn.parseTypeDescK');
|
||||
|
|
@ -1389,18 +1387,15 @@ begin
|
|||
result := newNodeP(nkGenericParams, p);
|
||||
getTok(p);
|
||||
optInd(p, result);
|
||||
while true do begin
|
||||
case p.tok.tokType of
|
||||
tkSymbol, tkAccent: a := parseGenericParam(p);
|
||||
tkBracketRi: begin getTok(p); break end;
|
||||
else begin parMessage(p, errTokenExpected, ']'+''); break; end;
|
||||
end;
|
||||
optInd(p, a);
|
||||
if p.tok.tokType = tkComma then begin
|
||||
getTok(p); optInd(p, a)
|
||||
end;
|
||||
while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
|
||||
a := parseGenericParam(p);
|
||||
addSon(result, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
optSad(p);
|
||||
eat(p, tkBracketRi);
|
||||
end;
|
||||
|
||||
function parseRoutine(var p: TParser; kind: TNodeKind): PNode;
|
||||
|
|
@ -1484,7 +1479,8 @@ begin
|
|||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end;
|
||||
popInd(p.lex^);
|
||||
end;
|
||||
tkSymbol, tkAccent: addSon(result, defparser(p));
|
||||
else parMessage(p, errIdentifierExpected, tokToStr(p.tok));
|
||||
|
|
@ -1628,7 +1624,8 @@ begin
|
|||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end;
|
||||
popInd(p.lex^);
|
||||
end;
|
||||
tkWhen: result := parseRecordWhen(p);
|
||||
tkCase: result := parseRecordCase(p);
|
||||
|
|
@ -1688,9 +1685,34 @@ begin
|
|||
indAndComment(p, result); // special extension!
|
||||
end;
|
||||
|
||||
function parseVarTuple(var p: TParser): PNode;
|
||||
var
|
||||
a: PNode;
|
||||
begin
|
||||
result := newNodeP(nkVarTuple, p);
|
||||
getTok(p); // skip '('
|
||||
optInd(p, result);
|
||||
while (p.tok.tokType = tkSymbol) or (p.tok.tokType = tkAccent) do begin
|
||||
a := identWithPragma(p);
|
||||
addSon(result, a);
|
||||
if p.tok.tokType <> tkComma then break;
|
||||
getTok(p);
|
||||
optInd(p, a)
|
||||
end;
|
||||
addSon(result, nil); // no type desc
|
||||
optSad(p);
|
||||
eat(p, tkParRi);
|
||||
eat(p, tkEquals);
|
||||
optInd(p, result);
|
||||
addSon(result, parseExpr(p));
|
||||
end;
|
||||
|
||||
function parseVariable(var p: TParser): PNode;
|
||||
begin
|
||||
result := parseIdentColonEquals(p, true);
|
||||
if p.tok.tokType = tkParLe then
|
||||
result := parseVarTuple(p)
|
||||
else
|
||||
result := parseIdentColonEquals(p, true);
|
||||
indAndComment(p, result); // special extension!
|
||||
end;
|
||||
|
||||
|
|
@ -1708,10 +1730,15 @@ begin
|
|||
tkFrom: result := parseFromStmt(p);
|
||||
tkInclude: result := parseIncludeStmt(p);
|
||||
tkComment: result := newCommentStmt(p);
|
||||
//tkSad, tkInd, tkDed: assert(false);
|
||||
else result := parseExprStmt(p)
|
||||
else begin
|
||||
if isExprStart(p) then
|
||||
result := parseExprStmt(p)
|
||||
else
|
||||
result := nil;
|
||||
end
|
||||
end;
|
||||
skipComment(p, result);
|
||||
if result <> nil then
|
||||
skipComment(p, result);
|
||||
end;
|
||||
|
||||
function complexOrSimpleStmt(var p: TParser): PNode;
|
||||
|
|
@ -1738,6 +1765,8 @@ begin
|
|||
end;
|
||||
|
||||
function parseStmt(var p: TParser): PNode;
|
||||
var
|
||||
a: PNode;
|
||||
begin
|
||||
if p.tok.tokType = tkInd then begin
|
||||
result := newNodeP(nkStmtList, p);
|
||||
|
|
@ -1748,9 +1777,14 @@ begin
|
|||
tkSad: getTok(p);
|
||||
tkEof: break;
|
||||
tkDed: begin getTok(p); break end;
|
||||
else addSon(result, complexOrSimpleStmt(p));
|
||||
end;
|
||||
end
|
||||
else begin
|
||||
a := complexOrSimpleStmt(p);
|
||||
if a = nil then break;
|
||||
addSon(result, a);
|
||||
end
|
||||
end
|
||||
end;
|
||||
popInd(p.lex^);
|
||||
end
|
||||
else begin
|
||||
// the case statement is only needed for better error messages:
|
||||
|
|
@ -1762,7 +1796,7 @@ begin
|
|||
end
|
||||
else begin
|
||||
result := simpleStmt(p);
|
||||
skipComment(p, result);
|
||||
if result = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
|
||||
if p.tok.tokType = tkSad then getTok(p);
|
||||
end
|
||||
end
|
||||
|
|
@ -1770,6 +1804,8 @@ begin
|
|||
end;
|
||||
|
||||
function parseModule(var p: TParser): PNode;
|
||||
var
|
||||
a: PNode;
|
||||
begin
|
||||
result := newNodeP(nkStmtList, p);
|
||||
while true do begin
|
||||
|
|
@ -1777,7 +1813,11 @@ begin
|
|||
tkSad: getTok(p);
|
||||
tkDed, tkInd: parMessage(p, errInvalidIndentation);
|
||||
tkEof: break;
|
||||
else addSon(result, complexOrSimpleStmt(p));
|
||||
else begin
|
||||
a := complexOrSimpleStmt(p);
|
||||
if a = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
|
||||
addSon(result, a);
|
||||
end
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
|
@ -1795,6 +1835,7 @@ begin
|
|||
tkEof: break;
|
||||
else begin
|
||||
result := complexOrSimpleStmt(p);
|
||||
if result = nil then parMessage(p, errExprExpected, tokToStr(p.tok));
|
||||
break
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -135,8 +135,8 @@ var
|
|||
v: string;
|
||||
m: TMagic;
|
||||
begin
|
||||
if not (sfSystemModule in c.module.flags) then
|
||||
liMessage(n.info, errMagicOnlyInSystem);
|
||||
//if not (sfSystemModule in c.module.flags) then
|
||||
// liMessage(n.info, errMagicOnlyInSystem);
|
||||
if n.kind <> nkExprColonExpr then
|
||||
liMessage(n.info, errStringLiteralExpected);
|
||||
if n.sons[1].kind = nkIdent then v := n.sons[1].ident.s
|
||||
|
|
@ -172,6 +172,19 @@ begin
|
|||
liMessage(n.info, errOnOrOffExpected)
|
||||
end;
|
||||
|
||||
procedure pragmaDeadCodeElim(c: PContext; n: PNode);
|
||||
begin
|
||||
if (n.kind = nkExprColonExpr) and (n.sons[1].kind = nkIdent) then begin
|
||||
case whichKeyword(n.sons[1].ident) of
|
||||
wOn: include(c.module.flags, sfDeadCodeElim);
|
||||
wOff: exclude(c.module.flags, sfDeadCodeElim);
|
||||
else liMessage(n.info, errOnOrOffExpected)
|
||||
end
|
||||
end
|
||||
else
|
||||
liMessage(n.info, errOnOrOffExpected)
|
||||
end;
|
||||
|
||||
procedure processCallConv(c: PContext; n: PNode);
|
||||
var
|
||||
sw: TSpecialWord;
|
||||
|
|
@ -466,6 +479,7 @@ begin
|
|||
wVolatile: begin noVal(it); Include(sym.flags, sfVolatile); end;
|
||||
wRegister: begin noVal(it); include(sym.flags, sfRegister); end;
|
||||
wThreadVar: begin noVal(it); include(sym.flags, sfThreadVar); end;
|
||||
wDeadCodeElim: pragmaDeadCodeElim(c, it);
|
||||
wMagic: processMagic(c, it, sym);
|
||||
wCompileTime: begin
|
||||
noVal(it);
|
||||
|
|
@ -612,7 +626,7 @@ begin
|
|||
wHint, wWarning, wError, wFatal, wDefine, wUndef,
|
||||
wCompile, wLink, wLinkSys, wPure,
|
||||
wPush, wPop, wFixupSystem, wBreakpoint, wCheckpoint,
|
||||
wPassL, wPassC]);
|
||||
wPassL, wPassC, wDeadCodeElim]);
|
||||
end;
|
||||
|
||||
procedure pragmaLambda(c: PContext; s: PSym; n: PNode);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ uses
|
|||
|
||||
type
|
||||
TRenderFlag = (renderNone, renderNoBody, renderNoComments,
|
||||
renderNoPragmas, renderIds);
|
||||
renderDocComments, renderNoPragmas, renderIds);
|
||||
TRenderFlags = set of TRenderFlag;
|
||||
|
||||
TRenderTok = record
|
||||
|
|
@ -354,12 +354,20 @@ end;
|
|||
const
|
||||
Space = ' '+'';
|
||||
|
||||
function shouldRenderComment(var g: TSrcGen; n: PNode): bool;
|
||||
begin
|
||||
result := false;
|
||||
if n.comment <> snil then
|
||||
result := not (renderNoComments in g.flags) or
|
||||
(renderDocComments in g.flags) and startsWith(n.comment, '##');
|
||||
end;
|
||||
|
||||
procedure gcom(var g: TSrcGen; n: PNode);
|
||||
var
|
||||
ml: int;
|
||||
begin
|
||||
assert(n <> nil);
|
||||
if (n.comment <> snil) and not (renderNoComments in g.flags) then begin
|
||||
if shouldRenderComment(g, n) then begin
|
||||
if (g.pendingNL < 0) and (length(g.buf) > 0)
|
||||
and (g.buf[length(g.buf)] <> ' ') then
|
||||
put(g, tkSpaces, Space);
|
||||
|
|
@ -488,7 +496,7 @@ begin
|
|||
nkAddr: result := lsub(n.sons[0])+length('addr()');
|
||||
nkHiddenAddr, nkHiddenDeref: result := lsub(n.sons[0]);
|
||||
nkCommand: result := lsub(n.sons[0])+lcomma(n, 1)+1;
|
||||
nkExprEqExpr, nkDefaultTypeParam, nkAsgn: result := lsons(n)+3;
|
||||
nkExprEqExpr, nkDefaultTypeParam, nkAsgn, nkFastAsgn: result := lsons(n)+3;
|
||||
nkPar, nkCurly, nkBracket: result := lcomma(n)+2;
|
||||
nkTupleTy: result := lcomma(n)+length('tuple[]');
|
||||
nkQualified, nkDotExpr: result := lsons(n)+1;
|
||||
|
|
@ -502,6 +510,7 @@ begin
|
|||
if n.sons[L-1] <> nil then
|
||||
result := result + lsub(n.sons[L-1]) + 3;
|
||||
end;
|
||||
nkVarTuple: result := lcomma(n, 0, -3) + length('() = ') + lsub(lastSon(n));
|
||||
nkChckRangeF: result := length('chckRangeF') + 2 + lcomma(n);
|
||||
nkChckRange64: result := length('chckRange64') + 2 + lcomma(n);
|
||||
nkChckRange: result := length('chckRange') + 2 + lcomma(n);
|
||||
|
|
@ -981,7 +990,7 @@ begin
|
|||
put(g, tkSpaces, space);
|
||||
gcomma(g, n, 1);
|
||||
end;
|
||||
nkExprEqExpr, nkDefaultTypeParam, nkAsgn: begin
|
||||
nkExprEqExpr, nkDefaultTypeParam, nkAsgn, nkFastAsgn: begin
|
||||
gsub(g, n.sons[0]);
|
||||
put(g, tkSpaces, Space);
|
||||
putWithSpace(g, tkEquals, '='+'');
|
||||
|
|
@ -1056,6 +1065,14 @@ begin
|
|||
gsub(g, n.sons[L-1], c)
|
||||
end;
|
||||
end;
|
||||
nkVarTuple: begin
|
||||
put(g, tkParLe, '('+'');
|
||||
gcomma(g, n, 0, -3);
|
||||
put(g, tkParRi, ')'+'');
|
||||
put(g, tkSpaces, Space);
|
||||
putWithSpace(g, tkEquals, '='+'');
|
||||
gsub(g, lastSon(n), c);
|
||||
end;
|
||||
nkExprColonExpr: begin
|
||||
gsub(g, n.sons[0]);
|
||||
putWithSpace(g, tkColon, ':'+'');
|
||||
|
|
@ -1362,11 +1379,11 @@ begin
|
|||
nkTupleTy: begin
|
||||
put(g, tkTuple, 'tuple');
|
||||
put(g, tkBracketLe, '['+'');
|
||||
assert(n.sons[0].kind = nkIdentDefs);
|
||||
gcomma(g, n);
|
||||
put(g, tkBracketRi, ']'+'');
|
||||
end;
|
||||
else begin
|
||||
//nkNone, nkMetaNode, nkTableConstr, nkExplicitTypeListCall: begin
|
||||
InternalError(n.info, 'rnimsyn.gsub(' +{&} nodeKindToStr[n.kind] +{&} ')')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -503,7 +503,7 @@ end;
|
|||
|
||||
function process(c: PPassContext; n: PNode): PNode;
|
||||
var
|
||||
i, j: int;
|
||||
i: int;
|
||||
w: PRodWriter;
|
||||
a: PNode;
|
||||
s: PSym;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ unit ropes;
|
|||
efficiently; especially concatenation is done in O(1) instead of O(N).
|
||||
Ropes make use a lazy evaluation: They are essentially concatenation
|
||||
trees that are only flattened when converting to a native Nimrod
|
||||
string or when written to disk. The empty string is represented with a
|
||||
string or when written to disk. The empty string is represented by a
|
||||
nil pointer.
|
||||
A little picture makes everything clear:
|
||||
|
||||
|
|
@ -541,7 +541,6 @@ begin
|
|||
if (r.data <> snil) then begin
|
||||
if r.len > bufSize then
|
||||
// A token bigger than 1 KB? - This cannot happen in reality.
|
||||
// Well, at least I hope so. 1 KB did happen!
|
||||
internalError('ropes: token too long');
|
||||
readBytes := readBuffer(bin, buf, r.len);
|
||||
result := (readBytes = r.len) // BUGFIX
|
||||
|
|
|
|||
14
nim/rst.pas
14
nim/rst.pas
|
|
@ -494,7 +494,13 @@ begin
|
|||
if n.kind = rnLeaf then begin
|
||||
for i := strStart to length(n.text)+strStart-1 do begin
|
||||
case n.text[i] of
|
||||
'a'..'z', '0'..'9': begin
|
||||
'0'..'9': begin
|
||||
if b then begin addChar(r, '-'); b := false; end;
|
||||
// BUGFIX: HTML id's cannot start with a digit
|
||||
if length(r) = 0 then addChar(r, 'Z');
|
||||
addChar(r, n.text[i])
|
||||
end;
|
||||
'a'..'z': begin
|
||||
if b then begin addChar(r, '-'); b := false; end;
|
||||
addChar(r, n.text[i])
|
||||
end;
|
||||
|
|
@ -1235,7 +1241,7 @@ begin
|
|||
result := nil;
|
||||
if (p.tok[p.idx].kind = tkIndent)
|
||||
and (p.tok[p.idx+1].symbol = ':'+'') then begin
|
||||
col := p.tok[p.idx].col;
|
||||
col := p.tok[p.idx].ival; // BUGFIX!
|
||||
result := newRstNode(rnFieldList);
|
||||
inc(p.idx);
|
||||
while true do begin
|
||||
|
|
@ -1252,10 +1258,10 @@ var
|
|||
i: int;
|
||||
f: PRstNode;
|
||||
begin
|
||||
assert(n.kind = rnDirective);
|
||||
result := '';
|
||||
if n.sons[1] = nil then exit;
|
||||
assert(n.sons[1].kind = rnFieldList);
|
||||
if (n.sons[1].kind <> rnFieldList) then
|
||||
InternalError('getFieldValue (2): ' + rstnodeKindToStr[n.sons[1].kind]);
|
||||
for i := 0 to rsonsLen(n.sons[1])-1 do begin
|
||||
f := n.sons[1].sons[i];
|
||||
if cmpIgnoreStyle(addNodes(f.sons[0]), fieldname) = 0 then begin
|
||||
|
|
|
|||
|
|
@ -21,9 +21,8 @@ interface
|
|||
{$include 'config.inc'}
|
||||
|
||||
uses
|
||||
charsets, nsystem, sysutils,
|
||||
hashes, options, msgs, strutils, platform, idents,
|
||||
lexbase, llstream, wordrecg;
|
||||
charsets, nsystem, sysutils, hashes, options, msgs, strutils, platform,
|
||||
idents, lexbase, llstream, wordrecg;
|
||||
|
||||
const
|
||||
MaxLineLength = 80; // lines longer than this lead to a warning
|
||||
|
|
@ -31,7 +30,7 @@ const
|
|||
numChars: TCharSet = ['0'..'9','a'..'z','A'..'Z'];
|
||||
SymChars: TCharSet = ['a'..'z', 'A'..'Z', '0'..'9', #128..#255];
|
||||
SymStartChars: TCharSet = ['a'..'z', 'A'..'Z', #128..#255];
|
||||
OpChars: TCharSet = ['+', '-', '*', '/', '<', '>', '!', '?', '^', '.',
|
||||
OpChars: TCharSet = ['+', '-', '*', '/', '\', '<', '>', '!', '?', '^', '.',
|
||||
'|', '=', '%', '&', '$', '@', '~', #128..#255];
|
||||
|
||||
type
|
||||
|
|
@ -162,6 +161,8 @@ var
|
|||
gLinesCompiled: int; // all lines that have been compiled
|
||||
|
||||
procedure pushInd(var L: TLexer; indent: int);
|
||||
procedure popInd(var L: TLexer);
|
||||
|
||||
function isKeyword(kind: TTokType): boolean;
|
||||
|
||||
procedure openLexer(out lex: TLexer; const filename: string;
|
||||
|
|
@ -206,6 +207,14 @@ begin
|
|||
//writeln('push indent ', indent);
|
||||
end;
|
||||
|
||||
procedure popInd(var L: TLexer);
|
||||
var
|
||||
len: int;
|
||||
begin
|
||||
len := length(L.indentStack);
|
||||
setLength(L.indentStack, len-1);
|
||||
end;
|
||||
|
||||
function findIdent(const L: TLexer; indent: int): boolean;
|
||||
var
|
||||
i: int;
|
||||
|
|
@ -809,13 +818,11 @@ begin
|
|||
end;
|
||||
dec(L.dedent);
|
||||
tok.tokType := tkDed;
|
||||
if i >= 0 then
|
||||
setLength(L.indentStack, i+1) // pop indentations
|
||||
else begin
|
||||
if i < 0 then begin
|
||||
tok.tokType := tkSad; // for the parser it is better as SAD
|
||||
lexMessage(L, errInvalidIndentation);
|
||||
end
|
||||
end;
|
||||
end
|
||||
end;
|
||||
|
||||
procedure scanComment(var L: TLexer; var tok: TToken);
|
||||
|
|
|
|||
25
nim/sem.pas
25
nim/sem.pas
|
|
@ -76,6 +76,22 @@ function semStmt(c: PContext; n: PNode): PNode; forward;
|
|||
function semConstExpr(c: PContext; n: PNode): PNode;
|
||||
var
|
||||
e: PNode;
|
||||
begin
|
||||
e := semExprWithType(c, n);
|
||||
if e = nil then begin
|
||||
liMessage(n.info, errConstExprExpected);
|
||||
result := nil; exit
|
||||
end;
|
||||
result := getConstExpr(c.module, e);
|
||||
if result = nil then
|
||||
liMessage(n.info, errConstExprExpected);
|
||||
end;
|
||||
|
||||
function semAndEvalConstExpr(c: PContext; n: PNode): PNode;
|
||||
var
|
||||
e: PNode;
|
||||
p: PEvalContext;
|
||||
s: PStackFrame;
|
||||
begin
|
||||
e := semExprWithType(c, n);
|
||||
if e = nil then begin
|
||||
|
|
@ -85,7 +101,14 @@ begin
|
|||
result := getConstExpr(c.module, e);
|
||||
if result = nil then begin
|
||||
//writeln(output, renderTree(n));
|
||||
liMessage(n.info, errConstExprExpected);
|
||||
p := newEvalContext(c.module, '');
|
||||
s := newStackFrame();
|
||||
s.call := e;
|
||||
pushStackFrame(p, s);
|
||||
result := eval(p, e);
|
||||
popStackFrame(p);
|
||||
if (result = nil) or (result.kind = nkEmpty) then
|
||||
liMessage(n.info, errConstExprExpected);
|
||||
end
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ type
|
|||
PContext = ^TContext;
|
||||
TContext = object(TPassContext) // a context represents a module
|
||||
module: PSym; // the module sym belonging to the context
|
||||
filename: string; // the module's filename
|
||||
tab: TSymTab; // each module has its own symbol table
|
||||
AmbigiousSymbols: TIntSet; // contains ids of all ambigious symbols (cannot
|
||||
// store this info in the syms themselves!)
|
||||
|
|
@ -160,6 +161,7 @@ begin
|
|||
result.module := module;
|
||||
result.generics := newNode(nkStmtList);
|
||||
{@emit result.converters := @[];}
|
||||
result.filename := nimfile;
|
||||
end;
|
||||
|
||||
procedure addConverter(c: PContext; conv: PSym);
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ var
|
|||
diff: int;
|
||||
begin
|
||||
diff := inheritanceDiff(castDest, src);
|
||||
if diff = 0 then
|
||||
liMessage(info, hintConvToBaseNotNeeded)
|
||||
else if diff = high(int) then
|
||||
//if diff = 0 then
|
||||
// liMessage(info, hintConvToBaseNotNeeded)
|
||||
if diff = high(int) then
|
||||
liMessage(info, errGenerated,
|
||||
format(MsgKindToString(errIllegalConvFromXtoY),
|
||||
[typeToString(src), typeToString(castDest)]));
|
||||
|
|
@ -466,11 +466,11 @@ begin
|
|||
if m.state <> csMatch then begin
|
||||
msg := msgKindToString(errTypeMismatch);
|
||||
for i := 1 to sonsLen(n)-1 do begin
|
||||
msg := msg +{&} typeToString(n.sons[i].typ);
|
||||
if i <> sonsLen(n)-1 then msg := msg + ', ';
|
||||
add(msg, typeToString(n.sons[i].typ));
|
||||
if i <> sonsLen(n)-1 then add(msg, ', ');
|
||||
end;
|
||||
msg := msg +{&} ')' +{&} nl +{&} msgKindToString(errButExpected) +{&}
|
||||
nl +{&} typeToString(n.sons[0].typ);
|
||||
add(msg, ')' +{&} nl +{&} msgKindToString(errButExpected) +{&}
|
||||
nl +{&} typeToString(n.sons[0].typ));
|
||||
liMessage(n.Info, errGenerated, msg);
|
||||
result := nil
|
||||
end
|
||||
|
|
@ -1117,7 +1117,7 @@ begin
|
|||
if s <> nil then
|
||||
result := semSym(c, n, s, flags)
|
||||
else
|
||||
// test!
|
||||
// this is a test comment; please don't touch it
|
||||
result := semFieldAccess(c, n, flags);
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,25 @@ begin
|
|||
end
|
||||
end;
|
||||
|
||||
function enumValToString(a: PNode): string;
|
||||
var
|
||||
n: PNode;
|
||||
field: PSym;
|
||||
x: biggestInt;
|
||||
i: int;
|
||||
begin
|
||||
x := getInt(a);
|
||||
n := a.typ.n;
|
||||
for i := 0 to sonsLen(n)-1 do begin
|
||||
if n.sons[i].kind <> nkSym then InternalError(a.info, 'enumValToString');
|
||||
field := n.sons[i].sym;
|
||||
if field.position = x then begin
|
||||
result := field.name.s; exit
|
||||
end;
|
||||
end;
|
||||
InternalError(a.info, 'no symbol for ordinal value: ' + toString(x));
|
||||
end;
|
||||
|
||||
function evalOp(m: TMagic; n, a, b: PNode): PNode;
|
||||
// if this is an unary operation, b is nil
|
||||
begin
|
||||
|
|
@ -246,11 +265,18 @@ begin
|
|||
// available for interpretation. I don't know how to fix this.
|
||||
//result := newStrNodeT(renderTree(a, {@set}[renderNoComments]), n);
|
||||
end;
|
||||
mIntToStr, mInt64ToStr, mBoolToStr, mCharToStr:
|
||||
mIntToStr, mInt64ToStr:
|
||||
result := newStrNodeT(toString(getOrdValue(a)), n);
|
||||
mBoolToStr: begin
|
||||
if getOrdValue(a) = 0 then
|
||||
result := newStrNodeT('false', n)
|
||||
else
|
||||
result := newStrNodeT('true', n)
|
||||
end;
|
||||
mFloatToStr: result := newStrNodeT(toStringF(getFloat(a)), n);
|
||||
mCStrToStr: result := newStrNodeT(getStrOrChar(a), n);
|
||||
mCStrToStr, mCharToStr: result := newStrNodeT(getStrOrChar(a), n);
|
||||
mStrToStr: result := a;
|
||||
mEnumToStr: result := newStrNodeT(enumValToString(a), n);
|
||||
mArrToSeq: begin
|
||||
result := copyTree(a);
|
||||
result.typ := n.typ;
|
||||
|
|
@ -370,6 +396,10 @@ begin
|
|||
mNimrodMinor: result := newIntNodeT(VersionMinor, n);
|
||||
mNimrodPatch: result := newIntNodeT(VersionPatch, n);
|
||||
mCpuEndian: result := newIntNodeT(ord(CPU[targetCPU].endian), n);
|
||||
mHostOS:
|
||||
result := newStrNodeT(toLower(platform.OS[targetOS].name), n);
|
||||
mHostCPU:
|
||||
result := newStrNodeT(toLower(platform.CPU[targetCPU].name),n);
|
||||
mNaN: result := newFloatNodeT(NaN, n);
|
||||
mInf: result := newFloatNodeT(Inf, n);
|
||||
mNegInf: result := newFloatNodeT(NegInf, n);
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ begin
|
|||
a := newNodeI(nkAsgn, n.sons[0].info);
|
||||
|
||||
n.sons[0] := fitNode(c, restype, n.sons[0]);
|
||||
// optimize away ``return result``, because it would be transferred
|
||||
// optimize away ``return result``, because it would be transformed
|
||||
// to ``result = result; return``:
|
||||
if (n.sons[0].kind = nkSym) and (sfResult in n.sons[0].sym.flags) then
|
||||
begin
|
||||
|
|
@ -378,14 +378,14 @@ function semVar(c: PContext; n: PNode): PNode;
|
|||
var
|
||||
i, j, len: int;
|
||||
a, b, def: PNode;
|
||||
typ: PType;
|
||||
typ, tup: PType;
|
||||
v: PSym;
|
||||
begin
|
||||
result := copyNode(n);
|
||||
for i := 0 to sonsLen(n)-1 do begin
|
||||
a := n.sons[i];
|
||||
if a.kind = nkCommentStmt then continue;
|
||||
if (a.kind <> nkIdentDefs) then IllFormedAst(a);
|
||||
if (a.kind <> nkIdentDefs) and (a.kind <> nkVarTuple) then IllFormedAst(a);
|
||||
checkMinSonsLen(a, 3);
|
||||
len := sonsLen(a);
|
||||
if a.sons[len-2] <> nil then
|
||||
|
|
@ -401,14 +401,21 @@ begin
|
|||
end
|
||||
else
|
||||
def := nil;
|
||||
tup := skipGeneric(typ);
|
||||
if a.kind = nkVarTuple then begin
|
||||
if tup.kind <> tyTuple then liMessage(a.info, errXExpected, 'tuple');
|
||||
if len-2 <> sonsLen(tup) then
|
||||
liMessage(a.info, errWrongNumberOfVariables);
|
||||
end;
|
||||
for j := 0 to len-3 do begin
|
||||
if (c.p.owner = nil) then begin
|
||||
if c.p.owner = nil then begin
|
||||
v := semIdentWithPragma(c, skVar, a.sons[j], {@set}[sfStar, sfMinus]);
|
||||
include(v.flags, sfGlobal);
|
||||
end
|
||||
else
|
||||
v := semIdentWithPragma(c, skVar, a.sons[j], {@set}[]);
|
||||
v.typ := typ;
|
||||
if a.kind <> nkVarTuple then v.typ := typ
|
||||
else v.typ := tup.sons[j];
|
||||
if v.flags * [sfStar, sfMinus] <> {@set}[] then
|
||||
include(v.flags, sfInInterface);
|
||||
addInterfaceDecl(c, v);
|
||||
|
|
@ -443,7 +450,7 @@ begin
|
|||
|
||||
if a.sons[1] <> nil then typ := semTypeNode(c, a.sons[1], nil)
|
||||
else typ := nil;
|
||||
def := semConstExpr(c, a.sons[2]);
|
||||
def := semAndEvalConstExpr(c, a.sons[2]);
|
||||
// check type compability between def.typ and typ:
|
||||
if (typ <> nil) then begin
|
||||
def := fitRemoveHiddenConv(c, typ, def);
|
||||
|
|
@ -495,7 +502,7 @@ begin
|
|||
iter := skipGeneric(n.sons[len-2].typ);
|
||||
openScope(c.tab);
|
||||
if iter.kind <> tyTuple then begin
|
||||
if len <> 3 then liMessage(n.info, errWrongNumberOfLoopVariables);
|
||||
if len <> 3 then liMessage(n.info, errWrongNumberOfVariables);
|
||||
v := newSymS(skForVar, n.sons[0], c);
|
||||
v.typ := iter;
|
||||
n.sons[0] := newSymNode(v);
|
||||
|
|
@ -503,7 +510,7 @@ begin
|
|||
end
|
||||
else begin
|
||||
if len-2 <> sonsLen(iter) then
|
||||
liMessage(n.info, errWrongNumberOfLoopVariables);
|
||||
liMessage(n.info, errWrongNumberOfVariables);
|
||||
for i := 0 to len-3 do begin
|
||||
v := newSymS(skForVar, n.sons[i], c);
|
||||
v.typ := iter.sons[i];
|
||||
|
|
@ -840,6 +847,7 @@ begin
|
|||
closeScope(c.tab); // close scope for parameters
|
||||
popOwner();
|
||||
c.p := oldP; // restore
|
||||
result.typ := s.typ;
|
||||
end;
|
||||
|
||||
function semProcAux(c: PContext; n: PNode; kind: TSymKind): PNode;
|
||||
|
|
|
|||
|
|
@ -256,8 +256,8 @@ begin
|
|||
if templ = nil then begin result := nil; exit end;
|
||||
case templ.kind of
|
||||
nkSym: begin
|
||||
if (templ.sym.kind = skTypeParam)
|
||||
and (templ.sym.owner.id = sym.id) then
|
||||
if (templ.sym.kind = skTypeParam) then
|
||||
//and (templ.sym.owner.id = sym.id) then
|
||||
result := copyTree(actual.sons[templ.sym.position+1])
|
||||
else
|
||||
result := copyNode(templ)
|
||||
|
|
@ -285,15 +285,18 @@ begin
|
|||
result.containerID := s.typ.containerID; // ... but the same containerID
|
||||
result.sym := s;
|
||||
if (s.typ.containerID = 0) then
|
||||
InternalError(n.info, 'semGeneric');
|
||||
InternalError(n.info, 'semtypes.semGeneric');
|
||||
for i := 1 to sonsLen(n)-1 do begin
|
||||
elem := semTypeNode(c, n.sons[i], nil);
|
||||
if elem.kind = tyGenericParam then result.kind := tyGeneric;
|
||||
if elem.kind = tyGenericParam then
|
||||
result.kind := tyGeneric; // prevend type from instantiation
|
||||
addSon(result, elem);
|
||||
end;
|
||||
if s.ast <> nil then begin
|
||||
inst := instGenericAux(c, s.ast.sons[2], n, s);
|
||||
if result.kind = tyGenericInst then begin
|
||||
if (result.kind = tyGenericInst) then begin
|
||||
inst := instGenericAux(c, s.ast.sons[2], n, s);
|
||||
internalError(n.info, 'Generic containers not implemented');
|
||||
// XXX: implementation does not work this way
|
||||
// does checking of instantiated type for us:
|
||||
elem := semTypeNode(c, inst, nil);
|
||||
elem.id := result.containerID;
|
||||
|
|
@ -304,6 +307,8 @@ begin
|
|||
end
|
||||
else
|
||||
liMessage(n.info, errCannotInstantiateX, s.name.s);
|
||||
(*if computeSize(result) < 0 then
|
||||
liMessage(s.info, errIllegalRecursionInTypeX, s.name.s);*)
|
||||
end;
|
||||
|
||||
function semIdentVis(c: PContext; kind: TSymKind; n: PNode;
|
||||
|
|
|
|||
|
|
@ -80,20 +80,23 @@ var
|
|||
begin
|
||||
result := msgKindToString(errTypeMismatch);
|
||||
for i := 1 to sonsLen(n)-1 do begin
|
||||
result := result +{&} typeToString(n.sons[i].typ);
|
||||
if i <> sonsLen(n)-1 then result := result + ', ';
|
||||
debug(n.sons[i].typ);
|
||||
add(result, typeToString(n.sons[i].typ));
|
||||
if i <> sonsLen(n)-1 then add(result, ', ');
|
||||
end;
|
||||
addChar(result, ')');
|
||||
candidates := '';
|
||||
sym := initOverloadIter(o, c, n.sons[0]);
|
||||
while sym <> nil do begin
|
||||
if sym.kind in [skProc, skIterator, skConverter] then
|
||||
candidates := candidates +{&} getProcHeader(sym) +{&} nl;
|
||||
if sym.kind in [skProc, skIterator, skConverter] then begin
|
||||
add(candidates, getProcHeader(sym));
|
||||
add(candidates, nl)
|
||||
end;
|
||||
sym := nextOverloadIter(o, c, n.sons[0]);
|
||||
end;
|
||||
if candidates <> '' then
|
||||
result := result +{&} nl +{&} msgKindToString(errButExpected) +{&} nl
|
||||
+{&} candidates;
|
||||
add(result, nl +{&} msgKindToString(errButExpected) +{&} nl
|
||||
+{&} candidates);
|
||||
end;
|
||||
|
||||
function typeRel(var mapping: TIdTable; f, a: PType): TTypeRelation; overload;
|
||||
|
|
@ -431,7 +434,7 @@ begin // is a subtype of f?
|
|||
tyAnyEnum: begin
|
||||
case a.kind of
|
||||
tyRange: result := typeRel(mapping, f, base(a));
|
||||
tyEnum: result := isEqual;
|
||||
tyEnum: result := isSubtype;
|
||||
else begin end
|
||||
end
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@
|
|||
//
|
||||
unit strtabs;
|
||||
|
||||
// A configuration file parser; the Nimrod version of this file
|
||||
// will become part of the standard library.
|
||||
// String tables.
|
||||
|
||||
interface
|
||||
|
||||
|
|
|
|||
|
|
@ -75,13 +75,14 @@ const
|
|||
function strip(const s: string; const chars: TCharSet = WhiteSpace): string;
|
||||
function allCharsInSet(const s: string; const theSet: TCharSet): bool;
|
||||
|
||||
function quoteIfSpaceExists(const s: string): string;
|
||||
function quoteIfContainsWhite(const s: string): string;
|
||||
|
||||
implementation
|
||||
|
||||
function quoteIfSpaceExists(const s: string): string;
|
||||
function quoteIfContainsWhite(const s: string): string;
|
||||
begin
|
||||
if (findSubStr(' ', s) >= strStart) and (s[strStart] <> '"') then
|
||||
if ((findSubStr(' ', s) >= strStart)
|
||||
or (findSubStr(#9, s) >= strStart)) and (s[strStart] <> '"') then
|
||||
result := '"' +{&} s +{&} '"'
|
||||
else
|
||||
result := s
|
||||
|
|
|
|||
47
nim/tigen.pas
Normal file
47
nim/tigen.pas
Normal 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.
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ interface
|
|||
uses
|
||||
sysutils, nsystem, charsets, strutils,
|
||||
lists, options, ast, astalgo, trees, treetab,
|
||||
msgs, nos, idents, rnimsyn, types, passes, semfold;
|
||||
msgs, nos, idents, rnimsyn, types, passes, semfold, magicsys;
|
||||
|
||||
const
|
||||
genPrefix = ':tmp'; // prefix for generated names
|
||||
|
|
@ -139,7 +139,7 @@ More efficient, but not implementable:
|
|||
|
||||
function newAsgnStmt(c: PTransf; le, ri: PNode): PNode;
|
||||
begin
|
||||
result := newNodeI(nkAsgn, ri.info);
|
||||
result := newNodeI(nkFastAsgn, ri.info);
|
||||
addSon(result, le);
|
||||
addSon(result, ri);
|
||||
end;
|
||||
|
|
@ -224,6 +224,28 @@ begin
|
|||
end
|
||||
end;
|
||||
|
||||
function newTupleAccess(tup: PNode; i: int): PNode;
|
||||
var
|
||||
lit: PNode;
|
||||
begin
|
||||
result := newNodeIT(nkBracketExpr, tup.info, tup.typ.sons[i]);
|
||||
addSon(result, copyTree(tup));
|
||||
lit := newNodeIT(nkIntLit, tup.info, getSysType(tyInt));
|
||||
lit.intVal := i;
|
||||
addSon(result, lit);
|
||||
end;
|
||||
|
||||
procedure unpackTuple(c: PTransf; n, father: PNode);
|
||||
var
|
||||
i: int;
|
||||
begin
|
||||
// XXX: BUG: what if `n` is an expression with side-effects?
|
||||
for i := 0 to sonsLen(n)-1 do begin
|
||||
addSon(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
|
||||
transform(c, newTupleAccess(n, i))));
|
||||
end
|
||||
end;
|
||||
|
||||
function transformYield(c: PTransf; n: PNode): PNode;
|
||||
var
|
||||
e: PNode;
|
||||
|
|
@ -239,10 +261,8 @@ begin
|
|||
transform(c, copyTree(e.sons[i]))));
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// XXX: tuple unpacking:
|
||||
internalError(n.info, 'tuple unpacking is not implemented');
|
||||
end
|
||||
else
|
||||
unpackTuple(c, e, result);
|
||||
end
|
||||
else begin
|
||||
e := transform(c, copyTree(e));
|
||||
|
|
@ -523,7 +543,7 @@ end;
|
|||
(*
|
||||
# example:
|
||||
proc map(f: proc (x: int): int {.closure}, a: seq[int]): seq[int] =
|
||||
result = []
|
||||
result = @[]
|
||||
for elem in a:
|
||||
add result, f(a)
|
||||
|
||||
|
|
@ -534,12 +554,12 @@ end;
|
|||
|
||||
proc map(f: proc(x: int): int, closure: pointer,
|
||||
a: seq[int]): seq[int] =
|
||||
result = []
|
||||
result = @[]
|
||||
for elem in a:
|
||||
add result, f(a, closure)
|
||||
|
||||
type
|
||||
PMyClosure = ref record
|
||||
PMyClosure = ref object
|
||||
y: var int
|
||||
|
||||
proc myLambda(x: int, closure: pointer) =
|
||||
|
|
|
|||
|
|
@ -638,15 +638,15 @@ begin
|
|||
assert(sonsLen(t.n) = sonsLen(t));
|
||||
for i := 0 to sonsLen(t.n)-1 do begin
|
||||
assert(t.n.sons[i].kind = nkSym);
|
||||
result := result +{&} t.n.sons[i].sym.name.s +{&} ': '
|
||||
+{&} typeToString(t.sons[i]);
|
||||
if i < sonsLen(t.n)-1 then result := result +{&} ', ';
|
||||
add(result, t.n.sons[i].sym.name.s +{&} ': '
|
||||
+{&} typeToString(t.sons[i]));
|
||||
if i < sonsLen(t.n)-1 then add(result, ', ');
|
||||
end
|
||||
end
|
||||
else begin
|
||||
for i := 0 to sonsLen(t)-1 do begin
|
||||
result := result +{&} typeToString(t.sons[i]);
|
||||
if i < sonsLen(t)-1 then result := result +{&} ', ';
|
||||
add(result, typeToString(t.sons[i]));
|
||||
if i < sonsLen(t)-1 then add(result, ', ');
|
||||
end
|
||||
end;
|
||||
addChar(result, ']')
|
||||
|
|
@ -659,14 +659,14 @@ begin
|
|||
tyProc: begin
|
||||
result := 'proc (';
|
||||
for i := 1 to sonsLen(t)-1 do begin
|
||||
result := result +{&} typeToString(t.sons[i]);
|
||||
if i < sonsLen(t)-1 then result := result +{&} ', ';
|
||||
add(result, typeToString(t.sons[i]));
|
||||
if i < sonsLen(t)-1 then add(result, ', ');
|
||||
end;
|
||||
addChar(result, ')');
|
||||
if t.sons[0] <> nil then
|
||||
result := result +{&} ': ' +{&} TypeToString(t.sons[0]);
|
||||
add(result, ': ' +{&} TypeToString(t.sons[0]));
|
||||
if t.callConv <> ccDefault then
|
||||
result := result +{&} '{.' +{&} CallingConvToStr[t.callConv] +{&} '.}';
|
||||
add(result, '{.' +{&} CallingConvToStr[t.callConv] +{&} '.}');
|
||||
end;
|
||||
else begin
|
||||
result := typeToStr[t.kind]
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue