mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 09:24:36 +00:00
beginning of a taint mode; type system enhancements
This commit is contained in:
@@ -143,6 +143,7 @@ type
|
||||
nkWhileStmt, # a while statement
|
||||
nkCaseStmt, # a case statement
|
||||
nkVarSection, # a var section
|
||||
nkLetSection, # a let section
|
||||
nkConstSection, # a const section
|
||||
nkConstDef, # a const definition
|
||||
nkTypeSection, # a type section (consists of type definitions)
|
||||
@@ -178,6 +179,8 @@ type
|
||||
nkRefTy,
|
||||
nkPtrTy,
|
||||
nkVarTy,
|
||||
nkConstTy, # ``const T``
|
||||
nkMutableTy, # ``mutable T``
|
||||
nkDistinctTy, # distinct type
|
||||
nkProcTy,
|
||||
nkEnumTy,
|
||||
@@ -186,7 +189,7 @@ type
|
||||
TNodeKinds* = set[TNodeKind]
|
||||
|
||||
type
|
||||
TSymFlag* = enum # already 29 flags!
|
||||
TSymFlag* = enum # already 30 flags!
|
||||
sfUsed, # read access of sym (for warnings) or simply used
|
||||
sfExported, # symbol is exported from module
|
||||
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
||||
@@ -212,6 +215,7 @@ type
|
||||
sfProcvar, # proc can be passed to a proc var
|
||||
sfDiscriminant, # field is a discriminant in a record/object
|
||||
sfDeprecated, # symbol is deprecated
|
||||
sfError, # usage of symbol should trigger a compile-time error
|
||||
sfInClosure, # variable is accessed by a closure
|
||||
sfThread, # proc will run as a thread
|
||||
sfCompileTime, # proc can be evaluated at compile time
|
||||
@@ -254,7 +258,12 @@ type
|
||||
tyPointer, tyOpenArray,
|
||||
tyString, tyCString, tyForward,
|
||||
tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
|
||||
tyFloat, tyFloat32, tyFloat64, tyFloat128
|
||||
tyFloat, tyFloat32, tyFloat64, tyFloat128,
|
||||
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
|
||||
tyBigNum,
|
||||
tyConst, tyMutable, tyVarargs,
|
||||
tyIter, # unused
|
||||
tyProxy # currently unused
|
||||
|
||||
TTypeKinds* = set[TTypeKind]
|
||||
|
||||
|
||||
@@ -30,94 +30,8 @@ const
|
||||
"Copyright (c) 2004-2011 by Andreas Rumpf\n"
|
||||
|
||||
const
|
||||
Usage = """
|
||||
Usage:
|
||||
nimrod command [options] [projectfile] [arguments]
|
||||
Command:
|
||||
compile, c compile project with default code generator (C)
|
||||
doc generate the documentation for inputfile
|
||||
i start Nimrod in interactive mode (limited)
|
||||
Arguments:
|
||||
arguments are passed to the program being run (if --run option is selected)
|
||||
Options:
|
||||
-p, --path:PATH add path to search paths
|
||||
-d, --define:SYMBOL define a conditional symbol
|
||||
-u, --undef:SYMBOL undefine a conditional symbol
|
||||
-f, --forceBuild force rebuilding of all modules
|
||||
--stackTrace:on|off turn stack tracing on|off
|
||||
--lineTrace:on|off turn line tracing on|off
|
||||
--threads:on|off turn support for multi-threading on|off
|
||||
-x, --checks:on|off turn all runtime checks on|off
|
||||
--objChecks:on|off turn obj conversion checks on|off
|
||||
--fieldChecks:on|off turn case variant field checks on|off
|
||||
--rangeChecks:on|off turn range checks on|off
|
||||
--boundChecks:on|off turn bound checks on|off
|
||||
--overflowChecks:on|off turn int over-/underflow checks on|off
|
||||
-a, --assertions:on|off turn assertions on|off
|
||||
--floatChecks:on|off turn all floating point (NaN/Inf) checks on|off
|
||||
--nanChecks:on|off turn NaN checks on|off
|
||||
--infChecks:on|off turn Inf checks on|off
|
||||
--deadCodeElim: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|dynamic library
|
||||
-r, --run run the compiled program with given arguments
|
||||
--advanced show advanced command line switches
|
||||
-h, --help show this help
|
||||
"""
|
||||
|
||||
AdvancedUsage = """
|
||||
Advanced commands:
|
||||
compileToC, cc compile project with C code generator
|
||||
compileToCpp, cpp compile project to C++ code
|
||||
compileToOC, objc compile project to Objective C code
|
||||
rst2html convert a reStructuredText file to HTML
|
||||
rst2tex convert a reStructuredText file to TeX
|
||||
run run the project (with Tiny C backend; buggy!)
|
||||
pretty pretty print the inputfile
|
||||
genDepend generate a DOT file containing the
|
||||
module dependency graph
|
||||
dump dump all defined conditionals and search paths
|
||||
check checks the project for syntax and semantic
|
||||
idetools compiler support for IDEs: possible options:
|
||||
--track:FILE,LINE,COL track a file/cursor position
|
||||
--suggest suggest all possible symbols at position
|
||||
--def list all possible symbols at position
|
||||
--context list possible invokation context
|
||||
Advanced options:
|
||||
-o, --out:FILE set the output filename
|
||||
--stdout output to stdout
|
||||
-w, --warnings:on|off turn all warnings on|off
|
||||
--warning[X]:on|off turn specific warning X on|off
|
||||
--hints:on|off turn all hints on|off
|
||||
--hint[X]:on|off turn specific hint X on|off
|
||||
--lib:PATH set the system library path
|
||||
--nimcache:PATH set the path used for generated files
|
||||
-c, --compileOnly compile only; do not assemble or link
|
||||
--noLinking compile but do not link
|
||||
--noMain do not generate a main procedure
|
||||
--genScript generate a compile script (in the 'nimcache'
|
||||
subdirectory named 'compile_$project$scriptext')
|
||||
--os:SYMBOL set the target operating system (cross-compilation)
|
||||
--cpu:SYMBOL set the target processor (cross-compilation)
|
||||
--debuginfo enables debug information
|
||||
--debugger:on|off turn Embedded Nimrod Debugger on|off
|
||||
-t, --passc:OPTION pass an option to the C compiler
|
||||
-l, --passl:OPTION pass an option to the linker
|
||||
--genMapping generate a mapping file containing
|
||||
(Nimrod, mangled) identifier pairs
|
||||
--lineDir:on|off generation of #line directive on|off
|
||||
--threadanalysis:on|off turn thread analysis on|off
|
||||
--skipCfg do not read the general configuration file
|
||||
--skipProjCfg do not read the project's configuration file
|
||||
--gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no GC
|
||||
--index:FILE use FILE to generate a documentation index file
|
||||
--putenv:key=value set an environment variable
|
||||
--listCmd list the commands used to execute external programs
|
||||
--parallelBuild=0|1|... perform a parallel build
|
||||
value = number of processors (0 for auto-detect)
|
||||
--verbosity:0|1|2|3 set Nimrod's verbosity level (0 is default)
|
||||
-v, --version show detailed version information
|
||||
"""
|
||||
Usage = slurp"doc/basicopt.txt".replace("//", "")
|
||||
AdvancedUsage = slurp"doc/advopt.txt".replace("//", "")
|
||||
|
||||
proc getCommandLineDesc(): string =
|
||||
result = `%`(HelpMessage, [VersionAsString, platform.os[platform.hostOS].name,
|
||||
@@ -276,6 +190,7 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
|
||||
of "symbolfiles": result = contains(gGlobalOptions, optSymbolFiles)
|
||||
of "genscript": result = contains(gGlobalOptions, optGenScript)
|
||||
of "threads": result = contains(gGlobalOptions, optThreads)
|
||||
of "taintmode": result = contains(gGlobalOptions, optTaintMode)
|
||||
else: InvalidCmdLineOption(passCmd1, switch, info)
|
||||
|
||||
proc processPath(path: string): string =
|
||||
@@ -399,6 +314,7 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
|
||||
of "assertions", "a": ProcessOnOffSwitch({optAssert}, arg, pass, info)
|
||||
of "deadcodeelim": ProcessOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
|
||||
of "threads": ProcessOnOffSwitchG({optThreads}, arg, pass, info)
|
||||
of "taintmode": ProcessOnOffSwitchG({optTaintMode}, arg, pass, info)
|
||||
of "opt":
|
||||
expectArg(switch, arg, pass, info)
|
||||
case arg.normalize
|
||||
|
||||
@@ -89,7 +89,7 @@ type
|
||||
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
|
||||
errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument,
|
||||
errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate,
|
||||
errXhasSideEffects, errIteratorExpected,
|
||||
errXhasSideEffects, errIteratorExpected, errWrongSymbolX,
|
||||
errUser,
|
||||
warnCannotOpenFile,
|
||||
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
||||
@@ -315,6 +315,7 @@ const
|
||||
errXisNoMacroOrTemplate: "\'$1\' is no macro or template",
|
||||
errXhasSideEffects: "\'$1\' can have side effects",
|
||||
errIteratorExpected: "iterator within for loop context expected",
|
||||
errWrongSymbolX: "usage of \'$1\' is a user-defined error",
|
||||
errUser: "$1",
|
||||
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
||||
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
||||
|
||||
@@ -46,7 +46,8 @@ type # please make sure we have under 32 options
|
||||
optSuggest, # ideTools: 'suggest'
|
||||
optContext, # ideTools: 'context'
|
||||
optDef, # ideTools: 'def'
|
||||
optThreadAnalysis # thread analysis pass
|
||||
optThreadAnalysis, # thread analysis pass
|
||||
optTaintMode # taint mode turned on
|
||||
|
||||
TGlobalOptions* = set[TGlobalOption]
|
||||
TCommands* = enum # Nimrod's commands
|
||||
|
||||
@@ -23,15 +23,15 @@ const
|
||||
wMagic, wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader,
|
||||
wCompilerProc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
|
||||
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
|
||||
wNoStackFrame}
|
||||
wNoStackFrame, wError}
|
||||
converterPragmas* = procPragmas
|
||||
methodPragmas* = procPragmas
|
||||
macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
||||
wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
|
||||
wImportcpp, wImportobjc}
|
||||
wImportcpp, wImportobjc, wError}
|
||||
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
|
||||
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
|
||||
wImportcpp, wImportobjc}
|
||||
wImportcpp, wImportobjc, wError}
|
||||
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
|
||||
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, wLinedir,
|
||||
wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, wFatal,
|
||||
@@ -43,14 +43,14 @@ const
|
||||
wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame}
|
||||
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
|
||||
wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow,
|
||||
wImportcpp, wImportobjc}
|
||||
wImportcpp, wImportobjc, wError}
|
||||
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
||||
wImportcpp, wImportobjc}
|
||||
wImportcpp, wImportobjc, wError}
|
||||
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
||||
wMagic, wHeader, wDeprecated, wCompilerProc, wDynLib, wExtern,
|
||||
wImportcpp, wImportobjc}
|
||||
wImportcpp, wImportobjc, wError}
|
||||
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
|
||||
wExtern, wImportcpp, wImportobjc}
|
||||
wExtern, wImportcpp, wImportobjc, wError}
|
||||
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideEffect,
|
||||
wThread}
|
||||
allRoutinePragmas* = procPragmas + iteratorPragmas + lambdaPragmas
|
||||
@@ -530,7 +530,12 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
||||
if sym.typ != nil: incl(sym.typ.flags, tfThread)
|
||||
of wHint: Message(it.info, hintUser, expectStrLit(c, it))
|
||||
of wWarning: Message(it.info, warnUser, expectStrLit(c, it))
|
||||
of wError: LocalError(it.info, errUser, expectStrLit(c, it))
|
||||
of wError:
|
||||
if sym != nil:
|
||||
noVal(it)
|
||||
incl(sym.flags, sfError)
|
||||
else:
|
||||
LocalError(it.info, errUser, expectStrLit(c, it))
|
||||
of wFatal: Fatal(it.info, errUser, expectStrLit(c, it))
|
||||
of wDefine: processDefine(c, it)
|
||||
of wUndef: processUndef(c, it)
|
||||
|
||||
@@ -178,7 +178,9 @@ proc makeRangeType(c: PContext, first, last: biggestInt,
|
||||
|
||||
proc markUsed*(n: PNode, s: PSym) =
|
||||
incl(s.flags, sfUsed)
|
||||
if sfDeprecated in s.flags: Message(n.info, warnDeprecated, s.name.s)
|
||||
if {sfDeprecated, sfError} * s.flags != {}:
|
||||
if sfDeprecated in s.flags: Message(n.info, warnDeprecated, s.name.s)
|
||||
if sfError in s.flags: LocalError(n.info, errWrongSymbolX, s.name.s)
|
||||
|
||||
proc illFormedAst*(n: PNode) =
|
||||
GlobalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
|
||||
|
||||
@@ -103,7 +103,7 @@ proc checkConversionBetweenObjects(info: TLineInfo, castDest, src: PType) =
|
||||
proc checkConvertible(info: TLineInfo, castDest, src: PType) =
|
||||
const
|
||||
IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyFloat128}
|
||||
if sameType(castDest, src):
|
||||
if sameType(castDest, src) and castDest.sym == src.sym:
|
||||
# don't annoy conversions that may be needed on another processor:
|
||||
if not (castDest.kind in {tyInt..tyFloat128, tyNil}):
|
||||
Message(info, hintConvFromXtoItselfNotNeeded, typeToString(castDest))
|
||||
|
||||
@@ -50,16 +50,19 @@ proc equalParams*(a, b: PNode): TParamsEquality
|
||||
proc isOrdinalType*(t: PType): bool
|
||||
proc enumHasHoles*(t: PType): bool
|
||||
const
|
||||
abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal}
|
||||
abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal}
|
||||
abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal}
|
||||
abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal}
|
||||
abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal}
|
||||
abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal,
|
||||
tyConst, tyMutable}
|
||||
abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal,
|
||||
tyConst, tyMutable}
|
||||
abstractRange* = {tyGenericInst, tyRange, tyDistinct, tyOrdinal,
|
||||
tyConst, tyMutable}
|
||||
abstractVarRange* = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal,
|
||||
tyConst, tyMutable}
|
||||
abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable}
|
||||
|
||||
skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst}
|
||||
skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyConst, tyMutable}
|
||||
|
||||
proc skipTypes*(t: PType, kinds: TTypeKinds): PType
|
||||
proc elemType*(t: PType): PType
|
||||
proc containsObject*(t: PType): bool
|
||||
proc containsGarbageCollectedRef*(typ: PType): bool
|
||||
proc containsHiddenPointer*(typ: PType): bool
|
||||
@@ -122,10 +125,11 @@ proc getOrdValue(n: PNode): biggestInt =
|
||||
result = 0
|
||||
|
||||
proc isCompatibleToCString(a: PType): bool =
|
||||
result = false
|
||||
if a.kind == tyArray:
|
||||
if (firstOrd(a.sons[0]) == 0) and
|
||||
(skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and
|
||||
(skipTypes(a.sons[0], {tyRange, tyConst,
|
||||
tyMutable, tyGenericInst}).kind in
|
||||
{tyInt..tyInt64, tyUInt..tyUInt64}) and
|
||||
(a.sons[1].kind == tyChar):
|
||||
result = true
|
||||
|
||||
@@ -142,7 +146,7 @@ proc getProcHeader(sym: PSym): string =
|
||||
add(result, ')')
|
||||
if n.sons[0].typ != nil: result.add(": " & typeToString(n.sons[0].typ))
|
||||
|
||||
proc elemType(t: PType): PType =
|
||||
proc elemType*(t: PType): PType =
|
||||
assert(t != nil)
|
||||
case t.kind
|
||||
of tyGenericInst, tyDistinct: result = elemType(lastSon(t))
|
||||
@@ -153,36 +157,7 @@ proc elemType(t: PType): PType =
|
||||
proc skipGeneric(t: PType): PType =
|
||||
result = t
|
||||
while result.kind == tyGenericInst: result = lastSon(result)
|
||||
|
||||
proc skipRange(t: PType): PType =
|
||||
result = t
|
||||
while result.kind == tyRange: result = base(result)
|
||||
|
||||
proc skipAbstract(t: PType): PType =
|
||||
result = t
|
||||
while result.kind in {tyRange, tyGenericInst}: result = lastSon(result)
|
||||
|
||||
proc skipVar(t: PType): PType =
|
||||
result = t
|
||||
while result.kind == tyVar: result = result.sons[0]
|
||||
|
||||
proc skipVarGeneric(t: PType): PType =
|
||||
result = t
|
||||
while result.kind in {tyGenericInst, tyVar}: result = lastSon(result)
|
||||
|
||||
proc skipPtrsGeneric(t: PType): PType =
|
||||
result = t
|
||||
while result.kind in {tyGenericInst, tyVar, tyPtr, tyRef}:
|
||||
result = lastSon(result)
|
||||
|
||||
proc skipVarGenericRange(t: PType): PType =
|
||||
result = t
|
||||
while result.kind in {tyGenericInst, tyVar, tyRange}: result = lastSon(result)
|
||||
|
||||
proc skipGenericRange(t: PType): PType =
|
||||
result = t
|
||||
while result.kind in {tyGenericInst, tyVar, tyRange}: result = lastSon(result)
|
||||
|
||||
|
||||
proc skipTypes(t: PType, kinds: TTypeKinds): PType =
|
||||
result = t
|
||||
while result.kind in kinds: result = lastSon(result)
|
||||
@@ -190,18 +165,18 @@ proc skipTypes(t: PType, kinds: TTypeKinds): PType =
|
||||
proc isOrdinalType(t: PType): bool =
|
||||
assert(t != nil)
|
||||
result = (t.Kind in {tyChar, tyInt..tyInt64, tyBool, tyEnum}) or
|
||||
(t.Kind in {tyRange, tyOrdinal}) and isOrdinalType(t.sons[0])
|
||||
(t.Kind in {tyRange, tyOrdinal, tyConst, tyMutable, tyGenericInst}) and
|
||||
isOrdinalType(t.sons[0])
|
||||
|
||||
proc enumHasHoles(t: PType): bool =
|
||||
var b = t
|
||||
while b.kind == tyRange: b = b.sons[0]
|
||||
while b.kind in {tyConst, tyMutable, tyRange, tyGenericInst}: b = b.sons[0]
|
||||
result = b.Kind == tyEnum and tfEnumHasHoles in b.flags
|
||||
|
||||
proc iterOverTypeAux(marker: var TIntSet, t: PType, iter: TTypeIter,
|
||||
closure: PObject): bool
|
||||
proc iterOverNode(marker: var TIntSet, n: PNode, iter: TTypeIter,
|
||||
closure: PObject): bool =
|
||||
result = false
|
||||
if n != nil:
|
||||
case n.kind
|
||||
of nkNone..nkNilLit:
|
||||
@@ -289,9 +264,9 @@ proc containsObject(t: PType): bool =
|
||||
result = searchTypeFor(t, isObjectPredicate)
|
||||
|
||||
proc isObjectWithTypeFieldPredicate(t: PType): bool =
|
||||
result = (t.kind == tyObject) and (t.sons[0] == nil) and
|
||||
not ((t.sym != nil) and (sfPure in t.sym.flags)) and
|
||||
not (tfFinal in t.flags)
|
||||
result = t.kind == tyObject and t.sons[0] == nil and
|
||||
not (t.sym != nil and sfPure in t.sym.flags) and
|
||||
tfFinal notin t.flags
|
||||
|
||||
proc analyseObjectWithTypeFieldAux(t: PType,
|
||||
marker: var TIntSet): TTypeFieldResult =
|
||||
@@ -310,7 +285,7 @@ proc analyseObjectWithTypeFieldAux(t: PType,
|
||||
if res == frHeader: result = frHeader
|
||||
if result == frNone:
|
||||
if isObjectWithTypeFieldPredicate(t): result = frHeader
|
||||
of tyGenericInst, tyDistinct:
|
||||
of tyGenericInst, tyDistinct, tyConst, tyMutable:
|
||||
result = analyseObjectWithTypeFieldAux(lastSon(t), marker)
|
||||
of tyArray, tyArrayConstr, tyTuple:
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
@@ -406,8 +381,6 @@ proc mutateTypeAux(marker: var TIntSet, t: PType, iter: TTypeMutator,
|
||||
if not ContainsOrIncl(marker, t.id):
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
result.sons[i] = mutateTypeAux(marker, result.sons[i], iter, closure)
|
||||
if (result.sons[i] == nil) and (result.kind == tyGenericInst):
|
||||
assert(false)
|
||||
if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure)
|
||||
assert(result != nil)
|
||||
|
||||
@@ -427,7 +400,10 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
"distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple",
|
||||
"set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc",
|
||||
"pointer", "OpenArray[$1]", "string", "CString", "Forward", "int", "int8",
|
||||
"int16", "int32", "int64", "float", "float32", "float64", "float128"]
|
||||
"int16", "int32", "int64", "float", "float32", "float64", "float128",
|
||||
|
||||
"uint", "uint8", "uint16", "uint32", "uint64", "bignum", "const ",
|
||||
"!", "varargs[$1]", "iter[$1]", "proxy[$1]"]
|
||||
var t = typ
|
||||
result = ""
|
||||
if t == nil: return
|
||||
@@ -474,7 +450,7 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
add(result, typeToString(t.sons[i]))
|
||||
if i < sonsLen(t) - 1: add(result, ", ")
|
||||
add(result, ']')
|
||||
of tyPtr, tyRef, tyVar:
|
||||
of tyPtr, tyRef, tyVar, tyMutable, tyConst:
|
||||
result = typeToStr[t.kind] & typeToString(t.sons[0])
|
||||
of tyRange:
|
||||
result = "range " & rangeToStr(t.n)
|
||||
@@ -495,6 +471,8 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
addSep(prag)
|
||||
add(prag, "thread")
|
||||
if len(prag) != 0: add(result, "{." & prag & ".}")
|
||||
of tyVarargs, tyIter, tyProxy:
|
||||
result = typeToStr[t.kind] % typeToString(t.sons[0])
|
||||
else:
|
||||
result = typeToStr[t.kind]
|
||||
|
||||
@@ -528,7 +506,8 @@ proc firstOrd(t: PType): biggestInt =
|
||||
else:
|
||||
assert(t.n.sons[0].kind == nkSym)
|
||||
result = t.n.sons[0].sym.position
|
||||
of tyGenericInst, tyDistinct: result = firstOrd(lastSon(t))
|
||||
of tyGenericInst, tyDistinct, tyConst, tyMutable:
|
||||
result = firstOrd(lastSon(t))
|
||||
else:
|
||||
InternalError("invalid kind for first(" & $t.kind & ')')
|
||||
result = 0
|
||||
@@ -553,7 +532,8 @@ proc lastOrd(t: PType): biggestInt =
|
||||
of tyEnum:
|
||||
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
|
||||
result = t.n.sons[sonsLen(t.n) - 1].sym.position
|
||||
of tyGenericInst, tyDistinct: result = lastOrd(lastSon(t))
|
||||
of tyGenericInst, tyDistinct, tyConst, tyMutable:
|
||||
result = lastOrd(lastSon(t))
|
||||
else:
|
||||
InternalError("invalid kind for last(" & $t.kind & ')')
|
||||
result = 0
|
||||
@@ -561,21 +541,21 @@ proc lastOrd(t: PType): biggestInt =
|
||||
proc lengthOrd(t: PType): biggestInt =
|
||||
case t.kind
|
||||
of tyInt64, tyInt32, tyInt: result = lastOrd(t)
|
||||
of tyDistinct: result = lengthOrd(t.sons[0])
|
||||
of tyDistinct, tyConst, tyMutable: result = lengthOrd(t.sons[0])
|
||||
else: result = lastOrd(t) - firstOrd(t) + 1
|
||||
|
||||
proc equalParam(a, b: PSym): TParamsEquality =
|
||||
if SameTypeOrNil(a.typ, b.typ):
|
||||
if (a.ast == b.ast):
|
||||
if a.ast == b.ast:
|
||||
result = paramsEqual
|
||||
elif (a.ast != nil) and (b.ast != nil):
|
||||
elif a.ast != nil and b.ast != nil:
|
||||
if ExprStructuralEquivalent(a.ast, b.ast): result = paramsEqual
|
||||
else: result = paramsIncompatible
|
||||
elif (a.ast != nil):
|
||||
elif a.ast != nil:
|
||||
result = paramsEqual
|
||||
elif (b.ast != nil):
|
||||
elif b.ast != nil:
|
||||
result = paramsIncompatible
|
||||
else:
|
||||
else:
|
||||
result = paramsNotEqual
|
||||
|
||||
proc equalParams(a, b: PNode): TParamsEquality =
|
||||
@@ -611,11 +591,10 @@ proc SameTypeOrNil(a, b: PType): bool =
|
||||
if a == b:
|
||||
result = true
|
||||
else:
|
||||
if (a == nil) or (b == nil): result = false
|
||||
if a == nil or b == nil: result = false
|
||||
else: result = SameType(a, b)
|
||||
|
||||
proc SameLiteral(x, y: PNode): bool =
|
||||
result = false
|
||||
if x.kind == y.kind:
|
||||
case x.kind
|
||||
of nkCharLit..nkInt64Lit: result = x.intVal == y.intVal
|
||||
@@ -625,7 +604,7 @@ proc SameLiteral(x, y: PNode): bool =
|
||||
|
||||
proc SameRanges(a, b: PNode): bool =
|
||||
result = SameLiteral(a.sons[0], b.sons[0]) and
|
||||
SameLiteral(a.sons[1], b.sons[1])
|
||||
SameLiteral(a.sons[1], b.sons[1])
|
||||
|
||||
proc sameTuple(a, b: PType, DistinctOf: bool): bool =
|
||||
# two tuples are equivalent iff the names, types and positions are the same;
|
||||
@@ -637,7 +616,7 @@ proc sameTuple(a, b: PType, DistinctOf: bool): bool =
|
||||
if DistinctOf: result = equalOrDistinctOf(a.sons[i], b.sons[i])
|
||||
else: result = SameType(a.sons[i], b.sons[i])
|
||||
if not result: return
|
||||
if (a.n != nil) and (b.n != nil):
|
||||
if a.n != nil and b.n != nil:
|
||||
for i in countup(0, sonsLen(a.n) - 1):
|
||||
# check field names:
|
||||
if a.n.sons[i].kind != nkSym: InternalError(a.n.info, "sameTuple")
|
||||
@@ -659,13 +638,14 @@ proc SameType(x, y: PType): bool =
|
||||
return false
|
||||
case a.Kind
|
||||
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
|
||||
tyInt..tyFloat128, tyExpr, tyStmt, tyTypeDesc:
|
||||
tyInt..tyBigNum, tyExpr, tyStmt, tyTypeDesc:
|
||||
result = true
|
||||
of tyEnum, tyForward, tyObject, tyDistinct: result = (a.id == b.id)
|
||||
of tyEnum, tyForward, tyObject, tyDistinct, tyProxy: result = (a.id == b.id)
|
||||
of tyTuple: result = sameTuple(a, b, false)
|
||||
of tyGenericInst: result = sameType(lastSon(a), lastSon(b))
|
||||
of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, tyOrdinal,
|
||||
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc:
|
||||
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc,
|
||||
tyConst, tyMutable, tyVarargs, tyIter:
|
||||
if sonsLen(a) == sonsLen(b):
|
||||
result = true
|
||||
for i in countup(0, sonsLen(a) - 1):
|
||||
@@ -694,13 +674,14 @@ proc equalOrDistinctOf(x, y: PType): bool =
|
||||
return false
|
||||
case a.Kind
|
||||
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
|
||||
tyInt..tyFloat128, tyExpr, tyStmt, tyTypeDesc:
|
||||
tyInt..tyBigNum, tyExpr, tyStmt, tyTypeDesc:
|
||||
result = true
|
||||
of tyEnum, tyForward, tyObject, tyDistinct: result = (a.id == b.id)
|
||||
of tyEnum, tyForward, tyObject, tyDistinct, tyProxy: result = (a.id == b.id)
|
||||
of tyTuple: result = sameTuple(a, b, true)
|
||||
of tyGenericInst: result = equalOrDistinctOf(lastSon(a), lastSon(b))
|
||||
of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, tyOrdinal,
|
||||
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc:
|
||||
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc,
|
||||
tyConst, tyMutable, tyVarargs, tyIter:
|
||||
if sonsLen(a) == sonsLen(b):
|
||||
result = true
|
||||
for i in countup(0, sonsLen(a) - 1):
|
||||
@@ -769,7 +750,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
|
||||
result = false #InternalError('shit found');
|
||||
of tyEmpty, tyNil:
|
||||
result = kind == skConst
|
||||
of tyString, tyBool, tyChar, tyEnum, tyInt..tyFloat128, tyCString, tyPointer:
|
||||
of tyString, tyBool, tyChar, tyEnum, tyInt..tyBigNum, tyCString, tyPointer:
|
||||
result = true
|
||||
of tyOrdinal:
|
||||
result = kind == skParam
|
||||
@@ -778,7 +759,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
|
||||
of tyRange:
|
||||
result = skipTypes(t.sons[0], abstractInst).kind in
|
||||
{tyChar, tyEnum, tyInt..tyFloat128}
|
||||
of tyOpenArray:
|
||||
of tyOpenArray, tyVarargs:
|
||||
result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar)
|
||||
of tySequence:
|
||||
result = (kind != skConst) and typeAllowedAux(marker, t.sons[0], skVar) or
|
||||
@@ -788,7 +769,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
|
||||
t.sons[1].kind == tyEmpty
|
||||
of tyPtr, tyRef:
|
||||
result = typeAllowedAux(marker, t.sons[0], skVar)
|
||||
of tyArrayConstr, tyTuple, tySet:
|
||||
of tyArrayConstr, tyTuple, tySet, tyConst, tyMutable, tyIter, tyProxy:
|
||||
for i in countup(0, sonsLen(t) - 1):
|
||||
result = typeAllowedAux(marker, t.sons[i], kind)
|
||||
if not result: break
|
||||
@@ -856,19 +837,19 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt =
|
||||
return
|
||||
typ.size = - 2 # mark as being computed
|
||||
case typ.kind
|
||||
of tyInt:
|
||||
of tyInt, tyUInt:
|
||||
result = IntSize
|
||||
a = result
|
||||
of tyInt8, tyBool, tyChar:
|
||||
of tyInt8, tyUInt8, tyBool, tyChar:
|
||||
result = 1
|
||||
a = result
|
||||
of tyInt16:
|
||||
of tyInt16, tyUInt16:
|
||||
result = 2
|
||||
a = result
|
||||
of tyInt32, tyFloat32:
|
||||
of tyInt32, tyUInt32, tyFloat32:
|
||||
result = 4
|
||||
a = result
|
||||
of tyInt64, tyFloat64:
|
||||
of tyInt64, tyUInt64, tyFloat64:
|
||||
result = 8
|
||||
a = result
|
||||
of tyFloat:
|
||||
@@ -878,7 +859,8 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt =
|
||||
if typ.callConv == ccClosure: result = 2 * ptrSize
|
||||
else: result = ptrSize
|
||||
a = ptrSize
|
||||
of tyNil, tyCString, tyString, tySequence, tyPtr, tyRef, tyVar, tyOpenArray:
|
||||
of tyNil, tyCString, tyString, tySequence, tyPtr, tyRef, tyVar, tyOpenArray,
|
||||
tyBigNum:
|
||||
result = ptrSize
|
||||
a = result
|
||||
of tyArray, tyArrayConstr:
|
||||
@@ -930,7 +912,8 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt =
|
||||
if result < 0: return
|
||||
if a < maxAlign: a = maxAlign
|
||||
result = align(result, a)
|
||||
of tyGenericInst, tyDistinct, tyGenericBody:
|
||||
of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter,
|
||||
tyProxy:
|
||||
result = computeSizeAux(lastSon(typ), a)
|
||||
else:
|
||||
#internalError("computeSizeAux()")
|
||||
|
||||
@@ -40,6 +40,7 @@ Advanced options:
|
||||
(Nimrod, mangled) identifier pairs
|
||||
--lineDir:on|off generation of #line directive on|off
|
||||
--threadanalysis:on|off turn thread analysis on|off
|
||||
--taintMode:on|off turn taint mode on|off
|
||||
--skipCfg do not read the general configuration file
|
||||
--skipProjCfg do not read the project's configuration file
|
||||
--gc:refc|boehm|none use Nimrod's native GC|Boehm GC|no GC
|
||||
|
||||
@@ -435,8 +435,9 @@ have no side-effect can be used in constant expressions too:
|
||||
|
||||
The rules for compile-time computability are:
|
||||
|
||||
1. Literals are compile-time computable.
|
||||
2. Procedure calls of the form ``p(X)`` are compile-time computable if
|
||||
1. Literals are compile-time computable.
|
||||
2. Type conversions are compile-time computable.
|
||||
3. Procedure calls of the form ``p(X)`` are compile-time computable if
|
||||
``p`` is a proc without side-effects (see the `noSideEffect pragma`_
|
||||
for details) and if ``X`` is a (possibly empty) list of compile-time
|
||||
computable arguments.
|
||||
@@ -1207,7 +1208,7 @@ Void type
|
||||
~~~~~~~~~
|
||||
|
||||
The `void`:idx: type denotes the absense of any type. Parameters of
|
||||
type ``void`` are treated as non-existent, a result ``void`` type means that
|
||||
type ``void`` are treated as non-existent, ``void`` as a return type means that
|
||||
the procedure does not return a value:
|
||||
|
||||
.. code-block:: nimrod
|
||||
@@ -1293,7 +1294,49 @@ algorithm (in pseudo-code) determines type equality:
|
||||
|
||||
Since types are graphs which can have cycles, the above algorithm needs an
|
||||
auxiliary set ``s`` to detect this case.
|
||||
|
||||
|
||||
Type equality modulo type distinction
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following algorithm (in pseudo-code) determines whether two types
|
||||
are equal with no respect to ``distinct`` types. For brevity the cycle check
|
||||
with an auxiliary set ``s`` is omitted:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc typeEqualsOrDistinct(a, b: PType): bool =
|
||||
if a.kind == b.kind:
|
||||
case a.kind
|
||||
of int, intXX, float, floatXX, char, string, cstring, pointer,
|
||||
bool, nil, void:
|
||||
# leaf type: kinds identical; nothing more to check
|
||||
result = true
|
||||
of ref, ptr, var, set, seq, openarray:
|
||||
result = typeEqualsOrDistinct(a.baseType, b.baseType)
|
||||
of range:
|
||||
result = typeEqualsOrDistinct(a.baseType, b.baseType) and
|
||||
(a.rangeA == b.rangeA) and (a.rangeB == b.rangeB)
|
||||
of array:
|
||||
result = typeEqualsOrDistinct(a.baseType, b.baseType) and
|
||||
typeEqualsOrDistinct(a.indexType, b.indexType)
|
||||
of tuple:
|
||||
if a.tupleLen == b.tupleLen:
|
||||
for i in 0..a.tupleLen-1:
|
||||
if not typeEqualsOrDistinct(a[i], b[i]): return false
|
||||
result = true
|
||||
of distinct:
|
||||
result = typeEqualsOrDistinct(a.baseType, b.baseType)
|
||||
of object, enum:
|
||||
result = a == b
|
||||
of proc:
|
||||
result = typeEqualsOrDistinct(a.parameterTuple, b.parameterTuple) and
|
||||
typeEqualsOrDistinct(a.resultType, b.resultType) and
|
||||
a.callingConvention == b.callingConvention
|
||||
elif a.kind == distinct:
|
||||
result = typeEqualsOrDistinct(a.baseType, b)
|
||||
elif b.kind == distinct:
|
||||
result = typeEqualsOrDistinct(a, b.baseType)
|
||||
|
||||
|
||||
Subtype relation
|
||||
~~~~~~~~~~~~~~~~
|
||||
@@ -1323,14 +1366,6 @@ algorithm returns true:
|
||||
# XXX range types?
|
||||
proc isImplicitlyConvertible(a, b: PType): bool =
|
||||
case a.kind
|
||||
of proc:
|
||||
if b.kind == proc:
|
||||
var x = a.parameterTuple
|
||||
var y = b.parameterTuple
|
||||
if x.tupleLen == y.tupleLen:
|
||||
for i in 0.. x.tupleLen-1:
|
||||
if not isSubtype(x[i], y[i]): return false
|
||||
result = isSubType(b.resultType, a.resultType)
|
||||
of int8: result = b.kind in {int16, int32, int64, int}
|
||||
of int16: result = b.kind in {int32, int64, int}
|
||||
of int32: result = b.kind in {int64, int}
|
||||
@@ -1357,10 +1392,9 @@ algorithm returns true:
|
||||
|
||||
proc isExplicitlyConvertible(a, b: PType): bool =
|
||||
if isImplicitlyConvertible(a, b): return true
|
||||
if typeEqualsOrDistinct(a, b): return true
|
||||
if isIntegralType(a) and isIntegralType(b): return true
|
||||
if isSubtype(a, b) or isSubtype(b, a): return true
|
||||
if a.kind == distinct and typeEquals(a.baseType, b): return true
|
||||
if b.kind == distinct and typeEquals(b.baseType, a): return true
|
||||
return false
|
||||
|
||||
The convertible relation can be relaxed by a user-defined type
|
||||
@@ -1379,7 +1413,10 @@ The convertible relation can be relaxed by a user-defined type
|
||||
# you can use the explicit form too
|
||||
x = chr.toInt
|
||||
echo x # => 97
|
||||
|
||||
|
||||
The type conversion ``T(a)`` is an L-value if ``a`` is an L-value and
|
||||
``typeEqualsOrDistinct(T, type(a))`` holds.
|
||||
|
||||
|
||||
Assignment compatibility
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -2852,7 +2889,7 @@ as helpers for macros.
|
||||
|
||||
noReturn pragma
|
||||
---------------
|
||||
The `noreturn`:idx: pragma is used to mark a proc that it never returns.
|
||||
The `noreturn`:idx: pragma is used to mark a proc that never returns.
|
||||
|
||||
|
||||
Acyclic pragma
|
||||
@@ -2930,8 +2967,17 @@ only consist of an assembler statement.
|
||||
error pragma
|
||||
------------
|
||||
The `error`:idx: pragma is used to make the compiler output an error message
|
||||
with the given content. Compilation currently aborts after an error, but this
|
||||
may be changed in later versions.
|
||||
with the given content. Compilation does not necessarily abort after an error
|
||||
though.
|
||||
|
||||
The ``error`` pragma can also be used to
|
||||
annotate a symbol (like an iterator or proc). The *usage* of the symbol then
|
||||
triggers a compile-time error. This is especially useful to rule out that some
|
||||
operation is valid due to overloading and type conversions:
|
||||
|
||||
.. code-block:: nimrod
|
||||
## check that underlying int values are compared and not the pointers:
|
||||
proc `==`(x, y: ptr int): bool {.error.}
|
||||
|
||||
|
||||
fatal pragma
|
||||
@@ -3238,7 +3284,7 @@ Even though Nimrod's `thread`:idx: support and semantics are preliminary,
|
||||
they should be quite usable already. To enable thread support
|
||||
the ``--threads:on`` command line switch needs to be used. The ``system``
|
||||
module then contains several threading primitives.
|
||||
See the `threads <threads.html>`_ and `inboxes <inboxes.html>`_ modules
|
||||
See the `threads <threads.html>`_ and `channels <channels.html>`_ modules
|
||||
for the thread API.
|
||||
|
||||
Nimrod's memory model for threads is quite different than that of other common
|
||||
@@ -3259,7 +3305,7 @@ violations of the `no heap sharing restriction`:idx:\: This restriction implies
|
||||
that it is invalid to construct a data structure that consists of memory
|
||||
allocated from different (thread local) heaps.
|
||||
|
||||
Since the semantic checking of threads requires a whole program analysis,
|
||||
Since the semantic checking of threads requires whole program analysis,
|
||||
it is quite expensive and can be turned off with ``--threadanalysis:off`` to
|
||||
improve compile times.
|
||||
|
||||
|
||||
@@ -36,15 +36,18 @@ type
|
||||
nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch,
|
||||
nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt,
|
||||
nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt,
|
||||
nnkForStmt, nnkWhileStmt, nnkCaseStmt, nnkVarSection,
|
||||
nnkConstSection, nnkConstDef, nnkTypeSection, nnkTypeDef,
|
||||
nnkForStmt, nnkWhileStmt, nnkCaseStmt,
|
||||
nnkVarSection, nnkLetSection, nnkConstSection,
|
||||
nnkConstDef, nnkTypeSection, nnkTypeDef,
|
||||
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
|
||||
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt,
|
||||
nnkDiscardStmt, nnkStmtList, nnkImportStmt, nnkFromStmt,
|
||||
nnkIncludeStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
|
||||
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
|
||||
nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen,
|
||||
nnkRefTy, nnkPtrTy, nnkVarTy, nnkDistinctTy,
|
||||
nnkRefTy, nnkPtrTy, nnkVarTy,
|
||||
nnkConstTy, nnkMutableTy,
|
||||
nnkDistinctTy,
|
||||
nnkProcTy, nnkEnumTy, nnkEnumFieldDef, nnkReturnToken
|
||||
TNimNodeKinds* = set[TNimrodNodeKind]
|
||||
TNimrodTypeKind* = enum
|
||||
@@ -184,7 +187,7 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||
## in a string literal node
|
||||
return newStrLitNode(repr(n))
|
||||
|
||||
proc prettyPrint*(n: PNimrodNode): string {.compileTime.} =
|
||||
proc toLisp*(n: PNimrodNode): string {.compileTime.} =
|
||||
## Convert the AST `n` to a human-readable string
|
||||
##
|
||||
## You can use this as a tool to explore the Nimrod's abstract syntax
|
||||
@@ -204,10 +207,10 @@ proc prettyPrint*(n: PNimrodNode): string {.compileTime.} =
|
||||
of nnkIdent: add(result, $n.ident)
|
||||
of nnkSym, nnkNone: assert false
|
||||
else:
|
||||
add(result, prettyPrint(n[0]))
|
||||
add(result, toLisp(n[0]))
|
||||
for j in 1..n.len-1:
|
||||
add(result, ", ")
|
||||
add(result, prettyPrint(n[j]))
|
||||
add(result, toLisp(n[j]))
|
||||
|
||||
add(result, ")")
|
||||
|
||||
@@ -215,7 +218,7 @@ proc toYaml*(n: PNimrodNode): string {.magic: "AstToYaml".}
|
||||
## Converts the AST `n` to an YAML string
|
||||
##
|
||||
## Provides more detailed, potentially harder to digest information
|
||||
## than `prettyPrint`
|
||||
## than `toLisp`
|
||||
|
||||
proc parseExpr*(s: string) : expr {.magic: "ParseExprToAst".}
|
||||
## Compiles the passed string to its AST representation
|
||||
@@ -226,10 +229,13 @@ proc parseStmt*(s: string) : stmt {.magic: "ParseStmtToAst".}
|
||||
## Expects one or more statements
|
||||
|
||||
proc getAst*(macroOrTemplate: expr): expr {.magic: "ExpandMacroToAst".}
|
||||
## Obtains the AST nodes returned from a macro or template invocation
|
||||
## example:
|
||||
## macro FooMacro() =
|
||||
## var ast = getAst(BarTemplate())
|
||||
## Obtains the AST nodes returned from a macro or template invocation.
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
##
|
||||
## macro FooMacro() =
|
||||
## var ast = getAst(BarTemplate())
|
||||
|
||||
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
|
||||
## checks that `n` is of kind `k`. If this is not the case,
|
||||
|
||||
@@ -107,7 +107,7 @@ proc cgiError*(msg: string) {.noreturn.} =
|
||||
raise e
|
||||
|
||||
proc getEncodedData(allowedMethods: set[TRequestMethod]): string =
|
||||
case getenv("REQUEST_METHOD")
|
||||
case getenv("REQUEST_METHOD").string
|
||||
of "POST":
|
||||
if methodPost notin allowedMethods:
|
||||
cgiError("'REQUEST_METHOD' 'POST' is not supported")
|
||||
@@ -192,131 +192,131 @@ proc validateData*(data: PStringTable, validKeys: openarray[string]) =
|
||||
|
||||
proc getContentLength*(): string =
|
||||
## returns contents of the ``CONTENT_LENGTH`` environment variable
|
||||
return getenv("CONTENT_LENGTH")
|
||||
return getenv("CONTENT_LENGTH").string
|
||||
|
||||
proc getContentType*(): string =
|
||||
## returns contents of the ``CONTENT_TYPE`` environment variable
|
||||
return getenv("CONTENT_Type")
|
||||
return getenv("CONTENT_Type").string
|
||||
|
||||
proc getDocumentRoot*(): string =
|
||||
## returns contents of the ``DOCUMENT_ROOT`` environment variable
|
||||
return getenv("DOCUMENT_ROOT")
|
||||
return getenv("DOCUMENT_ROOT").string
|
||||
|
||||
proc getGatewayInterface*(): string =
|
||||
## returns contents of the ``GATEWAY_INTERFACE`` environment variable
|
||||
return getenv("GATEWAY_INTERFACE")
|
||||
return getenv("GATEWAY_INTERFACE").string
|
||||
|
||||
proc getHttpAccept*(): string =
|
||||
## returns contents of the ``HTTP_ACCEPT`` environment variable
|
||||
return getenv("HTTP_ACCEPT")
|
||||
return getenv("HTTP_ACCEPT").string
|
||||
|
||||
proc getHttpAcceptCharset*(): string =
|
||||
## returns contents of the ``HTTP_ACCEPT_CHARSET`` environment variable
|
||||
return getenv("HTTP_ACCEPT_CHARSET")
|
||||
return getenv("HTTP_ACCEPT_CHARSET").string
|
||||
|
||||
proc getHttpAcceptEncoding*(): string =
|
||||
## returns contents of the ``HTTP_ACCEPT_ENCODING`` environment variable
|
||||
return getenv("HTTP_ACCEPT_ENCODING")
|
||||
return getenv("HTTP_ACCEPT_ENCODING").string
|
||||
|
||||
proc getHttpAcceptLanguage*(): string =
|
||||
## returns contents of the ``HTTP_ACCEPT_LANGUAGE`` environment variable
|
||||
return getenv("HTTP_ACCEPT_LANGUAGE")
|
||||
return getenv("HTTP_ACCEPT_LANGUAGE").string
|
||||
|
||||
proc getHttpConnection*(): string =
|
||||
## returns contents of the ``HTTP_CONNECTION`` environment variable
|
||||
return getenv("HTTP_CONNECTION")
|
||||
return getenv("HTTP_CONNECTION").string
|
||||
|
||||
proc getHttpCookie*(): string =
|
||||
## returns contents of the ``HTTP_COOKIE`` environment variable
|
||||
return getenv("HTTP_COOKIE")
|
||||
return getenv("HTTP_COOKIE").string
|
||||
|
||||
proc getHttpHost*(): string =
|
||||
## returns contents of the ``HTTP_HOST`` environment variable
|
||||
return getenv("HTTP_HOST")
|
||||
return getenv("HTTP_HOST").string
|
||||
|
||||
proc getHttpReferer*(): string =
|
||||
## returns contents of the ``HTTP_REFERER`` environment variable
|
||||
return getenv("HTTP_REFERER")
|
||||
return getenv("HTTP_REFERER").string
|
||||
|
||||
proc getHttpUserAgent*(): string =
|
||||
## returns contents of the ``HTTP_USER_AGENT`` environment variable
|
||||
return getenv("HTTP_USER_AGENT")
|
||||
return getenv("HTTP_USER_AGENT").string
|
||||
|
||||
proc getPathInfo*(): string =
|
||||
## returns contents of the ``PATH_INFO`` environment variable
|
||||
return getenv("PATH_INFO")
|
||||
return getenv("PATH_INFO").string
|
||||
|
||||
proc getPathTranslated*(): string =
|
||||
## returns contents of the ``PATH_TRANSLATED`` environment variable
|
||||
return getenv("PATH_TRANSLATED")
|
||||
return getenv("PATH_TRANSLATED").string
|
||||
|
||||
proc getQueryString*(): string =
|
||||
## returns contents of the ``QUERY_STRING`` environment variable
|
||||
return getenv("QUERY_STRING")
|
||||
return getenv("QUERY_STRING").string
|
||||
|
||||
proc getRemoteAddr*(): string =
|
||||
## returns contents of the ``REMOTE_ADDR`` environment variable
|
||||
return getenv("REMOTE_ADDR")
|
||||
return getenv("REMOTE_ADDR").string
|
||||
|
||||
proc getRemoteHost*(): string =
|
||||
## returns contents of the ``REMOTE_HOST`` environment variable
|
||||
return getenv("REMOTE_HOST")
|
||||
return getenv("REMOTE_HOST").string
|
||||
|
||||
proc getRemoteIdent*(): string =
|
||||
## returns contents of the ``REMOTE_IDENT`` environment variable
|
||||
return getenv("REMOTE_IDENT")
|
||||
return getenv("REMOTE_IDENT").string
|
||||
|
||||
proc getRemotePort*(): string =
|
||||
## returns contents of the ``REMOTE_PORT`` environment variable
|
||||
return getenv("REMOTE_PORT")
|
||||
return getenv("REMOTE_PORT").string
|
||||
|
||||
proc getRemoteUser*(): string =
|
||||
## returns contents of the ``REMOTE_USER`` environment variable
|
||||
return getenv("REMOTE_USER")
|
||||
return getenv("REMOTE_USER").string
|
||||
|
||||
proc getRequestMethod*(): string =
|
||||
## returns contents of the ``REQUEST_METHOD`` environment variable
|
||||
return getenv("REQUEST_METHOD")
|
||||
return getenv("REQUEST_METHOD").string
|
||||
|
||||
proc getRequestURI*(): string =
|
||||
## returns contents of the ``REQUEST_URI`` environment variable
|
||||
return getenv("REQUEST_URI")
|
||||
return getenv("REQUEST_URI").string
|
||||
|
||||
proc getScriptFilename*(): string =
|
||||
## returns contents of the ``SCRIPT_FILENAME`` environment variable
|
||||
return getenv("SCRIPT_FILENAME")
|
||||
return getenv("SCRIPT_FILENAME").string
|
||||
|
||||
proc getScriptName*(): string =
|
||||
## returns contents of the ``SCRIPT_NAME`` environment variable
|
||||
return getenv("SCRIPT_NAME")
|
||||
return getenv("SCRIPT_NAME").string
|
||||
|
||||
proc getServerAddr*(): string =
|
||||
## returns contents of the ``SERVER_ADDR`` environment variable
|
||||
return getenv("SERVER_ADDR")
|
||||
return getenv("SERVER_ADDR").string
|
||||
|
||||
proc getServerAdmin*(): string =
|
||||
## returns contents of the ``SERVER_ADMIN`` environment variable
|
||||
return getenv("SERVER_ADMIN")
|
||||
return getenv("SERVER_ADMIN").string
|
||||
|
||||
proc getServerName*(): string =
|
||||
## returns contents of the ``SERVER_NAME`` environment variable
|
||||
return getenv("SERVER_NAME")
|
||||
return getenv("SERVER_NAME").string
|
||||
|
||||
proc getServerPort*(): string =
|
||||
## returns contents of the ``SERVER_PORT`` environment variable
|
||||
return getenv("SERVER_PORT")
|
||||
return getenv("SERVER_PORT").string
|
||||
|
||||
proc getServerProtocol*(): string =
|
||||
## returns contents of the ``SERVER_PROTOCOL`` environment variable
|
||||
return getenv("SERVER_PROTOCOL")
|
||||
return getenv("SERVER_PROTOCOL").string
|
||||
|
||||
proc getServerSignature*(): string =
|
||||
## returns contents of the ``SERVER_SIGNATURE`` environment variable
|
||||
return getenv("SERVER_SIGNATURE")
|
||||
return getenv("SERVER_SIGNATURE").string
|
||||
|
||||
proc getServerSoftware*(): string =
|
||||
## returns contents of the ``SERVER_SOFTWARE`` environment variable
|
||||
return getenv("SERVER_SOFTWARE")
|
||||
return getenv("SERVER_SOFTWARE").string
|
||||
|
||||
proc setTestData*(keysvalues: openarray[string]) =
|
||||
## fills the appropriate environment variables to test your CGI application.
|
||||
@@ -360,10 +360,10 @@ proc setCookie*(name, value: string) =
|
||||
var
|
||||
gcookies: PStringTable = nil
|
||||
|
||||
proc getCookie*(name: string): string =
|
||||
proc getCookie*(name: string): TaintedString =
|
||||
## Gets a cookie. If no cookie of `name` exists, "" is returned.
|
||||
if gcookies == nil: gcookies = parseCookies(getHttpCookie())
|
||||
result = gcookies[name]
|
||||
result = TaintedString(gcookies[name])
|
||||
|
||||
proc existsCookie*(name: string): bool =
|
||||
## Checks if a cookie of `name` exists.
|
||||
|
||||
@@ -692,7 +692,7 @@ proc findEnvVar(key: string): int =
|
||||
if startsWith(environment[i], temp): return i
|
||||
return -1
|
||||
|
||||
proc getEnv*(key: string): string =
|
||||
proc getEnv*(key: string): TaintedString =
|
||||
## Returns the value of the `environment variable`:idx: named `key`.
|
||||
##
|
||||
## If the variable does not exist, "" is returned. To distinguish
|
||||
@@ -700,11 +700,11 @@ proc getEnv*(key: string): string =
|
||||
## `existsEnv(key)`.
|
||||
var i = findEnvVar(key)
|
||||
if i >= 0:
|
||||
return substr(environment[i], find(environment[i], '=')+1)
|
||||
return TaintedString(substr(environment[i], find(environment[i], '=')+1))
|
||||
else:
|
||||
var env = cgetenv(key)
|
||||
if env == nil: return ""
|
||||
result = $env
|
||||
if env == nil: return TaintedString("")
|
||||
result = TaintedString($env)
|
||||
|
||||
proc existsEnv*(key: string): bool =
|
||||
## Checks whether the environment variable named `key` exists.
|
||||
@@ -733,14 +733,15 @@ proc putEnv*(key, val: string) =
|
||||
if SetEnvironmentVariableA(key, val) == 0'i32:
|
||||
OSError()
|
||||
|
||||
iterator envPairs*(): tuple[key, value: string] =
|
||||
iterator envPairs*(): tuple[key, value: TaintedString] =
|
||||
## Iterate over all `environments variables`:idx:. In the first component
|
||||
## of the tuple is the name of the current variable stored, in the second
|
||||
## its value.
|
||||
getEnvVarsC()
|
||||
for i in 0..high(environment):
|
||||
var p = find(environment[i], '=')
|
||||
yield (substr(environment[i], 0, p-1), substr(environment[i], p+1))
|
||||
yield (TaintedString(substr(environment[i], 0, p-1)),
|
||||
TaintedString(substr(environment[i], p+1)))
|
||||
|
||||
iterator walkFiles*(pattern: string): string =
|
||||
## Iterate over all the files that match the `pattern`. On POSIX this uses
|
||||
@@ -1078,18 +1079,18 @@ proc exclFilePermissions*(filename: string,
|
||||
|
||||
proc getHomeDir*(): string {.rtl, extern: "nos$1".} =
|
||||
## Returns the home directory of the current user.
|
||||
when defined(windows): return getEnv("USERPROFILE") & "\\"
|
||||
else: return getEnv("HOME") & "/"
|
||||
when defined(windows): return string(getEnv("USERPROFILE")) & "\\"
|
||||
else: return string(getEnv("HOME")) & "/"
|
||||
|
||||
proc getConfigDir*(): string {.rtl, extern: "nos$1".} =
|
||||
## Returns the config directory of the current user for applications.
|
||||
when defined(windows): return getEnv("APPDATA") & "\\"
|
||||
else: return getEnv("HOME") & "/.config/"
|
||||
when defined(windows): return string(getEnv("APPDATA")) & "\\"
|
||||
else: return string(getEnv("HOME")) & "/.config/"
|
||||
|
||||
proc getTempDir*(): string {.rtl, extern: "nos$1".} =
|
||||
## Returns the temporary directory of the current user for applications to
|
||||
## save temporary files in.
|
||||
when defined(windows): return getEnv("TEMP") & "\\"
|
||||
when defined(windows): return string(getEnv("TEMP")) & "\\"
|
||||
else: return "/tmp/"
|
||||
|
||||
when defined(windows):
|
||||
@@ -1107,14 +1108,14 @@ when defined(windows):
|
||||
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
|
||||
result = ownArgv.len-1
|
||||
|
||||
proc paramStr*(i: int): string {.rtl, extern: "nos$1".} =
|
||||
proc paramStr*(i: int): TaintedString {.rtl, extern: "nos$1".} =
|
||||
## Returns the `i`-th `command line argument`:idx: given to the
|
||||
## application.
|
||||
##
|
||||
## `i` should be in the range `1..paramCount()`, else
|
||||
## the `EOutOfIndex` exception is raised.
|
||||
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
|
||||
return ownArgv[i]
|
||||
return TaintedString(ownArgv[i])
|
||||
|
||||
elif not defined(createNimRtl):
|
||||
# On Posix, there is no portable way to get the command line from a DLL.
|
||||
@@ -1122,8 +1123,8 @@ elif not defined(createNimRtl):
|
||||
cmdCount {.importc: "cmdCount".}: cint
|
||||
cmdLine {.importc: "cmdLine".}: cstringArray
|
||||
|
||||
proc paramStr*(i: int): string =
|
||||
if i < cmdCount and i >= 0: return $cmdLine[i]
|
||||
proc paramStr*(i: int): TaintedString =
|
||||
if i < cmdCount and i >= 0: return TaintedString($cmdLine[i])
|
||||
raise newException(EInvalidIndex, "invalid index")
|
||||
|
||||
proc paramCount*(): int = return cmdCount-1
|
||||
@@ -1172,13 +1173,14 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1".} =
|
||||
result = "" # error!
|
||||
else:
|
||||
# little heuristic that may work on other POSIX-like systems:
|
||||
result = getEnv("_")
|
||||
result = string(getEnv("_"))
|
||||
if len(result) == 0:
|
||||
result = ParamStr(0) # POSIX guaranties that this contains the executable
|
||||
# as it has been executed by the calling process
|
||||
result = string(ParamStr(0))
|
||||
# POSIX guaranties that this contains the executable
|
||||
# as it has been executed by the calling process
|
||||
if len(result) > 0 and result[0] != DirSep: # not an absolute path?
|
||||
# iterate over any path in the $PATH environment variable
|
||||
for p in split(getEnv("PATH"), {PathSep}):
|
||||
for p in split(string(getEnv("PATH")), {PathSep}):
|
||||
var x = joinPath(p, result)
|
||||
if ExistsFile(x): return x
|
||||
|
||||
@@ -1230,7 +1232,7 @@ proc findExe*(exe: string): string =
|
||||
## is added an ``.exe`` file extension if it has no extension.
|
||||
result = addFileExt(exe, os.exeExt)
|
||||
if ExistsFile(result): return
|
||||
var path = os.getEnv("PATH")
|
||||
var path = string(os.getEnv("PATH"))
|
||||
for candidate in split(path, pathSep):
|
||||
var x = candidate / result
|
||||
if ExistsFile(x): return x
|
||||
|
||||
@@ -48,7 +48,7 @@ when defined(os.ParamCount):
|
||||
else:
|
||||
result.cmd = ""
|
||||
for i in countup(1, ParamCount()):
|
||||
result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i)) & ' '
|
||||
result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i).string) & ' '
|
||||
result.kind = cmdEnd
|
||||
result.key = ""
|
||||
result.val = ""
|
||||
|
||||
@@ -787,6 +787,19 @@ proc compileOption*(option, arg: string): bool {.
|
||||
const
|
||||
hasThreadSupport = compileOption("threads")
|
||||
hasSharedHeap = defined(boehmgc) # don't share heaps; every thread has its own
|
||||
# taintMode = compileOption("taintmode")
|
||||
|
||||
when defined(taintMode):
|
||||
# XXX use a compile time option for it!
|
||||
type TaintedString* = distinct string ## a distinct string type that
|
||||
## is `tainted`:idx:. It is an alias for
|
||||
## ``string`` if the taint mode is not
|
||||
## turned on. Use the ``-d:taintMode``
|
||||
## command line switch to turn the taint
|
||||
## mode on.
|
||||
else:
|
||||
type TaintedString* = string
|
||||
|
||||
|
||||
when hasThreadSupport:
|
||||
{.pragma: rtlThreadVar, threadvar.}
|
||||
@@ -896,11 +909,6 @@ type # these work for most platforms:
|
||||
PInt64* = ptr Int64 ## an alias for ``ptr int64``
|
||||
PInt32* = ptr Int32 ## an alias for ``ptr int32``
|
||||
|
||||
type TOptional*[T] = object
|
||||
case hasValue* : bool
|
||||
of true: value*: T
|
||||
of false: nil
|
||||
|
||||
proc toFloat*(i: int): float {.
|
||||
magic: "ToFloat", noSideEffect, importc: "toFloat".}
|
||||
## converts an integer `i` into a ``float``. If the conversion
|
||||
@@ -1653,7 +1661,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
proc FlushFile*(f: TFile) {.importc: "fflush", noDecl.}
|
||||
## Flushes `f`'s buffer.
|
||||
|
||||
proc readFile*(filename: string): string
|
||||
proc readFile*(filename: string): TaintedString
|
||||
## Opens a file named `filename` for reading. Then reads the
|
||||
## file's content completely into a string and
|
||||
## closes the file afterwards. Returns the string.
|
||||
@@ -1675,7 +1683,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
proc write*(f: TFile, a: openArray[string])
|
||||
## Writes a value to the file `f`. May throw an IO exception.
|
||||
|
||||
proc readLine*(f: TFile): string
|
||||
proc readLine*(f: TFile): TaintedString
|
||||
## reads a line of text from the file `f`. May throw an IO exception.
|
||||
## A line of text may be delimited by ``CR``, ``LF`` or
|
||||
## ``CRLF``. The newline character(s) are not part of the returned string.
|
||||
@@ -1814,22 +1822,22 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
when hasThreadSupport:
|
||||
include "system/channels"
|
||||
|
||||
iterator lines*(filename: string): string =
|
||||
iterator lines*(filename: string): TaintedString =
|
||||
## Iterate over any line in the file named `filename`.
|
||||
## If the file does not exist `EIO` is raised.
|
||||
var f = open(filename)
|
||||
var res = ""
|
||||
while not endOfFile(f):
|
||||
rawReadLine(f, res)
|
||||
yield res
|
||||
yield TaintedString(res)
|
||||
Close(f)
|
||||
|
||||
iterator lines*(f: TFile): string =
|
||||
iterator lines*(f: TFile): TaintedString =
|
||||
## Iterate over any line in the file `f`.
|
||||
var res = ""
|
||||
while not endOfFile(f):
|
||||
rawReadLine(f, res)
|
||||
yield res
|
||||
yield TaintedString(res)
|
||||
|
||||
include "system/assign"
|
||||
include "system/repr"
|
||||
|
||||
@@ -55,8 +55,8 @@ proc rawReadLine(f: TFile, result: var string) =
|
||||
break
|
||||
add result, chr(int(c))
|
||||
|
||||
proc readLine(f: TFile): string =
|
||||
result = ""
|
||||
proc readLine(f: TFile): TaintedString =
|
||||
result = TaintedString("")
|
||||
rawReadLine(f, result)
|
||||
|
||||
proc write(f: TFile, i: int) =
|
||||
@@ -81,7 +81,7 @@ proc write(f: TFile, c: Char) = putc(c, f)
|
||||
proc write(f: TFile, a: openArray[string]) =
|
||||
for x in items(a): write(f, x)
|
||||
|
||||
proc readFile(filename: string): string =
|
||||
proc readFile(filename: string): TaintedString =
|
||||
var f = open(filename)
|
||||
try:
|
||||
var len = getFileSize(f)
|
||||
|
||||
4
todo.txt
4
todo.txt
@@ -2,14 +2,14 @@ Version 0.8.14
|
||||
==============
|
||||
|
||||
- 'let x = y'
|
||||
- threads should not have an inbox per default
|
||||
- T(x) as l-value
|
||||
- fix actors.nim
|
||||
- make threadvar efficient again on linux after testing
|
||||
- fix the 'const' issues
|
||||
- test the sort implementation again
|
||||
- optional indentation for 'case' statement
|
||||
- taint mode
|
||||
- const ptr/ref
|
||||
- {.error.} pragma for proc headers
|
||||
|
||||
|
||||
version 0.9.0
|
||||
|
||||
@@ -45,6 +45,8 @@ Language Additions
|
||||
- The built-in type ``void`` can be used to denote the absense of any type.
|
||||
This is useful in generic code.
|
||||
- Return types may be of the type ``var T`` to return an l-value.
|
||||
- The error pragma can now be used to mark symbols whose *usage* should trigger
|
||||
a compile-time error.
|
||||
|
||||
|
||||
Compiler Additions
|
||||
|
||||
Reference in New Issue
Block a user