code generator supports constant sequences; more consistent compile time evaluation

This commit is contained in:
Araq 2011-10-07 09:02:08 +02:00
commit 42516c0086
18 changed files with 180 additions and 137 deletions

View file

@ -559,9 +559,11 @@ const
tyBool, tyChar, tyEnum, tyArray, tyObject, tyBool, tyChar, tyEnum, tyArray, tyObject,
tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc,
tyPointer, tyPointer,
tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128} tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64}
ConstantDataTypes*: TTypeKinds = {tyArray, tySet, tyTuple} ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet,
tyTuple, tySequence}
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator,
skMacro, skTemplate, skConverter, skStub} skMacro, skTemplate, skConverter, skStub}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst}

View file

@ -1831,6 +1831,7 @@ proc expr(p: BProc, e: PNode, d: var TLoc) =
if sym.loc.r == nil or sym.loc.t == nil: if sym.loc.r == nil or sym.loc.t == nil:
InternalError(e.info, "expr: proc not init " & sym.name.s) InternalError(e.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc) putLocIntoDest(p, d, sym.loc)
of nkMetaNode: expr(p, e.sons[0], d)
else: InternalError(e.info, "expr(" & $e.kind & "); unknown node kind") else: InternalError(e.info, "expr(" & $e.kind & "); unknown node kind")
proc genNamedConstExpr(p: BProc, n: PNode): PRope = proc genNamedConstExpr(p: BProc, n: PNode): PRope =
@ -1845,6 +1846,24 @@ proc genConstSimpleList(p: BProc, n: PNode): PRope =
if length > 0: app(result, genNamedConstExpr(p, n.sons[length - 1])) if length > 0: app(result, genNamedConstExpr(p, n.sons[length - 1]))
appf(result, "}$n") appf(result, "}$n")
proc genConstSeq(p: BProc, n: PNode, t: PType): PRope =
var data = ropef("{{$1, $1}", n.len.toRope)
for i in countup(0, n.len - 1):
appf(data, ",$1$n", [genConstExpr(p, n.sons[i])])
data.app("}")
inc(p.labels)
result = con("CNSTSEQ", p.labels.toRope)
appcg(p.module, cfsData,
"NIM_CONST struct {$n" &
" #TGenericSeq Sup;$n" &
" $1 data[$2];$n" &
"} $3 = $4;$n", [
getTypeDesc(p.module, t.sons[0]), n.len.toRope, result, data])
result = ropef("(($1)&$2)", [getTypeDesc(p.module, t), result])
proc genConstExpr(p: BProc, n: PNode): PRope = proc genConstExpr(p: BProc, n: PNode): PRope =
case n.Kind case n.Kind
of nkHiddenStdConv, nkHiddenSubConv: of nkHiddenStdConv, nkHiddenSubConv:
@ -1855,7 +1874,11 @@ proc genConstExpr(p: BProc, n: PNode): PRope =
result = genRawSetData(cs, int(getSize(n.typ))) result = genRawSetData(cs, int(getSize(n.typ)))
of nkBracket, nkPar: of nkBracket, nkPar:
# XXX: tySequence! # XXX: tySequence!
result = genConstSimpleList(p, n) var t = skipTypes(n.typ, abstractInst)
if t.kind == tySequence:
result = genConstSeq(p, n, t)
else:
result = genConstSimpleList(p, n)
else: else:
var d: TLoc var d: TLoc
initLocExpr(p, n, d) initLocExpr(p, n, d)

View file

@ -67,7 +67,8 @@ proc genConstStmt(p: BProc, t: PNode) =
var c = it.sons[0].sym var c = it.sons[0].sym
if sfFakeConst in c.flags: if sfFakeConst in c.flags:
genSingleVar(p, it) genSingleVar(p, it)
elif c.typ.kind in ConstantDataTypes and not (lfNoDecl in c.loc.flags): elif c.typ.kind in ConstantDataTypes and not (lfNoDecl in c.loc.flags) and
c.ast.len != 0:
# generate the data: # generate the data:
fillLoc(c.loc, locData, c.typ, mangleName(c), OnUnknown) fillLoc(c.loc, locData, c.typ, mangleName(c), OnUnknown)
if sfImportc in c.flags: if sfImportc in c.flags:

View file

@ -1426,6 +1426,7 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) =
of nkCStringToString: convCStrToStr(p, n, r) of nkCStringToString: convCStrToStr(p, n, r)
of nkStmtListExpr: genStmtListExpr(p, n, r) of nkStmtListExpr: genStmtListExpr(p, n, r)
of nkEmpty: nil of nkEmpty: nil
of nkMetaNode: gen(p, n.sons[0], r)
else: InternalError(n.info, "gen: unknown node type: " & $n.kind) else: InternalError(n.info, "gen: unknown node type: " & $n.kind)
var globals: PGlobals var globals: PGlobals

View file

@ -43,7 +43,7 @@ const
evalMaxIterations = 500_000 # max iterations of all loops evalMaxIterations = 500_000 # max iterations of all loops
evalMaxRecDepth = 10_000 # max recursion depth for evaluation evalMaxRecDepth = 10_000 # max recursion depth for evaluation
# Much better: use a timeout! -> Wether code compiles depends on the machine # other idea: use a timeout! -> Wether code compiles depends on the machine
# the compiler runs on then! Bad idea! # the compiler runs on then! Bad idea!
proc newStackFrame*(): PStackFrame = proc newStackFrame*(): PStackFrame =
@ -755,16 +755,14 @@ proc evalRepr(c: PEvalContext, n: PNode): PNode =
result = newStrNodeT(renderTree(result, {renderNoComments}), n) result = newStrNodeT(renderTree(result, {renderNoComments}), n)
proc isEmpty(n: PNode): bool = proc isEmpty(n: PNode): bool =
result = (n != nil) and (n.kind == nkEmpty) result = n != nil and n.kind == nkEmpty
# The lexer marks multi-line strings as residing at the line where they # The lexer marks multi-line strings as residing at the line where they
# are closed. This function returns the line where the string begins # are closed. This function returns the line where the string begins
# Maybe the lexer should mark both the beginning and the end of expressions, # Maybe the lexer should mark both the beginning and the end of expressions,
# then this function could be removed. # then this function could be removed.
proc stringStartingLine(s: PNode): int = proc stringStartingLine(s: PNode): int =
var totalLines = 0 result = s.info.line - countLines(s.strVal)
for ln in splitLines(s.strVal): inc totalLines
result = s.info.line - totalLines
proc evalParseExpr(c: PEvalContext, n: Pnode): Pnode = proc evalParseExpr(c: PEvalContext, n: Pnode): Pnode =
var code = evalAux(c, n.sons[1], {}) var code = evalAux(c, n.sons[1], {})
@ -1069,9 +1067,6 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
dec(gNestedEvals) dec(gNestedEvals)
if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations) if gNestedEvals <= 0: stackTrace(c, n, errTooManyIterations)
case n.kind # atoms: case n.kind # atoms:
of nkMetaNode:
result = copyTree(n.sons[0])
result.typ = n.typ
of nkEmpty: result = n of nkEmpty: result = n
of nkSym: result = evalSym(c, n, flags) of nkSym: result = evalSym(c, n, flags)
of nkType..nkNilLit: result = copyNode(n) # end of atoms of nkType..nkNilLit: result = copyNode(n) # end of atoms
@ -1131,6 +1126,9 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
nkTypeSection, nkTemplateDef, nkConstSection, nkIteratorDef, nkTypeSection, nkTemplateDef, nkConstSection, nkIteratorDef,
nkConverterDef, nkIncludeStmt, nkImportStmt, nkFromStmt: nkConverterDef, nkIncludeStmt, nkImportStmt, nkFromStmt:
nil nil
of nkMetaNode:
result = copyTree(n.sons[0])
result.typ = n.typ
of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr,
nkLambda, nkContinueStmt, nkIdent: nkLambda, nkContinueStmt, nkIdent:
stackTrace(c, n, errCannotInterpretNodeX, $n.kind) stackTrace(c, n, errCannotInterpretNodeX, $n.kind)

View file

@ -55,24 +55,25 @@ proc ParamsTypeCheck(c: PContext, typ: PType) {.inline.} =
GlobalError(typ.n.info, errXisNoType, typeToString(typ)) GlobalError(typ.n.info, errXisNoType, typeToString(typ))
proc semConstExpr(c: PContext, n: PNode): PNode = proc semConstExpr(c: PContext, n: PNode): PNode =
result = semExprWithType(c, n)
if result == nil:
GlobalError(n.info, errConstExprExpected)
return
result = getConstExpr(c.module, result)
if result == nil: GlobalError(n.info, errConstExprExpected)
proc semAndEvalConstExpr(c: PContext, n: PNode): PNode =
var e = semExprWithType(c, n) var e = semExprWithType(c, n)
if e == nil: if e == nil:
GlobalError(n.info, errConstExprExpected) GlobalError(n.info, errConstExprExpected)
return nil return nil
result = getConstExpr(c.module, e) result = getConstExpr(c.module, e)
if result == nil: if result == nil:
#writeln(output, renderTree(n));
result = evalConstExpr(c.module, e) result = evalConstExpr(c.module, e)
if result == nil or result.kind == nkEmpty: if result == nil or result.kind == nkEmpty:
GlobalError(n.info, errConstExprExpected) GlobalError(n.info, errConstExprExpected)
when false:
result = semExprWithType(c, n)
if result == nil:
GlobalError(n.info, errConstExprExpected)
return
result = getConstExpr(c.module, result)
if result == nil: GlobalError(n.info, errConstExprExpected)
proc semAndEvalConstExpr(c: PContext, n: PNode): PNode =
result = semConstExpr(c, n)
include seminst, semcall include seminst, semcall

View file

@ -9,10 +9,6 @@
# this module does the semantic checking for expressions # this module does the semantic checking for expressions
const
ConstAbstractTypes = {tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
tyArrayConstr, tyTuple, tySet}
proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode = proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode =
markUsed(n, s) markUsed(n, s)
pushInfoContext(n.info) pushInfoContext(n.info)
@ -49,6 +45,11 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
result = symChoice(c, n, s) result = symChoice(c, n, s)
proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
result = copyTree(s.ast)
result.typ = s.typ
result.info = n.info
proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
case s.kind case s.kind
of skProc, skMethod, skIterator, skConverter: of skProc, skMethod, skIterator, skConverter:
@ -57,23 +58,24 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
LocalError(n.info, errXCannotBePassedToProcVar, s.name.s) LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
result = symChoice(c, n, s) result = symChoice(c, n, s)
of skConst: of skConst:
#
# Consider::
# const x = []
# proc p(a: openarray[int])
# proc q(a: openarray[char])
# p(x)
# q(x)
#
# It is clear that ``[]`` means two totally different things. Thus, we
# copy `x`'s AST into each context, so that the type fixup phase can
# deal with two different ``[]``.
#
markUsed(n, s) markUsed(n, s)
if s.typ.kind in ConstAbstractTypes: case skipTypes(s.typ, abstractInst).kind
result = copyTree(s.ast) of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
result.typ = s.typ tyTuple, tySet, tyUInt..tyUInt64:
result.info = n.info result = inlineConst(n, s)
of tyArrayConstr, tySequence:
# Consider::
# const x = []
# proc p(a: openarray[int])
# proc q(a: openarray[char])
# p(x)
# q(x)
#
# It is clear that ``[]`` means two totally different things. Thus, we
# copy `x`'s AST into each context, so that the type fixup phase can
# deal with two different ``[]``.
if s.ast.len == 0: result = inlineConst(n, s)
else: result = newSymNode(s, n.info)
else: else:
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
of skMacro: result = semMacroExpr(c, n, s) of skMacro: result = semMacroExpr(c, n, s)

View file

@ -182,9 +182,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n) of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n)
of mInSet: result = newIntNodeT(Ord(inSet(a, b)), n) of mInSet: result = newIntNodeT(Ord(inSet(a, b)), n)
of mRepr: of mRepr:
# BUGFIX: we cannot eval mRepr here. But this means that it is not # BUGFIX: we cannot eval mRepr here for reasons that I forgot.
# available for interpretation. I don't know how to fix this.
#result := newStrNodeT(renderTree(a, {@set}[renderNoComments]), n);
of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n) of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n)
of mBoolToStr: of mBoolToStr:
if getOrdValue(a) == 0: result = newStrNodeT("false", n) if getOrdValue(a) == 0: result = newStrNodeT("false", n)
@ -324,7 +322,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
var s = n.sym var s = n.sym
if s.kind == skEnumField: if s.kind == skEnumField:
result = newIntNodeT(s.position, n) result = newIntNodeT(s.position, n)
elif (s.kind == skConst): elif s.kind == skConst:
case s.magic case s.magic
of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n) of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n)
of mCompileDate: result = newStrNodeT(times.getDateStr(), n) of mCompileDate: result = newStrNodeT(times.getDateStr(), n)
@ -370,10 +368,14 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of mLow: of mLow:
result = newIntNodeT(firstOrd(n.sons[1].typ), n) result = newIntNodeT(firstOrd(n.sons[1].typ), n)
of mHigh: of mHigh:
if not (skipTypes(n.sons[1].typ, abstractVar).kind in if skipTypes(n.sons[1].typ, abstractVar).kind notin
{tyOpenArray, tySequence, tyString}): {tyOpenArray, tySequence, tyString}:
result = newIntNodeT(lastOrd(skipTypes(n.sons[1].typ, abstractVar)), result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n)
n) else:
var a = n.sons[1]
if a.kind == nkBracket:
# we can optimize it away:
result = newIntNodeT(sonsLen(a)-1, n)
of mLengthOpenArray: of mLengthOpenArray:
var a = n.sons[1] var a = n.sons[1]
if a.kind == nkBracket: if a.kind == nkBracket:

View file

@ -280,22 +280,33 @@ proc semConst(c: PContext, n: PNode): PNode =
var typ: PType = nil var typ: PType = nil
if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil) if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil)
var e = semExprWithType(c, a.sons[2]) when true:
if e == nil: GlobalError(a.sons[2].info, errConstExprExpected) var def = semConstExpr(c, a.sons[2])
var def = getConstExpr(c.module, e) if def == nil: GlobalError(a.sons[2].info, errConstExprExpected)
if def == nil: # check type compatibility between def.typ and typ:
v.flags.incl(sfFakeConst) if typ != nil:
def = evalConstExpr(c.module, e) def = fitRemoveHiddenConv(c, typ, def)
if def == nil or def.kind == nkEmpty: def = e else:
# check type compatibility between def.typ and typ: typ = def.typ
if typ != nil: if not typeAllowed(typ, skConst):
def = fitRemoveHiddenConv(c, typ, def)
else:
typ = def.typ
if not typeAllowed(typ, skConst):
v.flags.incl(sfFakeConst)
if not typeAllowed(typ, skVar):
GlobalError(a.info, errXisNoType, typeToString(typ)) GlobalError(a.info, errXisNoType, typeToString(typ))
else:
var e = semExprWithType(c, a.sons[2])
if e == nil: GlobalError(a.sons[2].info, errConstExprExpected)
var def = getConstExpr(c.module, e)
if def == nil:
v.flags.incl(sfFakeConst)
def = evalConstExpr(c.module, e)
if def == nil or def.kind == nkEmpty: def = e
# check type compatibility between def.typ and typ:
if typ != nil:
def = fitRemoveHiddenConv(c, typ, def)
else:
typ = def.typ
if not typeAllowed(typ, skConst):
v.flags.incl(sfFakeConst)
if not typeAllowed(typ, skVar):
GlobalError(a.info, errXisNoType, typeToString(typ))
v.typ = typ v.typ = typ
v.ast = def # no need to copy v.ast = def # no need to copy
addInterfaceDecl(c, v) addInterfaceDecl(c, v)

View file

@ -161,7 +161,6 @@ proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
proc transformSymAux(c: PTransf, n: PNode): PNode = proc transformSymAux(c: PTransf, n: PNode): PNode =
var b: PNode var b: PNode
if (n.kind != nkSym): internalError(n.info, "transformSym")
var tc = c.transCon var tc = c.transCon
if sfBorrow in n.sym.flags: if sfBorrow in n.sym.flags:
# simply exchange the symbol: # simply exchange the symbol:
@ -176,14 +175,15 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
if result != nil: return if result != nil: return
tc = tc.next tc = tc.next
result = b result = b
case b.sym.kind when false:
of skConst, skEnumField: case b.sym.kind
if sfFakeConst notin b.sym.flags: of skConst, skEnumField:
if skipTypes(b.sym.typ, abstractInst).kind notin ConstantDataTypes: if sfFakeConst notin b.sym.flags:
result = getConstExpr(c.module, b) if skipTypes(b.sym.typ, abstractInst).kind notin ConstantDataTypes:
if result == nil: InternalError(b.info, "transformSym: const") result = getConstExpr(c.module, b)
else: if result == nil: InternalError(b.info, "transformSym: const")
nil else:
nil
proc transformSym(c: PTransf, n: PNode): PTransNode = proc transformSym(c: PTransf, n: PNode): PTransNode =
result = PTransNode(transformSymAux(c, n)) result = PTransNode(transformSymAux(c, n))
@ -360,7 +360,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
var dest = skipTypes(n.typ, abstractVarRange) var dest = skipTypes(n.typ, abstractVarRange)
var source = skipTypes(n.sons[1].typ, abstractVarRange) var source = skipTypes(n.sons[1].typ, abstractVarRange)
case dest.kind case dest.kind
of tyInt..tyInt64, tyEnum, tyChar, tyBool: of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt..tyUInt64:
if not isOrdinalType(source): if not isOrdinalType(source):
# XXX int64 -> float conversion? # XXX int64 -> float conversion?
result = transformSons(c, n) result = transformSons(c, n)
@ -659,7 +659,7 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
proc transform(c: PTransf, n: PNode): PTransNode = proc transform(c: PTransf, n: PNode): PTransNode =
case n.kind case n.kind
of nkSym: of nkSym:
return transformSym(c, n) result = transformSym(c, n)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit: of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
# nothing to be done for leaves: # nothing to be done for leaves:
result = PTransNode(n) result = PTransNode(n)
@ -719,7 +719,7 @@ proc transform(c: PTransf, n: PNode): PTransNode =
else: else:
result = transformSons(c, n) result = transformSons(c, n)
var cnst = getConstExpr(c.module, PNode(result)) var cnst = getConstExpr(c.module, PNode(result))
if cnst != nil: if cnst != nil and (cnst.kind != nkBracket or cnst.len == 0):
result = PTransNode(cnst) # do not miss an optimization result = PTransNode(cnst) # do not miss an optimization
proc processTransf(context: PPassContext, n: PNode): PNode = proc processTransf(context: PPassContext, n: PNode): PNode =

View file

@ -762,7 +762,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar) result = (kind == skParam) and typeAllowedAux(marker, t.sons[0], skVar)
of tySequence: of tySequence:
result = (kind != skConst) and typeAllowedAux(marker, t.sons[0], skVar) or result = typeAllowedAux(marker, t.sons[0], skVar) or
t.sons[0].kind == tyEmpty t.sons[0].kind == tyEmpty
of tyArray: of tyArray:
result = typeAllowedAux(marker, t.sons[1], skVar) or result = typeAllowedAux(marker, t.sons[1], skVar) or

View file

@ -417,37 +417,6 @@ The grammar's start symbol is ``module``.
Semantics Semantics
========= =========
Constants
---------
`Constants`:idx: are symbols which are bound to a value. The constant's value
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 which
have no side-effect can be used in constant expressions too:
.. code-block:: nimrod
import strutils
const
constEval = contains("abc", 'b') # computed at compile time!
The rules for compile-time computability are:
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.
Constants cannot be of type ``var`` or ``object``, nor can
they contain such a type. For the types ``ptr`` and ``ref`` only the
constant literal ``nil`` is possible.
Types Types
----- -----
@ -671,8 +640,8 @@ and ``pred`` are not available for them either.
The compiler supports the built-in stringify operator ``$`` for enumerations. The compiler supports the built-in stringify operator ``$`` for enumerations.
The stringify's result can be controlled by specifying the string values to The stringify's result can be controlled by explicitely giving the string
use explicitely: values to use:
.. code-block:: nimrod .. code-block:: nimrod
@ -1521,6 +1490,7 @@ variables of the same type:
If an initializer is given the type can be omitted: the variable is then of the If an initializer is given the type can be omitted: the variable is then of the
same type as the initializing expression. Variables are always initialized same type as the initializing expression. Variables are always initialized
with a default value if there is no initializing expression. The default with a default value if there is no initializing expression. The default
value depends on the type and is always a zero in binary. value depends on the type and is always a zero in binary.
@ -1554,17 +1524,32 @@ Syntax::
| COMMENT | COMMENT
constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop constSection ::= 'const' indPush constDecl (SAD constDecl)* DED indPop
`Constants`:idx: are symbols which are bound to a value. The constant's value
cannot change. The compiler must be able to evaluate the expression in a
constant declaration at compile time.
Example: Nimrod contains a sophisticated compile-time evaluator, so procedures which
have no side-effect can be used in constant expressions too:
.. code-block:: nimrod .. code-block:: nimrod
import strutils
const const
MyFilename = "/home/my/file.txt" constEval = contains("abc", 'b') # computed at compile time!
debugMode: bool = false
The `const`:idx: section declares symbolic constants. A symbolic constant is
a name for a constant expression. Symbolic constants only allow read-access. The rules for compile-time computability are:
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.
Constants cannot be of type ``var`` or ``object``, nor can
they contain such a type. For the types ``ptr`` and ``ref`` only the
constant literal ``nil`` is possible.
If statement If statement
@ -2398,7 +2383,6 @@ Example:
add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and add(root, newNode("hallo")) # instantiates generic procs ``newNode`` and
add(root, newNode("world")) # ``add`` add(root, newNode("world")) # ``add``
for str in inorder(root): for str in inorder(root):
writeln(stdout, str) writeln(stdout, str)
`Generics`:idx: are Nimrod's means to parametrize procs, iterators or types with `Generics`:idx: are Nimrod's means to parametrize procs, iterators or types with
@ -3338,6 +3322,12 @@ improve compile times.
A thread proc is passed to ``createThread`` and invoked indirectly; so the A thread proc is passed to ``createThread`` and invoked indirectly; so the
``thread`` pragma implies ``procvar``. ``thread`` pragma implies ``procvar``.
If a global variable can also be marked with the ``thread`` pragma; it is
a `thead-local`:idx: variable then:
.. code-block:: nimrod
var checkpoints* {.thread.}: seq[string] = @[]
Actor model Actor model
----------- -----------

View file

@ -362,7 +362,7 @@ static N_INLINE(NI32, float32ToInt32)(float val) {
#define STRING_LITERAL(name, str, length) \ #define STRING_LITERAL(name, str, length) \
static const struct { \ static const struct { \
TGenericSeq Sup; \ TGenericSeq Sup; \
NIM_CHAR data[length + 1]; \ NIM_CHAR data[(length) + 1]; \
} name = {{length, length}, str} } name = {{length, length}, str}
typedef struct TStringDesc* string; typedef struct TStringDesc* string;

View file

@ -8,8 +8,8 @@
# #
## This module contains various string utility routines. ## This module contains various string utility routines.
## See the module `re` for regular expression support. ## See the module `re <re.html>`_ for regular expression support.
## See the module `pegs` for PEG support. ## See the module `pegs <pegs.html>`_ for PEG support.
import parseutils import parseutils
@ -369,6 +369,19 @@ proc splitLines*(s: string): seq[string] {.noSideEffect,
## sequence of substrings. ## sequence of substrings.
accumulateResult(splitLines(s)) accumulateResult(splitLines(s))
proc countLines*(s: string): int {.noSideEffect,
rtl, extern: "nsuCountLines".} =
## same as ``len(splitLines(s))``, but much more efficient.
var i = 0
while i < s.len:
case s[i]
of '\c':
if s[i+1] == '\l': inc i
inc result
of '\l': inc result
else: nil
inc i
proc split*(s: string, seps: set[char] = Whitespace): seq[string] {. proc split*(s: string, seps: set[char] = Whitespace): seq[string] {.
noSideEffect, rtl, extern: "nsuSplitCharSet".} = noSideEffect, rtl, extern: "nsuSplitCharSet".} =
## The same as the `split` iterator, but is a proc that returns a ## The same as the `split` iterator, but is a proc that returns a
@ -754,7 +767,7 @@ proc replace*(s: string, sub, by: char): string {.noSideEffect,
proc delete*(s: var string, first, last: int) {.noSideEffect, proc delete*(s: var string, first, last: int) {.noSideEffect,
rtl, extern: "nsuDelete".} = rtl, extern: "nsuDelete".} =
## Deletes in `s` the characters at position `first`..`last`. This modifies ## Deletes in `s` the characters at position `first` .. `last`. This modifies
## `s` itself, it does not return a copy. ## `s` itself, it does not return a copy.
var i = first var i = first
var j = last+1 var j = last+1
@ -958,7 +971,7 @@ proc c_sprintf(buf, frmt: CString) {.nodecl, importc: "sprintf", varargs,
noSideEffect.} noSideEffect.}
type type
TFloatFormat* = enum TFloatFormat* = enum ## the different modes of floating point formating
ffDefault, ## use the shorter floating point notation ffDefault, ## use the shorter floating point notation
ffDecimal, ## use decimal floating point notation ffDecimal, ## use decimal floating point notation
ffScientific ## use scientific notation (using ``e`` character) ffScientific ## use scientific notation (using ``e`` character)

View file

@ -1,5 +1,4 @@
discard """ discard """
disabled: true
""" """
import strutils import strutils

View file

@ -1,10 +1,10 @@
Version 0.8.14 Version 0.8.14
============== ==============
- fix the 'const' issues
- 'let x = y'; const ptr/ref - 'let x = y'; const ptr/ref
- fix actors.nim - fix actors.nim
- make threadvar efficient again on linux after testing - make threadvar efficient again on linux after testing
- fix the 'const' issues
- test the sort implementation again - test the sort implementation again
- optional indentation for 'case' statement - optional indentation for 'case' statement

View file

@ -76,7 +76,7 @@ Library Additions
``system.deallocShared``, ``system.reallocShared``. ``system.deallocShared``, ``system.reallocShared``.
- Added explicit channels for thread communication. - Added explicit channels for thread communication.
- Added ``matchers`` module for email address etc. matching. - Added ``matchers`` module for email address etc. matching.
- Added ``strutils.unindent``. - Added ``strutils.unindent``, ``strutils.countLines``.
- Added ``system.slurp`` for easy resource embedding. - Added ``system.slurp`` for easy resource embedding.
- Added ``system.running`` for threads. - Added ``system.running`` for threads.
- Added ``xmltree.innerText``. - Added ``xmltree.innerText``.