Merge branch 'devel' of github.com:Araq/Nimrod into seq_toString
This commit is contained in:
commit
ffb36db5a6
215 changed files with 9066 additions and 4504 deletions
|
|
@ -62,7 +62,7 @@ type
|
||||||
nkTripleStrLit, # a triple string literal """
|
nkTripleStrLit, # a triple string literal """
|
||||||
nkNilLit, # the nil literal
|
nkNilLit, # the nil literal
|
||||||
# end of atoms
|
# end of atoms
|
||||||
nkMetaNode, # difficult to explain; represents itself
|
nkMetaNode_Obsolete, # difficult to explain; represents itself
|
||||||
# (used for macros)
|
# (used for macros)
|
||||||
nkDotCall, # used to temporarily flag a nkCall node;
|
nkDotCall, # used to temporarily flag a nkCall node;
|
||||||
# this is used
|
# this is used
|
||||||
|
|
@ -188,6 +188,10 @@ type
|
||||||
nkStmtListType, # a statement list ending in a type; for macros
|
nkStmtListType, # a statement list ending in a type; for macros
|
||||||
nkBlockType, # a statement block ending in a type; for macros
|
nkBlockType, # a statement block ending in a type; for macros
|
||||||
# types as syntactic trees:
|
# types as syntactic trees:
|
||||||
|
|
||||||
|
nkWith, # distinct with `foo`
|
||||||
|
nkWithout, # distinct without `foo`
|
||||||
|
|
||||||
nkTypeOfExpr, # type(1+2)
|
nkTypeOfExpr, # type(1+2)
|
||||||
nkObjectTy, # object body
|
nkObjectTy, # object body
|
||||||
nkTupleTy, # tuple body
|
nkTupleTy, # tuple body
|
||||||
|
|
@ -383,6 +387,10 @@ type
|
||||||
# sons[1]: field type
|
# sons[1]: field type
|
||||||
# .n: nkDotExpr storing the field name
|
# .n: nkDotExpr storing the field name
|
||||||
|
|
||||||
|
static:
|
||||||
|
# remind us when TTypeKind stops to fit in a single 64-bit word
|
||||||
|
assert TTypeKind.high.ord <= 63
|
||||||
|
|
||||||
const
|
const
|
||||||
tyPureObject* = tyTuple
|
tyPureObject* = tyTuple
|
||||||
GcTypeKinds* = {tyRef, tySequence, tyString}
|
GcTypeKinds* = {tyRef, tySequence, tyString}
|
||||||
|
|
@ -394,7 +402,7 @@ const
|
||||||
tyUserTypeClass, tyUserTypeClassInst,
|
tyUserTypeClass, tyUserTypeClassInst,
|
||||||
tyAnd, tyOr, tyNot, tyAnything}
|
tyAnd, tyOr, tyNot, tyAnything}
|
||||||
|
|
||||||
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses
|
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses
|
||||||
|
|
||||||
type
|
type
|
||||||
TTypeKinds* = set[TTypeKind]
|
TTypeKinds* = set[TTypeKind]
|
||||||
|
|
@ -409,12 +417,14 @@ type
|
||||||
# efficiency
|
# efficiency
|
||||||
nfTransf, # node has been transformed
|
nfTransf, # node has been transformed
|
||||||
nfSem # node has been checked for semantics
|
nfSem # node has been checked for semantics
|
||||||
nfDelegate # the call can use a delegator
|
nfDotField # the call can use a dot operator
|
||||||
|
nfDotSetter # the call can use a setter dot operarator
|
||||||
|
nfExplicitCall # x.y() was used instead of x.y
|
||||||
nfExprCall # this is an attempt to call a regular expression
|
nfExprCall # this is an attempt to call a regular expression
|
||||||
nfIsRef # this node is a 'ref' node; used for the VM
|
nfIsRef # this node is a 'ref' node; used for the VM
|
||||||
|
|
||||||
TNodeFlags* = set[TNodeFlag]
|
TNodeFlags* = set[TNodeFlag]
|
||||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23)
|
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28)
|
||||||
tfVarargs, # procedure has C styled varargs
|
tfVarargs, # procedure has C styled varargs
|
||||||
tfNoSideEffect, # procedure type does not allow side effects
|
tfNoSideEffect, # procedure type does not allow side effects
|
||||||
tfFinal, # is the object final?
|
tfFinal, # is the object final?
|
||||||
|
|
@ -426,8 +436,11 @@ type
|
||||||
tfFromGeneric, # type is an instantiation of a generic; this is needed
|
tfFromGeneric, # type is an instantiation of a generic; this is needed
|
||||||
# because for instantiations of objects, structural
|
# because for instantiations of objects, structural
|
||||||
# type equality has to be used
|
# type equality has to be used
|
||||||
tfUnresolved, # marks unresolved typedesc params: e.g.
|
tfUnresolved, # marks unresolved typedesc/static params: e.g.
|
||||||
# proc foo(T: typedesc, list: seq[T]): var T
|
# proc foo(T: typedesc, list: seq[T]): var T
|
||||||
|
# proc foo(L: static[int]): array[L, int]
|
||||||
|
# can be attached to ranges to indicate that the range
|
||||||
|
# depends on unresolved static params.
|
||||||
tfRetType, # marks return types in proc (used to detect type classes
|
tfRetType, # marks return types in proc (used to detect type classes
|
||||||
# used as return types for return type inference)
|
# used as return types for return type inference)
|
||||||
tfCapturesEnv, # whether proc really captures some environment
|
tfCapturesEnv, # whether proc really captures some environment
|
||||||
|
|
@ -443,9 +456,16 @@ type
|
||||||
tfHasMeta, # type contains "wildcard" sub-types such as generic params
|
tfHasMeta, # type contains "wildcard" sub-types such as generic params
|
||||||
# or other type classes
|
# or other type classes
|
||||||
tfHasGCedMem, # type contains GC'ed memory
|
tfHasGCedMem, # type contains GC'ed memory
|
||||||
|
tfPacked
|
||||||
tfHasStatic
|
tfHasStatic
|
||||||
tfGenericTypeParam
|
tfGenericTypeParam
|
||||||
tfImplicitTypeParam
|
tfImplicitTypeParam
|
||||||
|
tfWildcard # consider a proc like foo[T, I](x: Type[T, I])
|
||||||
|
# T and I here can bind to both typedesc and static types
|
||||||
|
# before this is determined, we'll consider them to be a
|
||||||
|
# wildcard type.
|
||||||
|
tfGuarded # guarded pointer
|
||||||
|
tfBorrowDot # distinct type borrows '.'
|
||||||
|
|
||||||
TTypeFlags* = set[TTypeFlag]
|
TTypeFlags* = set[TTypeFlag]
|
||||||
|
|
||||||
|
|
@ -467,7 +487,8 @@ type
|
||||||
skResult, # special 'result' variable
|
skResult, # special 'result' variable
|
||||||
skProc, # a proc
|
skProc, # a proc
|
||||||
skMethod, # a method
|
skMethod, # a method
|
||||||
skIterator, # an iterator
|
skIterator, # an inline iterator
|
||||||
|
skClosureIterator, # a resumable closure iterator
|
||||||
skConverter, # a type converter
|
skConverter, # a type converter
|
||||||
skMacro, # a macro
|
skMacro, # a macro
|
||||||
skTemplate, # a template; currently also misused for user-defined
|
skTemplate, # a template; currently also misused for user-defined
|
||||||
|
|
@ -479,12 +500,15 @@ type
|
||||||
skStub, # symbol is a stub and not yet loaded from the ROD
|
skStub, # symbol is a stub and not yet loaded from the ROD
|
||||||
# file (it is loaded on demand, which may
|
# file (it is loaded on demand, which may
|
||||||
# mean: never)
|
# mean: never)
|
||||||
|
skPackage # symbol is a package (used for canonicalization)
|
||||||
TSymKinds* = set[TSymKind]
|
TSymKinds* = set[TSymKind]
|
||||||
|
|
||||||
const
|
const
|
||||||
routineKinds* = {skProc, skMethod, skIterator, skConverter,
|
routineKinds* = {skProc, skMethod, skIterator, skClosureIterator,
|
||||||
skMacro, skTemplate}
|
skConverter, skMacro, skTemplate}
|
||||||
tfIncompleteStruct* = tfVarargs
|
tfIncompleteStruct* = tfVarargs
|
||||||
|
tfUncheckedArray* = tfVarargs
|
||||||
|
tfUnion* = tfNoSideEffect
|
||||||
skError* = skUnknown
|
skError* = skUnknown
|
||||||
|
|
||||||
# type flags that are essential for type equality:
|
# type flags that are essential for type equality:
|
||||||
|
|
@ -687,7 +711,7 @@ type
|
||||||
TSym* {.acyclic.} = object of TIdObj
|
TSym* {.acyclic.} = object of TIdObj
|
||||||
# proc and type instantiations are cached in the generic symbol
|
# proc and type instantiations are cached in the generic symbol
|
||||||
case kind*: TSymKind
|
case kind*: TSymKind
|
||||||
of skType:
|
of skType, skGenericParam:
|
||||||
typeInstCache*: seq[PType]
|
typeInstCache*: seq[PType]
|
||||||
typScope*: PScope
|
typScope*: PScope
|
||||||
of routineKinds:
|
of routineKinds:
|
||||||
|
|
@ -812,13 +836,16 @@ type
|
||||||
counter*: int
|
counter*: int
|
||||||
data*: TObjectSeq
|
data*: TObjectSeq
|
||||||
|
|
||||||
|
TImplication* = enum
|
||||||
|
impUnknown, impNo, impYes
|
||||||
|
|
||||||
# BUGFIX: a module is overloadable so that a proc can have the
|
# BUGFIX: a module is overloadable so that a proc can have the
|
||||||
# same name as an imported module. This is necessary because of
|
# same name as an imported module. This is necessary because of
|
||||||
# the poor naming choices in the standard library.
|
# the poor naming choices in the standard library.
|
||||||
|
|
||||||
const
|
const
|
||||||
OverloadableSyms* = {skProc, skMethod, skIterator, skConverter,
|
OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator,
|
||||||
skModule, skTemplate, skMacro}
|
skConverter, skModule, skTemplate, skMacro}
|
||||||
|
|
||||||
GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody,
|
GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody,
|
||||||
tyGenericParam}
|
tyGenericParam}
|
||||||
|
|
@ -840,10 +867,12 @@ const
|
||||||
tyTuple, tySequence}
|
tyTuple, tySequence}
|
||||||
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
|
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
|
||||||
tyProc, tyString, tyError}
|
tyProc, tyString, tyError}
|
||||||
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator,
|
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType,
|
||||||
|
skIterator, skClosureIterator,
|
||||||
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
|
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
|
||||||
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
|
||||||
nfAllConst, nfDelegate, nfIsRef}
|
nfDotSetter, nfDotField,
|
||||||
|
nfAllConst,nfIsRef}
|
||||||
namePos* = 0
|
namePos* = 0
|
||||||
patternPos* = 1 # empty except for term rewriting macros
|
patternPos* = 1 # empty except for term rewriting macros
|
||||||
genericParamsPos* = 2
|
genericParamsPos* = 2
|
||||||
|
|
@ -857,6 +886,7 @@ const
|
||||||
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
|
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
|
||||||
nkCommand, nkCallStrLit, nkHiddenCallConv}
|
nkCommand, nkCallStrLit, nkHiddenCallConv}
|
||||||
|
|
||||||
|
nkLiterals* = {nkCharLit..nkTripleStrLit}
|
||||||
nkLambdaKinds* = {nkLambda, nkDo}
|
nkLambdaKinds* = {nkLambda, nkDo}
|
||||||
declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef}
|
declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef}
|
||||||
procDefs* = nkLambdaKinds + declarativeDefs
|
procDefs* = nkLambdaKinds + declarativeDefs
|
||||||
|
|
@ -865,7 +895,10 @@ const
|
||||||
nkStrKinds* = {nkStrLit..nkTripleStrLit}
|
nkStrKinds* = {nkStrLit..nkTripleStrLit}
|
||||||
|
|
||||||
skLocalVars* = {skVar, skLet, skForVar, skParam, skResult}
|
skLocalVars* = {skVar, skLet, skForVar, skParam, skResult}
|
||||||
skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skMethod, skConverter}
|
skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skClosureIterator,
|
||||||
|
skMethod, skConverter}
|
||||||
|
|
||||||
|
skIterators* = {skIterator, skClosureIterator}
|
||||||
|
|
||||||
lfFullExternalName* = lfParamCopy # \
|
lfFullExternalName* = lfParamCopy # \
|
||||||
# only used when 'gCmd == cmdPretty': Indicates that the symbol has been
|
# only used when 'gCmd == cmdPretty': Indicates that the symbol has been
|
||||||
|
|
@ -946,7 +979,9 @@ var emptyNode* = newNode(nkEmpty)
|
||||||
# There is a single empty node that is shared! Do not overwrite it!
|
# There is a single empty node that is shared! Do not overwrite it!
|
||||||
|
|
||||||
proc isMetaType*(t: PType): bool =
|
proc isMetaType*(t: PType): bool =
|
||||||
return t.kind in tyMetaTypes or tfHasMeta in t.flags
|
return t.kind in tyMetaTypes or
|
||||||
|
(t.kind == tyStatic and t.n == nil) or
|
||||||
|
tfHasMeta in t.flags
|
||||||
|
|
||||||
proc linkTo*(t: PType, s: PSym): PType {.discardable.} =
|
proc linkTo*(t: PType, s: PSym): PType {.discardable.} =
|
||||||
t.sym = s
|
t.sym = s
|
||||||
|
|
@ -1008,7 +1043,7 @@ proc discardSons(father: PNode) =
|
||||||
father.sons = nil
|
father.sons = nil
|
||||||
|
|
||||||
when defined(useNodeIds):
|
when defined(useNodeIds):
|
||||||
const nodeIdToDebug = 612777 # 612794
|
const nodeIdToDebug* = 482228 # 612794
|
||||||
#612840 # 612905 # 614635 # 614637 # 614641
|
#612840 # 612905 # 614635 # 614637 # 614641
|
||||||
# 423408
|
# 423408
|
||||||
#429107 # 430443 # 441048 # 441090 # 441153
|
#429107 # 430443 # 441048 # 441090 # 441153
|
||||||
|
|
@ -1044,6 +1079,10 @@ proc newStrNode(kind: TNodeKind, strVal: string): PNode =
|
||||||
result = newNode(kind)
|
result = newNode(kind)
|
||||||
result.strVal = strVal
|
result.strVal = strVal
|
||||||
|
|
||||||
|
proc withInfo*(n: PNode, info: TLineInfo): PNode =
|
||||||
|
n.info = info
|
||||||
|
return n
|
||||||
|
|
||||||
proc newIdentNode(ident: PIdent, info: TLineInfo): PNode =
|
proc newIdentNode(ident: PIdent, info: TLineInfo): PNode =
|
||||||
result = newNode(nkIdent)
|
result = newNode(nkIdent)
|
||||||
result.ident = ident
|
result.ident = ident
|
||||||
|
|
@ -1105,10 +1144,6 @@ proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
|
||||||
result.info = info
|
result.info = info
|
||||||
result.typ = typ
|
result.typ = typ
|
||||||
|
|
||||||
proc newMetaNodeIT*(tree: PNode, info: TLineInfo, typ: PType): PNode =
|
|
||||||
result = newNodeIT(nkMetaNode, info, typ)
|
|
||||||
result.add(tree)
|
|
||||||
|
|
||||||
var emptyParams = newNode(nkFormalParams)
|
var emptyParams = newNode(nkFormalParams)
|
||||||
emptyParams.addSon(emptyNode)
|
emptyParams.addSon(emptyNode)
|
||||||
|
|
||||||
|
|
@ -1276,7 +1311,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
||||||
proc propagateToOwner*(owner, elem: PType) =
|
proc propagateToOwner*(owner, elem: PType) =
|
||||||
const HaveTheirOwnEmpty = {tySequence, tySet}
|
const HaveTheirOwnEmpty = {tySequence, tySet}
|
||||||
owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta,
|
owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta,
|
||||||
tfHasStatic, tfHasGCedMem})
|
tfHasGCedMem})
|
||||||
if tfNotNil in elem.flags:
|
if tfNotNil in elem.flags:
|
||||||
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}:
|
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}:
|
||||||
owner.flags.incl tfNotNil
|
owner.flags.incl tfNotNil
|
||||||
|
|
@ -1290,12 +1325,9 @@ proc propagateToOwner*(owner, elem: PType) =
|
||||||
if tfShared in elem.flags:
|
if tfShared in elem.flags:
|
||||||
owner.flags.incl tfHasShared
|
owner.flags.incl tfHasShared
|
||||||
|
|
||||||
if elem.kind in tyMetaTypes:
|
if elem.isMetaType:
|
||||||
owner.flags.incl tfHasMeta
|
owner.flags.incl tfHasMeta
|
||||||
|
|
||||||
if elem.kind == tyStatic:
|
|
||||||
owner.flags.incl tfHasStatic
|
|
||||||
|
|
||||||
if elem.kind in {tyString, tyRef, tySequence} or
|
if elem.kind in {tyString, tyRef, tySequence} or
|
||||||
elem.kind == tyProc and elem.callConv == ccClosure:
|
elem.kind == tyProc and elem.callConv == ccClosure:
|
||||||
owner.flags.incl tfHasGCedMem
|
owner.flags.incl tfHasGCedMem
|
||||||
|
|
@ -1475,8 +1507,7 @@ proc originatingModule*(s: PSym): PSym =
|
||||||
while result.kind != skModule: result = result.owner
|
while result.kind != skModule: result = result.owner
|
||||||
|
|
||||||
proc isRoutine*(s: PSym): bool {.inline.} =
|
proc isRoutine*(s: PSym): bool {.inline.} =
|
||||||
result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod,
|
result = s.kind in skProcKinds
|
||||||
skConverter}
|
|
||||||
|
|
||||||
proc hasPattern*(s: PSym): bool {.inline.} =
|
proc hasPattern*(s: PSym): bool {.inline.} =
|
||||||
result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty
|
result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty
|
||||||
|
|
@ -1484,6 +1515,9 @@ proc hasPattern*(s: PSym): bool {.inline.} =
|
||||||
iterator items*(n: PNode): PNode =
|
iterator items*(n: PNode): PNode =
|
||||||
for i in 0.. <n.len: yield n.sons[i]
|
for i in 0.. <n.len: yield n.sons[i]
|
||||||
|
|
||||||
|
iterator pairs*(n: PNode): tuple[i: int, n: PNode] =
|
||||||
|
for i in 0.. <n.len: yield (i, n.sons[i])
|
||||||
|
|
||||||
proc isAtom*(n: PNode): bool {.inline.} =
|
proc isAtom*(n: PNode): bool {.inline.} =
|
||||||
result = n.kind >= nkNone and n.kind <= nkNilLit
|
result = n.kind >= nkNone and n.kind <= nkNilLit
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,12 @@ proc leValue*(a, b: PNode): bool =
|
||||||
#InternalError(a.info, "leValue")
|
#InternalError(a.info, "leValue")
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
proc weakLeValue*(a, b: PNode): TImplication =
|
||||||
|
if a.kind notin nkLiterals or b.kind notin nkLiterals:
|
||||||
|
result = impUnknown
|
||||||
|
else:
|
||||||
|
result = if leValue(a, b): impYes else: impNo
|
||||||
|
|
||||||
proc lookupInRecord(n: PNode, field: PIdent): PSym =
|
proc lookupInRecord(n: PNode, field: PIdent): PSym =
|
||||||
result = nil
|
result = nil
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -337,7 +343,10 @@ proc treeToYamlAux(n: PNode, marker: var TIntSet, indent: int,
|
||||||
appf(result, ",$N$1\"floatVal\": $2",
|
appf(result, ",$N$1\"floatVal\": $2",
|
||||||
[istr, toRope(n.floatVal.toStrMaxPrecision)])
|
[istr, toRope(n.floatVal.toStrMaxPrecision)])
|
||||||
of nkStrLit..nkTripleStrLit:
|
of nkStrLit..nkTripleStrLit:
|
||||||
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
|
if n.strVal.isNil:
|
||||||
|
appf(result, ",$N$1\"strVal\": null", [istr])
|
||||||
|
else:
|
||||||
|
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
|
||||||
of nkSym:
|
of nkSym:
|
||||||
appf(result, ",$N$1\"sym\": $2",
|
appf(result, ",$N$1\"sym\": $2",
|
||||||
[istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)])
|
[istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)])
|
||||||
|
|
@ -407,7 +416,10 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope =
|
||||||
appf(result, ",$N$1\"floatVal\": $2",
|
appf(result, ",$N$1\"floatVal\": $2",
|
||||||
[istr, toRope(n.floatVal.toStrMaxPrecision)])
|
[istr, toRope(n.floatVal.toStrMaxPrecision)])
|
||||||
of nkStrLit..nkTripleStrLit:
|
of nkStrLit..nkTripleStrLit:
|
||||||
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
|
if n.strVal.isNil:
|
||||||
|
appf(result, ",$N$1\"strVal\": null", [istr])
|
||||||
|
else:
|
||||||
|
appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
|
||||||
of nkSym:
|
of nkSym:
|
||||||
appf(result, ",$N$1\"sym\": $2_$3",
|
appf(result, ",$N$1\"sym\": $2_$3",
|
||||||
[istr, toRope(n.sym.name.s), toRope(n.sym.id)])
|
[istr, toRope(n.sym.name.s), toRope(n.sym.id)])
|
||||||
|
|
|
||||||
|
|
@ -540,7 +540,7 @@ proc typeAtom(p: var TParser): PNode =
|
||||||
if p.tok.s == "unsigned":
|
if p.tok.s == "unsigned":
|
||||||
isUnsigned = true
|
isUnsigned = true
|
||||||
elif p.tok.s == "signed" or p.tok.s == "int":
|
elif p.tok.s == "signed" or p.tok.s == "int":
|
||||||
nil
|
discard
|
||||||
else:
|
else:
|
||||||
add(x, p.tok.s)
|
add(x, p.tok.s)
|
||||||
getTok(p, nil)
|
getTok(p, nil)
|
||||||
|
|
@ -624,10 +624,11 @@ proc parseTypeSuffix(p: var TParser, typ: PNode): PNode =
|
||||||
# array type:
|
# array type:
|
||||||
result = newNodeP(nkBracketExpr, p)
|
result = newNodeP(nkBracketExpr, p)
|
||||||
addSon(result, newIdentNodeP("array", p))
|
addSon(result, newIdentNodeP("array", p))
|
||||||
var r = newNodeP(nkRange, p)
|
#var r = newNodeP(nkRange, p)
|
||||||
addSon(r, newIntNodeP(nkIntLit, 0, p))
|
#addSon(r, newIntNodeP(nkIntLit, 0, p))
|
||||||
addSon(r, newBinary("-", index, newIntNodeP(nkIntLit, 1, p), p))
|
#addSon(r, newBinary("-", index, newIntNodeP(nkIntLit, 1, p), p))
|
||||||
addSon(result, r)
|
#addSon(result, r)
|
||||||
|
addSon(result, index)
|
||||||
addSon(result, tmp)
|
addSon(result, tmp)
|
||||||
else:
|
else:
|
||||||
# pointer type:
|
# pointer type:
|
||||||
|
|
@ -681,11 +682,6 @@ proc parseField(p: var TParser, kind: TNodeKind): PNode =
|
||||||
else: result = mangledIdent(p.tok.s, p)
|
else: result = mangledIdent(p.tok.s, p)
|
||||||
getTok(p, result)
|
getTok(p, result)
|
||||||
|
|
||||||
proc takeOnlyFirstField(p: TParser, isUnion: bool): bool =
|
|
||||||
# if we generate an interface to a header file, *all* fields can be
|
|
||||||
# generated:
|
|
||||||
result = isUnion and p.options.header.len == 0
|
|
||||||
|
|
||||||
proc parseStructBody(p: var TParser, isUnion: bool,
|
proc parseStructBody(p: var TParser, isUnion: bool,
|
||||||
kind: TNodeKind = nkRecList): PNode =
|
kind: TNodeKind = nkRecList): PNode =
|
||||||
result = newNodeP(kind, p)
|
result = newNodeP(kind, p)
|
||||||
|
|
@ -698,8 +694,7 @@ proc parseStructBody(p: var TParser, isUnion: bool,
|
||||||
var i = parseField(p, kind)
|
var i = parseField(p, kind)
|
||||||
t = parseTypeSuffix(p, t)
|
t = parseTypeSuffix(p, t)
|
||||||
addSon(def, i, t, ast.emptyNode)
|
addSon(def, i, t, ast.emptyNode)
|
||||||
if not takeOnlyFirstField(p, isUnion) or sonsLen(result) < 1:
|
addSon(result, def)
|
||||||
addSon(result, def)
|
|
||||||
if p.tok.xkind != pxComma: break
|
if p.tok.xkind != pxComma: break
|
||||||
getTok(p, def)
|
getTok(p, def)
|
||||||
eat(p, pxSemicolon, lastSon(result))
|
eat(p, pxSemicolon, lastSon(result))
|
||||||
|
|
@ -710,11 +705,12 @@ proc structPragmas(p: TParser, name: PNode, origName: string): PNode =
|
||||||
result = newNodeP(nkPragmaExpr, p)
|
result = newNodeP(nkPragmaExpr, p)
|
||||||
addSon(result, exportSym(p, name, origName))
|
addSon(result, exportSym(p, name, origName))
|
||||||
var pragmas = newNodeP(nkPragma, p)
|
var pragmas = newNodeP(nkPragma, p)
|
||||||
addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p))
|
#addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p))
|
||||||
if p.options.header.len > 0:
|
if p.options.header.len > 0:
|
||||||
addSon(pragmas, newIdentStrLitPair("importc", origName, p),
|
addSon(pragmas, newIdentStrLitPair("importc", origName, p),
|
||||||
newIdentStrLitPair("header", p.options.header, p))
|
newIdentStrLitPair("header", p.options.header, p))
|
||||||
addSon(result, pragmas)
|
if pragmas.len > 0: addSon(result, pragmas)
|
||||||
|
else: addSon(result, ast.emptyNode)
|
||||||
|
|
||||||
proc enumPragmas(p: TParser, name: PNode): PNode =
|
proc enumPragmas(p: TParser, name: PNode): PNode =
|
||||||
result = newNodeP(nkPragmaExpr, p)
|
result = newNodeP(nkPragmaExpr, p)
|
||||||
|
|
@ -728,7 +724,11 @@ proc enumPragmas(p: TParser, name: PNode): PNode =
|
||||||
|
|
||||||
proc parseStruct(p: var TParser, isUnion: bool): PNode =
|
proc parseStruct(p: var TParser, isUnion: bool): PNode =
|
||||||
result = newNodeP(nkObjectTy, p)
|
result = newNodeP(nkObjectTy, p)
|
||||||
addSon(result, ast.emptyNode, ast.emptyNode) # no pragmas, no inheritance
|
var pragmas = ast.emptyNode
|
||||||
|
if isUnion:
|
||||||
|
pragmas = newNodeP(nkPragma, p)
|
||||||
|
addSon(pragmas, newIdentNodeP("union", p))
|
||||||
|
addSon(result, pragmas, ast.emptyNode) # no inheritance
|
||||||
if p.tok.xkind == pxCurlyLe:
|
if p.tok.xkind == pxCurlyLe:
|
||||||
addSon(result, parseStructBody(p, isUnion))
|
addSon(result, parseStructBody(p, isUnion))
|
||||||
else:
|
else:
|
||||||
|
|
@ -746,7 +746,7 @@ proc directDeclarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
|
||||||
result = declarator(p, a, ident)
|
result = declarator(p, a, ident)
|
||||||
eat(p, pxParRi, result)
|
eat(p, pxParRi, result)
|
||||||
else:
|
else:
|
||||||
nil
|
discard
|
||||||
return parseTypeSuffix(p, a)
|
return parseTypeSuffix(p, a)
|
||||||
|
|
||||||
proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
|
proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
|
||||||
|
|
@ -1165,7 +1165,7 @@ proc enumSpecifier(p: var TParser): PNode =
|
||||||
|
|
||||||
proc setBaseFlags(n: PNode, base: TNumericalBase) =
|
proc setBaseFlags(n: PNode, base: TNumericalBase) =
|
||||||
case base
|
case base
|
||||||
of base10: nil
|
of base10: discard
|
||||||
of base2: incl(n.flags, nfBase2)
|
of base2: incl(n.flags, nfBase2)
|
||||||
of base8: incl(n.flags, nfBase8)
|
of base8: incl(n.flags, nfBase8)
|
||||||
of base16: incl(n.flags, nfBase16)
|
of base16: incl(n.flags, nfBase16)
|
||||||
|
|
@ -1279,7 +1279,8 @@ proc leftBindingPower(p : var TParser, tok : ref TToken) : int =
|
||||||
of pxComma:
|
of pxComma:
|
||||||
return 10
|
return 10
|
||||||
# throw == 20
|
# throw == 20
|
||||||
of pxAsgn, pxPlusAsgn, pxMinusAsgn, pxStarAsgn, pxSlashAsgn, pxModAsgn, pxShlAsgn, pxShrAsgn, pxAmpAsgn, pxHatAsgn, pxBarAsgn:
|
of pxAsgn, pxPlusAsgn, pxMinusAsgn, pxStarAsgn, pxSlashAsgn, pxModAsgn,
|
||||||
|
pxShlAsgn, pxShrAsgn, pxAmpAsgn, pxHatAsgn, pxBarAsgn:
|
||||||
return 30
|
return 30
|
||||||
of pxConditional:
|
of pxConditional:
|
||||||
return 40
|
return 40
|
||||||
|
|
@ -1686,7 +1687,7 @@ proc switchStatement(p: var TParser): PNode =
|
||||||
break
|
break
|
||||||
of "case", "default":
|
of "case", "default":
|
||||||
break
|
break
|
||||||
else: nil
|
else: discard
|
||||||
addSon(result, statement(p))
|
addSon(result, statement(p))
|
||||||
if sonsLen(result) == 0:
|
if sonsLen(result) == 0:
|
||||||
# translate empty statement list to Nimrod's ``nil`` statement
|
# translate empty statement list to Nimrod's ``nil`` statement
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ proc parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) =
|
||||||
m.body.add(tok)
|
m.body.add(tok)
|
||||||
of pxDirConc:
|
of pxDirConc:
|
||||||
# just ignore this token: this implements token merging correctly
|
# just ignore this token: this implements token merging correctly
|
||||||
nil
|
discard
|
||||||
else:
|
else:
|
||||||
m.body.add(p.tok)
|
m.body.add(p.tok)
|
||||||
# we do not want macro expansion here:
|
# we do not want macro expansion here:
|
||||||
|
|
@ -166,7 +166,7 @@ proc parseStmtList(p: var TParser): PNode =
|
||||||
of pxDirectiveParLe, pxDirective:
|
of pxDirectiveParLe, pxDirective:
|
||||||
case p.tok.s
|
case p.tok.s
|
||||||
of "else", "endif", "elif": break
|
of "else", "endif", "elif": break
|
||||||
else: nil
|
else: discard
|
||||||
addSon(result, statement(p))
|
addSon(result, statement(p))
|
||||||
|
|
||||||
proc eatEndif(p: var TParser) =
|
proc eatEndif(p: var TParser) =
|
||||||
|
|
|
||||||
416
compiler/canonicalizer.nim
Normal file
416
compiler/canonicalizer.nim
Normal file
|
|
@ -0,0 +1,416 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nimrod Compiler
|
||||||
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module implements the canonalization for the various caching mechanisms.
|
||||||
|
|
||||||
|
import strutils, db_sqlite, md5
|
||||||
|
|
||||||
|
var db: TDbConn
|
||||||
|
|
||||||
|
# We *hash* the relevant information into 128 bit hashes. This should be good
|
||||||
|
# enough to prevent any collisions.
|
||||||
|
|
||||||
|
type
|
||||||
|
TUid = distinct MD5Digest
|
||||||
|
|
||||||
|
# For name mangling we encode these hashes via a variant of base64 (called
|
||||||
|
# 'base64a') and prepend the *primary* identifier to ease the debugging pain.
|
||||||
|
# So a signature like:
|
||||||
|
#
|
||||||
|
# proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt)
|
||||||
|
#
|
||||||
|
# is mangled into:
|
||||||
|
# gABI_MTdmOWY5MTQ1MDcyNGQ3ZA
|
||||||
|
#
|
||||||
|
# This is a good compromise between correctness and brevity. ;-)
|
||||||
|
|
||||||
|
const
|
||||||
|
cb64 = [
|
||||||
|
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
||||||
|
"O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z",
|
||||||
|
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
|
||||||
|
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||||
|
"_A", "_B"]
|
||||||
|
|
||||||
|
proc toBase64a(s: cstring, len: int): string =
|
||||||
|
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||||
|
## `newline` is added.
|
||||||
|
result = newStringOfCap(((len + 2) div 3) * 4)
|
||||||
|
var i = 0
|
||||||
|
while i < s.len - 2:
|
||||||
|
let a = ord(s[i])
|
||||||
|
let b = ord(s[i+1])
|
||||||
|
let c = ord(s[i+2])
|
||||||
|
result.add cb64[a shr 2]
|
||||||
|
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||||
|
result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||||
|
result.add cb64[c and 0x3F]
|
||||||
|
inc(i, 3)
|
||||||
|
if i < s.len-1:
|
||||||
|
let a = ord(s[i])
|
||||||
|
let b = ord(s[i+1])
|
||||||
|
result.add cb64[a shr 2]
|
||||||
|
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||||
|
result.add cb64[((b and 0x0F) shl 2)]
|
||||||
|
elif i < s.len:
|
||||||
|
let a = ord(s[i])
|
||||||
|
result.add cb64[a shr 2]
|
||||||
|
result.add cb64[(a and 3) shl 4]
|
||||||
|
|
||||||
|
proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
|
||||||
|
|
||||||
|
proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
|
||||||
|
|
||||||
|
proc hashSym(c: var MD5Context, s: PSym) =
|
||||||
|
if sfAnon in s.flags or s.kind == skGenericParam:
|
||||||
|
c &= ":anon"
|
||||||
|
else:
|
||||||
|
var it = s.owner
|
||||||
|
while it != nil:
|
||||||
|
hashSym(c, it)
|
||||||
|
c &= "."
|
||||||
|
it = s.owner
|
||||||
|
c &= s.name.s
|
||||||
|
|
||||||
|
proc hashTree(c: var MD5Context, n: PNode) =
|
||||||
|
if n == nil:
|
||||||
|
c &= "\255"
|
||||||
|
return
|
||||||
|
var k = n.kind
|
||||||
|
md5Update(c, cast[cstring](addr(k)), 1)
|
||||||
|
# we really must not hash line information. 'n.typ' is debatable but
|
||||||
|
# shouldn't be necessary for now and avoids potential infinite recursions.
|
||||||
|
case n.kind
|
||||||
|
of nkEmpty, nkNilLit, nkType: discard
|
||||||
|
of nkIdent:
|
||||||
|
c &= n.ident.s
|
||||||
|
of nkSym:
|
||||||
|
hashSym(c, n.sym)
|
||||||
|
of nkCharLit..nkUInt64Lit:
|
||||||
|
var v = n.intVal
|
||||||
|
md5Update(c, cast[cstring](addr(v)), sizeof(v))
|
||||||
|
of nkFloatLit..nkFloat64Lit:
|
||||||
|
var v = n.floatVal
|
||||||
|
md5Update(c, cast[cstring](addr(v)), sizeof(v))
|
||||||
|
of nkStrLit..nkTripleStrLit:
|
||||||
|
c &= n.strVal
|
||||||
|
else:
|
||||||
|
for i in 0.. <n.len: hashTree(c, n.sons[i])
|
||||||
|
|
||||||
|
proc hashType(c: var MD5Context, t: PType) =
|
||||||
|
# modelled after 'typeToString'
|
||||||
|
if t == nil:
|
||||||
|
c &= "\254"
|
||||||
|
return
|
||||||
|
|
||||||
|
var k = t.kind
|
||||||
|
md5Update(c, cast[cstring](addr(k)), 1)
|
||||||
|
|
||||||
|
if t.sym != nil and sfAnon notin t.sym.flags:
|
||||||
|
# t.n for literals, but not for e.g. objects!
|
||||||
|
if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
|
||||||
|
c.hashSym(t.sym)
|
||||||
|
|
||||||
|
case t.kind
|
||||||
|
of tyGenericBody, tyGenericInst, tyGenericInvokation:
|
||||||
|
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)):
|
||||||
|
c.hashType t.sons[i]
|
||||||
|
of tyUserTypeClass:
|
||||||
|
internalAssert t.sym != nil and t.sym.owner != nil
|
||||||
|
c &= t.sym.owner.name.s
|
||||||
|
of tyUserTypeClassInst:
|
||||||
|
let body = t.base
|
||||||
|
c.hashSym body.sym
|
||||||
|
for i in countup(1, sonsLen(t) - 2):
|
||||||
|
c.hashType t.sons[i]
|
||||||
|
of tyFromExpr, tyFieldAccessor:
|
||||||
|
c.hashTree(t.n)
|
||||||
|
of tyArrayConstr:
|
||||||
|
c.hashTree(t.sons[0].n)
|
||||||
|
c.hashType(t.sons[1])
|
||||||
|
of tyTuple:
|
||||||
|
if t.n != nil:
|
||||||
|
assert(sonsLen(t.n) == sonsLen(t))
|
||||||
|
for i in countup(0, sonsLen(t.n) - 1):
|
||||||
|
assert(t.n.sons[i].kind == nkSym)
|
||||||
|
c &= t.n.sons[i].sym.name.s
|
||||||
|
c &= ":"
|
||||||
|
c.hashType(t.sons[i])
|
||||||
|
c &= ","
|
||||||
|
else:
|
||||||
|
for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i]
|
||||||
|
of tyRange:
|
||||||
|
c.hashTree(t.n)
|
||||||
|
c.hashType(t.sons[0])
|
||||||
|
of tyProc:
|
||||||
|
c &= (if tfIterator in t.flags: "iterator " else: "proc ")
|
||||||
|
for i in 0.. <t.len: c.hashType(t.sons[i])
|
||||||
|
md5Update(c, cast[cstring](addr(t.callConv)), 1)
|
||||||
|
|
||||||
|
if tfNoSideEffect in t.flags: c &= ".noSideEffect"
|
||||||
|
if tfThread in t.flags: c &= ".thread"
|
||||||
|
else:
|
||||||
|
for i in 0.. <t.len: c.hashType(t.sons[i])
|
||||||
|
if tfShared in t.flags: c &= "shared"
|
||||||
|
if tfNotNil in t.flags: c &= "not nil"
|
||||||
|
|
||||||
|
proc canonConst(n: PNode): TUid =
|
||||||
|
var c: MD5Context
|
||||||
|
md5Init(c)
|
||||||
|
c.hashTree(n)
|
||||||
|
c.hashType(n.typ)
|
||||||
|
md5Final(c, MD5Digest(result))
|
||||||
|
|
||||||
|
proc canonSym(s: PSym): TUid =
|
||||||
|
var c: MD5Context
|
||||||
|
md5Init(c)
|
||||||
|
c.hashSym(s)
|
||||||
|
md5Final(c, MD5Digest(result))
|
||||||
|
|
||||||
|
proc pushType(w: PRodWriter, t: PType) =
|
||||||
|
# check so that the stack does not grow too large:
|
||||||
|
if iiTableGet(w.index.tab, t.id) == InvalidKey:
|
||||||
|
w.tstack.add(t)
|
||||||
|
|
||||||
|
proc pushSym(w: PRodWriter, s: PSym) =
|
||||||
|
# check so that the stack does not grow too large:
|
||||||
|
if iiTableGet(w.index.tab, s.id) == InvalidKey:
|
||||||
|
w.sstack.add(s)
|
||||||
|
|
||||||
|
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||||
|
result: var string) =
|
||||||
|
if n == nil:
|
||||||
|
# nil nodes have to be stored too:
|
||||||
|
result.add("()")
|
||||||
|
return
|
||||||
|
result.add('(')
|
||||||
|
encodeVInt(ord(n.kind), result)
|
||||||
|
# we do not write comments for now
|
||||||
|
# Line information takes easily 20% or more of the filesize! Therefore we
|
||||||
|
# omit line information if it is the same as the father's line information:
|
||||||
|
if fInfo.fileIndex != n.info.fileIndex:
|
||||||
|
result.add('?')
|
||||||
|
encodeVInt(n.info.col, result)
|
||||||
|
result.add(',')
|
||||||
|
encodeVInt(n.info.line, result)
|
||||||
|
result.add(',')
|
||||||
|
encodeVInt(fileIdx(w, toFilename(n.info)), result)
|
||||||
|
elif fInfo.line != n.info.line:
|
||||||
|
result.add('?')
|
||||||
|
encodeVInt(n.info.col, result)
|
||||||
|
result.add(',')
|
||||||
|
encodeVInt(n.info.line, result)
|
||||||
|
elif fInfo.col != n.info.col:
|
||||||
|
result.add('?')
|
||||||
|
encodeVInt(n.info.col, result)
|
||||||
|
var f = n.flags * PersistentNodeFlags
|
||||||
|
if f != {}:
|
||||||
|
result.add('$')
|
||||||
|
encodeVInt(cast[int32](f), result)
|
||||||
|
if n.typ != nil:
|
||||||
|
result.add('^')
|
||||||
|
encodeVInt(n.typ.id, result)
|
||||||
|
pushType(w, n.typ)
|
||||||
|
case n.kind
|
||||||
|
of nkCharLit..nkInt64Lit:
|
||||||
|
if n.intVal != 0:
|
||||||
|
result.add('!')
|
||||||
|
encodeVBiggestInt(n.intVal, result)
|
||||||
|
of nkFloatLit..nkFloat64Lit:
|
||||||
|
if n.floatVal != 0.0:
|
||||||
|
result.add('!')
|
||||||
|
encodeStr($n.floatVal, result)
|
||||||
|
of nkStrLit..nkTripleStrLit:
|
||||||
|
if n.strVal != "":
|
||||||
|
result.add('!')
|
||||||
|
encodeStr(n.strVal, result)
|
||||||
|
of nkIdent:
|
||||||
|
result.add('!')
|
||||||
|
encodeStr(n.ident.s, result)
|
||||||
|
of nkSym:
|
||||||
|
result.add('!')
|
||||||
|
encodeVInt(n.sym.id, result)
|
||||||
|
pushSym(w, n.sym)
|
||||||
|
else:
|
||||||
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
|
encodeNode(w, n.info, n.sons[i], result)
|
||||||
|
add(result, ')')
|
||||||
|
|
||||||
|
proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
|
||||||
|
var oldLen = result.len
|
||||||
|
result.add('<')
|
||||||
|
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
|
||||||
|
if loc.s != low(loc.s):
|
||||||
|
add(result, '*')
|
||||||
|
encodeVInt(ord(loc.s), result)
|
||||||
|
if loc.flags != {}:
|
||||||
|
add(result, '$')
|
||||||
|
encodeVInt(cast[int32](loc.flags), result)
|
||||||
|
if loc.t != nil:
|
||||||
|
add(result, '^')
|
||||||
|
encodeVInt(cast[int32](loc.t.id), result)
|
||||||
|
pushType(w, loc.t)
|
||||||
|
if loc.r != nil:
|
||||||
|
add(result, '!')
|
||||||
|
encodeStr(ropeToStr(loc.r), result)
|
||||||
|
if loc.a != 0:
|
||||||
|
add(result, '?')
|
||||||
|
encodeVInt(loc.a, result)
|
||||||
|
if oldLen + 1 == result.len:
|
||||||
|
# no data was necessary, so remove the '<' again:
|
||||||
|
setLen(result, oldLen)
|
||||||
|
else:
|
||||||
|
add(result, '>')
|
||||||
|
|
||||||
|
proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
||||||
|
if t == nil:
|
||||||
|
# nil nodes have to be stored too:
|
||||||
|
result.add("[]")
|
||||||
|
return
|
||||||
|
# we need no surrounding [] here because the type is in a line of its own
|
||||||
|
if t.kind == tyForward: internalError("encodeType: tyForward")
|
||||||
|
# for the new rodfile viewer we use a preceeding [ so that the data section
|
||||||
|
# can easily be disambiguated:
|
||||||
|
add(result, '[')
|
||||||
|
encodeVInt(ord(t.kind), result)
|
||||||
|
add(result, '+')
|
||||||
|
encodeVInt(t.id, result)
|
||||||
|
if t.n != nil:
|
||||||
|
encodeNode(w, unknownLineInfo(), t.n, result)
|
||||||
|
if t.flags != {}:
|
||||||
|
add(result, '$')
|
||||||
|
encodeVInt(cast[int32](t.flags), result)
|
||||||
|
if t.callConv != low(t.callConv):
|
||||||
|
add(result, '?')
|
||||||
|
encodeVInt(ord(t.callConv), result)
|
||||||
|
if t.owner != nil:
|
||||||
|
add(result, '*')
|
||||||
|
encodeVInt(t.owner.id, result)
|
||||||
|
pushSym(w, t.owner)
|
||||||
|
if t.sym != nil:
|
||||||
|
add(result, '&')
|
||||||
|
encodeVInt(t.sym.id, result)
|
||||||
|
pushSym(w, t.sym)
|
||||||
|
if t.size != - 1:
|
||||||
|
add(result, '/')
|
||||||
|
encodeVBiggestInt(t.size, result)
|
||||||
|
if t.align != 2:
|
||||||
|
add(result, '=')
|
||||||
|
encodeVInt(t.align, result)
|
||||||
|
encodeLoc(w, t.loc, result)
|
||||||
|
for i in countup(0, sonsLen(t) - 1):
|
||||||
|
if t.sons[i] == nil:
|
||||||
|
add(result, "^()")
|
||||||
|
else:
|
||||||
|
add(result, '^')
|
||||||
|
encodeVInt(t.sons[i].id, result)
|
||||||
|
pushType(w, t.sons[i])
|
||||||
|
|
||||||
|
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
|
||||||
|
add(result, '|')
|
||||||
|
encodeVInt(ord(lib.kind), result)
|
||||||
|
add(result, '|')
|
||||||
|
encodeStr(ropeToStr(lib.name), result)
|
||||||
|
add(result, '|')
|
||||||
|
encodeNode(w, info, lib.path, result)
|
||||||
|
|
||||||
|
proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
|
||||||
|
if s == nil:
|
||||||
|
# nil nodes have to be stored too:
|
||||||
|
result.add("{}")
|
||||||
|
return
|
||||||
|
# we need no surrounding {} here because the symbol is in a line of its own
|
||||||
|
encodeVInt(ord(s.kind), result)
|
||||||
|
result.add('+')
|
||||||
|
encodeVInt(s.id, result)
|
||||||
|
result.add('&')
|
||||||
|
encodeStr(s.name.s, result)
|
||||||
|
if s.typ != nil:
|
||||||
|
result.add('^')
|
||||||
|
encodeVInt(s.typ.id, result)
|
||||||
|
pushType(w, s.typ)
|
||||||
|
result.add('?')
|
||||||
|
if s.info.col != -1'i16: encodeVInt(s.info.col, result)
|
||||||
|
result.add(',')
|
||||||
|
if s.info.line != -1'i16: encodeVInt(s.info.line, result)
|
||||||
|
result.add(',')
|
||||||
|
encodeVInt(fileIdx(w, toFilename(s.info)), result)
|
||||||
|
if s.owner != nil:
|
||||||
|
result.add('*')
|
||||||
|
encodeVInt(s.owner.id, result)
|
||||||
|
pushSym(w, s.owner)
|
||||||
|
if s.flags != {}:
|
||||||
|
result.add('$')
|
||||||
|
encodeVInt(cast[int32](s.flags), result)
|
||||||
|
if s.magic != mNone:
|
||||||
|
result.add('@')
|
||||||
|
encodeVInt(ord(s.magic), result)
|
||||||
|
if s.options != w.options:
|
||||||
|
result.add('!')
|
||||||
|
encodeVInt(cast[int32](s.options), result)
|
||||||
|
if s.position != 0:
|
||||||
|
result.add('%')
|
||||||
|
encodeVInt(s.position, result)
|
||||||
|
if s.offset != - 1:
|
||||||
|
result.add('`')
|
||||||
|
encodeVInt(s.offset, result)
|
||||||
|
encodeLoc(w, s.loc, result)
|
||||||
|
if s.annex != nil: encodeLib(w, s.annex, s.info, result)
|
||||||
|
if s.constraint != nil:
|
||||||
|
add(result, '#')
|
||||||
|
encodeNode(w, unknownLineInfo(), s.constraint, result)
|
||||||
|
# lazy loading will soon reload the ast lazily, so the ast needs to be
|
||||||
|
# the last entry of a symbol:
|
||||||
|
if s.ast != nil:
|
||||||
|
# we used to attempt to save space here by only storing a dummy AST if
|
||||||
|
# it is not necessary, but Nimrod's heavy compile-time evaluation features
|
||||||
|
# make that unfeasible nowadays:
|
||||||
|
encodeNode(w, s.info, s.ast, result)
|
||||||
|
|
||||||
|
|
||||||
|
proc createDb() =
|
||||||
|
db.exec(sql"""
|
||||||
|
create table if not exists Module(
|
||||||
|
id integer primary key,
|
||||||
|
name varchar(256) not null,
|
||||||
|
fullpath varchar(256) not null,
|
||||||
|
interfHash varchar(256) not null,
|
||||||
|
fullHash varchar(256) not null,
|
||||||
|
|
||||||
|
created timestamp not null default (DATETIME('now')),
|
||||||
|
);""")
|
||||||
|
|
||||||
|
db.exec(sql"""
|
||||||
|
create table if not exists Symbol(
|
||||||
|
id integer primary key,
|
||||||
|
module integer not null,
|
||||||
|
name varchar(max) not null,
|
||||||
|
data varchar(max) not null,
|
||||||
|
created timestamp not null default (DATETIME('now')),
|
||||||
|
|
||||||
|
foreign key (module) references module(id)
|
||||||
|
);""")
|
||||||
|
|
||||||
|
db.exec(sql"""
|
||||||
|
create table if not exists Type(
|
||||||
|
id integer primary key,
|
||||||
|
module integer not null,
|
||||||
|
name varchar(max) not null,
|
||||||
|
data varchar(max) not null,
|
||||||
|
created timestamp not null default (DATETIME('now')),
|
||||||
|
|
||||||
|
foreign key (module) references module(id)
|
||||||
|
);""")
|
||||||
|
|
||||||
|
|
||||||
|
#db.exec(sql"""
|
||||||
|
# --create unique index if not exists TsstNameIx on TestResult(name);
|
||||||
|
# """, [])
|
||||||
|
|
||||||
|
|
@ -593,7 +593,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
|
|
||||||
proc genDeref(p: BProc, e: PNode, d: var TLoc) =
|
proc genDeref(p: BProc, e: PNode, d: var TLoc) =
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
if mapType(e.sons[0].typ) == ctArray:
|
if mapType(e.sons[0].typ) in {ctArray, ctPtrToArray}:
|
||||||
# XXX the amount of hacks for C's arrays is incredible, maybe we should
|
# XXX the amount of hacks for C's arrays is incredible, maybe we should
|
||||||
# simply wrap them in a struct? --> Losing auto vectorization then?
|
# simply wrap them in a struct? --> Losing auto vectorization then?
|
||||||
expr(p, e.sons[0], d)
|
expr(p, e.sons[0], d)
|
||||||
|
|
@ -736,7 +736,7 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) =
|
||||||
var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs)
|
var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs)
|
||||||
var first = intLiteral(firstOrd(ty))
|
var first = intLiteral(firstOrd(ty))
|
||||||
# emit range check:
|
# emit range check:
|
||||||
if (optBoundsCheck in p.options):
|
if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
|
||||||
if not isConstExpr(e.sons[1]):
|
if not isConstExpr(e.sons[1]):
|
||||||
# semantic pass has already checked for const index expressions
|
# semantic pass has already checked for const index expressions
|
||||||
if firstOrd(ty) == 0:
|
if firstOrd(ty) == 0:
|
||||||
|
|
@ -1803,7 +1803,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
else:
|
else:
|
||||||
genProc(p.module, sym)
|
genProc(p.module, sym)
|
||||||
putLocIntoDest(p, d, sym.loc)
|
putLocIntoDest(p, d, sym.loc)
|
||||||
of skProc, skConverter, skIterator:
|
of skProc, skConverter, skIterators:
|
||||||
genProc(p.module, sym)
|
genProc(p.module, sym)
|
||||||
if sym.loc.r == nil or sym.loc.t == nil:
|
if sym.loc.r == nil or sym.loc.t == nil:
|
||||||
internalError(n.info, "expr: proc not init " & sym.name.s)
|
internalError(n.info, "expr: proc not init " & sym.name.s)
|
||||||
|
|
@ -1915,7 +1915,6 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
internalError(n.info, "expr: proc not init " & sym.name.s)
|
internalError(n.info, "expr: proc not init " & sym.name.s)
|
||||||
putLocIntoDest(p, d, sym.loc)
|
putLocIntoDest(p, d, sym.loc)
|
||||||
of nkClosure: genClosure(p, n, d)
|
of nkClosure: genClosure(p, n, d)
|
||||||
of nkMetaNode: expr(p, n.sons[0], d)
|
|
||||||
|
|
||||||
of nkEmpty: discard
|
of nkEmpty: discard
|
||||||
of nkWhileStmt: genWhileStmt(p, n)
|
of nkWhileStmt: genWhileStmt(p, n)
|
||||||
|
|
|
||||||
|
|
@ -145,33 +145,6 @@ proc atEndMark(buf: cstring, pos: int): bool =
|
||||||
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
|
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
|
||||||
result = s == NimMergeEndMark.len
|
result = s == NimMergeEndMark.len
|
||||||
|
|
||||||
when false:
|
|
||||||
proc readVerbatimSection(L: var TBaseLexer): PRope =
|
|
||||||
var pos = L.bufpos
|
|
||||||
var buf = L.buf
|
|
||||||
result = newMutableRope(30_000)
|
|
||||||
while true:
|
|
||||||
case buf[pos]
|
|
||||||
of CR:
|
|
||||||
pos = nimlexbase.HandleCR(L, pos)
|
|
||||||
buf = L.buf
|
|
||||||
result.data.add(tnl)
|
|
||||||
of LF:
|
|
||||||
pos = nimlexbase.HandleLF(L, pos)
|
|
||||||
buf = L.buf
|
|
||||||
result.data.add(tnl)
|
|
||||||
of '\0':
|
|
||||||
InternalError("ccgmerge: expected: " & NimMergeEndMark)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if atEndMark(buf, pos):
|
|
||||||
inc pos, NimMergeEndMark.len
|
|
||||||
break
|
|
||||||
result.data.add(buf[pos])
|
|
||||||
inc pos
|
|
||||||
L.bufpos = pos
|
|
||||||
freezeMutableRope(result)
|
|
||||||
|
|
||||||
proc readVerbatimSection(L: var TBaseLexer): PRope =
|
proc readVerbatimSection(L: var TBaseLexer): PRope =
|
||||||
var pos = L.bufpos
|
var pos = L.bufpos
|
||||||
var buf = L.buf
|
var buf = L.buf
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ proc startBlock(p: BProc, start: TFormatStr = "{$n",
|
||||||
setLen(p.blocks, result + 1)
|
setLen(p.blocks, result + 1)
|
||||||
p.blocks[result].id = p.labels
|
p.blocks[result].id = p.labels
|
||||||
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
|
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
|
||||||
|
p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16
|
||||||
|
|
||||||
proc assignLabel(b: var TBlock): PRope {.inline.} =
|
proc assignLabel(b: var TBlock): PRope {.inline.} =
|
||||||
b.label = con("LA", b.id.toRope)
|
b.label = con("LA", b.id.toRope)
|
||||||
|
|
@ -260,37 +261,57 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
|
||||||
else: internalError(n.info, "genIf()")
|
else: internalError(n.info, "genIf()")
|
||||||
if sonsLen(n) > 1: fixLabel(p, lend)
|
if sonsLen(n) > 1: fixLabel(p, lend)
|
||||||
|
|
||||||
proc blockLeaveActions(p: BProc, howMany: int) =
|
|
||||||
var L = p.nestedTryStmts.len
|
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||||
# danger of endless recursion! we workaround this here by a temp stack
|
# Called by return and break stmts.
|
||||||
|
# Deals with issues faced when jumping out of try/except/finally stmts,
|
||||||
|
|
||||||
var stack: seq[PNode]
|
var stack: seq[PNode]
|
||||||
newSeq(stack, howMany)
|
newSeq(stack, 0)
|
||||||
for i in countup(1, howMany):
|
|
||||||
stack[i-1] = p.nestedTryStmts[L-i]
|
|
||||||
setLen(p.nestedTryStmts, L-howMany)
|
|
||||||
|
|
||||||
var alreadyPoppedCnt = p.inExceptBlock
|
var alreadyPoppedCnt = p.inExceptBlock
|
||||||
for tryStmt in items(stack):
|
for i in countup(1, howManyTrys):
|
||||||
|
|
||||||
if gCmd != cmdCompileToCpp:
|
if gCmd != cmdCompileToCpp:
|
||||||
|
# Pop safe points generated by try
|
||||||
if alreadyPoppedCnt > 0:
|
if alreadyPoppedCnt > 0:
|
||||||
dec alreadyPoppedCnt
|
dec alreadyPoppedCnt
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts, "#popSafePoint();$n")
|
linefmt(p, cpsStmts, "#popSafePoint();$n")
|
||||||
|
|
||||||
|
# Pop this try-stmt of the list of nested trys
|
||||||
|
# so we don't infinite recurse on it in the next step.
|
||||||
|
var tryStmt = p.nestedTryStmts.pop
|
||||||
|
stack.add(tryStmt)
|
||||||
|
|
||||||
|
# Find finally-stmt for this try-stmt
|
||||||
|
# and generate a copy of its sons
|
||||||
var finallyStmt = lastSon(tryStmt)
|
var finallyStmt = lastSon(tryStmt)
|
||||||
if finallyStmt.kind == nkFinally:
|
if finallyStmt.kind == nkFinally:
|
||||||
genStmts(p, finallyStmt.sons[0])
|
genStmts(p, finallyStmt.sons[0])
|
||||||
|
|
||||||
# push old elements again:
|
# push old elements again:
|
||||||
for i in countdown(howMany-1, 0):
|
for i in countdown(howManyTrys-1, 0):
|
||||||
p.nestedTryStmts.add(stack[i])
|
p.nestedTryStmts.add(stack[i])
|
||||||
|
|
||||||
if gCmd != cmdCompileToCpp:
|
if gCmd != cmdCompileToCpp:
|
||||||
for i in countdown(p.inExceptBlock-1, 0):
|
# Pop exceptions that was handled by the
|
||||||
|
# except-blocks we are in
|
||||||
|
for i in countdown(howManyExcepts-1, 0):
|
||||||
linefmt(p, cpsStmts, "#popCurrentException();$n")
|
linefmt(p, cpsStmts, "#popCurrentException();$n")
|
||||||
|
|
||||||
proc genReturnStmt(p: BProc, t: PNode) =
|
proc genReturnStmt(p: BProc, t: PNode) =
|
||||||
p.beforeRetNeeded = true
|
p.beforeRetNeeded = true
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
|
if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
|
||||||
blockLeaveActions(p, min(1, p.nestedTryStmts.len))
|
blockLeaveActions(p,
|
||||||
|
howManyTrys = p.nestedTryStmts.len,
|
||||||
|
howManyExcepts = p.inExceptBlock)
|
||||||
|
if (p.finallySafePoints.len > 0):
|
||||||
|
# If we're in a finally block, and we came here by exception
|
||||||
|
# consume it before we return.
|
||||||
|
var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
|
||||||
|
linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
|
||||||
lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", [])
|
lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", [])
|
||||||
|
|
||||||
proc genComputedGoto(p: BProc; n: PNode) =
|
proc genComputedGoto(p: BProc; n: PNode) =
|
||||||
|
|
@ -450,7 +471,9 @@ proc genBreakStmt(p: BProc, t: PNode) =
|
||||||
if idx < 0 or not p.blocks[idx].isLoop:
|
if idx < 0 or not p.blocks[idx].isLoop:
|
||||||
internalError(t.info, "no loop to break")
|
internalError(t.info, "no loop to break")
|
||||||
let label = assignLabel(p.blocks[idx])
|
let label = assignLabel(p.blocks[idx])
|
||||||
blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts)
|
blockLeaveActions(p,
|
||||||
|
p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
|
||||||
|
p.inExceptBlock - p.blocks[idx].nestedExceptStmts)
|
||||||
genLineDir(p, t)
|
genLineDir(p, t)
|
||||||
lineF(p, cpsStmts, "goto $1;$n", [label])
|
lineF(p, cpsStmts, "goto $1;$n", [label])
|
||||||
|
|
||||||
|
|
@ -827,7 +850,9 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
|
||||||
discard pop(p.nestedTryStmts)
|
discard pop(p.nestedTryStmts)
|
||||||
endBlock(p) # end of else block
|
endBlock(p) # end of else block
|
||||||
if i < length and t.sons[i].kind == nkFinally:
|
if i < length and t.sons[i].kind == nkFinally:
|
||||||
|
p.finallySafePoints.add(safePoint)
|
||||||
exprBlock(p, t.sons[i].sons[0], d)
|
exprBlock(p, t.sons[i].sons[0], d)
|
||||||
|
discard pop(p.finallySafePoints)
|
||||||
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
|
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
|
||||||
|
|
||||||
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
|
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
|
||||||
|
|
@ -838,7 +863,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
|
||||||
res.add(t.sons[i].strVal)
|
res.add(t.sons[i].strVal)
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var sym = t.sons[i].sym
|
var sym = t.sons[i].sym
|
||||||
if sym.kind in {skProc, skIterator, skMethod}:
|
if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}:
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExpr(p, t.sons[i], a)
|
initLocExpr(p, t.sons[i], a)
|
||||||
res.add(rdLoc(a).ropeToStr)
|
res.add(rdLoc(a).ropeToStr)
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ proc mangleName(s: PSym): PRope =
|
||||||
if result == nil:
|
if result == nil:
|
||||||
if gCmd == cmdCompileToLLVM:
|
if gCmd == cmdCompileToLLVM:
|
||||||
case s.kind
|
case s.kind
|
||||||
of skProc, skMethod, skConverter, skConst, skIterator:
|
of skProc, skMethod, skConverter, skConst, skIterators:
|
||||||
result = ~"@"
|
result = ~"@"
|
||||||
of skVar, skForVar, skResult, skLet:
|
of skVar, skForVar, skResult, skLet:
|
||||||
if sfGlobal in s.flags: result = ~"@"
|
if sfGlobal in s.flags: result = ~"@"
|
||||||
|
|
@ -185,7 +185,7 @@ proc mapType(typ: PType): TCTypeKind =
|
||||||
of tyPtr, tyVar, tyRef:
|
of tyPtr, tyVar, tyRef:
|
||||||
var base = skipTypes(typ.sons[0], typedescInst)
|
var base = skipTypes(typ.sons[0], typedescInst)
|
||||||
case base.kind
|
case base.kind
|
||||||
of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctArray
|
of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctPtrToArray
|
||||||
else: result = ctPtr
|
else: result = ctPtr
|
||||||
of tyPointer: result = ctPtr
|
of tyPointer: result = ctPtr
|
||||||
of tySequence: result = ctNimSeq
|
of tySequence: result = ctNimSeq
|
||||||
|
|
@ -377,9 +377,12 @@ proc getTypePre(m: BModule, typ: PType): PRope =
|
||||||
result = getSimpleTypeDesc(m, typ)
|
result = getSimpleTypeDesc(m, typ)
|
||||||
if result == nil: result = cacheGetType(m.typeCache, typ)
|
if result == nil: result = cacheGetType(m.typeCache, typ)
|
||||||
|
|
||||||
|
proc structOrUnion(t: PType): PRope =
|
||||||
|
(if tfUnion in t.flags: toRope("union") else: toRope("struct"))
|
||||||
|
|
||||||
proc getForwardStructFormat(): string =
|
proc getForwardStructFormat(): string =
|
||||||
if gCmd == cmdCompileToCpp: result = "struct $1;$n"
|
if gCmd == cmdCompileToCpp: result = "$1 $2;$n"
|
||||||
else: result = "typedef struct $1 $1;$n"
|
else: result = "typedef $1 $2 $2;$n"
|
||||||
|
|
||||||
proc getTypeForward(m: BModule, typ: PType): PRope =
|
proc getTypeForward(m: BModule, typ: PType): PRope =
|
||||||
result = cacheGetType(m.forwTypeCache, typ)
|
result = cacheGetType(m.forwTypeCache, typ)
|
||||||
|
|
@ -390,7 +393,8 @@ proc getTypeForward(m: BModule, typ: PType): PRope =
|
||||||
of tySequence, tyTuple, tyObject:
|
of tySequence, tyTuple, tyObject:
|
||||||
result = getTypeName(typ)
|
result = getTypeName(typ)
|
||||||
if not isImportedType(typ):
|
if not isImportedType(typ):
|
||||||
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
|
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
|
||||||
|
[structOrUnion(typ), result])
|
||||||
idTablePut(m.forwTypeCache, typ, result)
|
idTablePut(m.forwTypeCache, typ, result)
|
||||||
else: internalError("getTypeForward(" & $typ.kind & ')')
|
else: internalError("getTypeForward(" & $typ.kind & ')')
|
||||||
|
|
||||||
|
|
@ -445,7 +449,12 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||||
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
|
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
|
||||||
else: ae = sname
|
else: ae = sname
|
||||||
fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
|
fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
|
||||||
appf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname])
|
let fieldType = field.loc.t
|
||||||
|
if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
|
||||||
|
appf(result, "$1 $2[SEQ_DECL_SIZE];$n",
|
||||||
|
[getTypeDescAux(m, fieldType.elemType, check), sname])
|
||||||
|
else:
|
||||||
|
appf(result, "$1 $2;$n", [getTypeDescAux(m, fieldType, check), sname])
|
||||||
else: internalError(n.info, "genRecordFieldsAux()")
|
else: internalError(n.info, "genRecordFieldsAux()")
|
||||||
|
|
||||||
proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope =
|
proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope =
|
||||||
|
|
@ -455,23 +464,33 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
|
||||||
check: var TIntSet): PRope =
|
check: var TIntSet): PRope =
|
||||||
# declare the record:
|
# declare the record:
|
||||||
var hasField = false
|
var hasField = false
|
||||||
|
|
||||||
|
var attribute: PRope =
|
||||||
|
if tfPacked in typ.flags: toRope(CC[ccompiler].packedPragma)
|
||||||
|
else: nil
|
||||||
|
|
||||||
|
result = ropecg(m, CC[ccompiler].structStmtFmt,
|
||||||
|
[structOrUnion(typ), name, attribute])
|
||||||
|
|
||||||
if typ.kind == tyObject:
|
if typ.kind == tyObject:
|
||||||
|
|
||||||
if typ.sons[0] == nil:
|
if typ.sons[0] == nil:
|
||||||
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
|
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
|
||||||
result = ropecg(m, "struct $1 {$n", [name])
|
appcg(m, result, " {$n", [])
|
||||||
else:
|
else:
|
||||||
result = ropecg(m, "struct $1 {$n#TNimType* m_type;$n", [name])
|
appcg(m, result, " {$n#TNimType* m_type;$n", [name, attribute])
|
||||||
hasField = true
|
hasField = true
|
||||||
elif gCmd == cmdCompileToCpp:
|
elif gCmd == cmdCompileToCpp:
|
||||||
result = ropecg(m, "struct $1 : public $2 {$n",
|
appcg(m, result, " : public $1 {$n",
|
||||||
[name, getTypeDescAux(m, typ.sons[0], check)])
|
[getTypeDescAux(m, typ.sons[0], check)])
|
||||||
hasField = true
|
hasField = true
|
||||||
else:
|
else:
|
||||||
result = ropecg(m, "struct $1 {$n $2 Sup;$n",
|
appcg(m, result, " {$n $1 Sup;$n",
|
||||||
[name, getTypeDescAux(m, typ.sons[0], check)])
|
[getTypeDescAux(m, typ.sons[0], check)])
|
||||||
hasField = true
|
hasField = true
|
||||||
else:
|
else:
|
||||||
result = ropef("struct $1 {$n", [name])
|
appf(result, " {$n", [name])
|
||||||
|
|
||||||
var desc = getRecordFields(m, typ, check)
|
var desc = getRecordFields(m, typ, check)
|
||||||
if (desc == nil) and not hasField:
|
if (desc == nil) and not hasField:
|
||||||
appf(result, "char dummy;$n", [])
|
appf(result, "char dummy;$n", [])
|
||||||
|
|
@ -481,7 +500,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
|
||||||
|
|
||||||
proc getTupleDesc(m: BModule, typ: PType, name: PRope,
|
proc getTupleDesc(m: BModule, typ: PType, name: PRope,
|
||||||
check: var TIntSet): PRope =
|
check: var TIntSet): PRope =
|
||||||
result = ropef("struct $1 {$n", [name])
|
result = ropef("$1 $2 {$n", [structOrUnion(typ), name])
|
||||||
var desc: PRope = nil
|
var desc: PRope = nil
|
||||||
for i in countup(0, sonsLen(typ) - 1):
|
for i in countup(0, sonsLen(typ) - 1):
|
||||||
appf(desc, "$1 Field$2;$n",
|
appf(desc, "$1 Field$2;$n",
|
||||||
|
|
@ -557,7 +576,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
|
||||||
if result == nil:
|
if result == nil:
|
||||||
result = getTypeName(t)
|
result = getTypeName(t)
|
||||||
if not isImportedType(t):
|
if not isImportedType(t):
|
||||||
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
|
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
|
||||||
|
[structOrUnion(t), result])
|
||||||
idTablePut(m.forwTypeCache, t, result)
|
idTablePut(m.forwTypeCache, t, result)
|
||||||
assert(cacheGetType(m.typeCache, t) == nil)
|
assert(cacheGetType(m.typeCache, t) == nil)
|
||||||
idTablePut(m.typeCache, t, con(result, "*"))
|
idTablePut(m.typeCache, t, con(result, "*"))
|
||||||
|
|
@ -588,7 +608,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
|
||||||
if result == nil:
|
if result == nil:
|
||||||
result = getTypeName(t)
|
result = getTypeName(t)
|
||||||
if not isImportedType(t):
|
if not isImportedType(t):
|
||||||
appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
|
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
|
||||||
|
[structOrUnion(t), result])
|
||||||
idTablePut(m.forwTypeCache, t, result)
|
idTablePut(m.forwTypeCache, t, result)
|
||||||
idTablePut(m.typeCache, t, result) # always call for sideeffects:
|
idTablePut(m.typeCache, t, result) # always call for sideeffects:
|
||||||
if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check)
|
if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check)
|
||||||
|
|
|
||||||
|
|
@ -87,9 +87,10 @@ proc getUniqueType*(key: PType): PType =
|
||||||
gCanonicalTypes[k] = key
|
gCanonicalTypes[k] = key
|
||||||
result = key
|
result = key
|
||||||
of tyTypeDesc, tyTypeClasses, tyGenericParam,
|
of tyTypeDesc, tyTypeClasses, tyGenericParam,
|
||||||
tyFromExpr, tyStatic, tyFieldAccessor:
|
tyFromExpr, tyFieldAccessor:
|
||||||
internalError("GetUniqueType")
|
internalError("GetUniqueType")
|
||||||
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter:
|
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable,
|
||||||
|
tyConst, tyIter, tyStatic:
|
||||||
result = getUniqueType(lastSon(key))
|
result = getUniqueType(lastSon(key))
|
||||||
of tyArrayConstr, tyGenericInvokation, tyGenericBody,
|
of tyArrayConstr, tyGenericInvokation, tyGenericBody,
|
||||||
tyOpenArray, tyArray, tySet, tyRange, tyTuple,
|
tyOpenArray, tyArray, tySet, tyRange, tyTuple,
|
||||||
|
|
@ -130,7 +131,6 @@ proc getUniqueType*(key: PType): PType =
|
||||||
idTablePut(gTypeTable[k], key, key)
|
idTablePut(gTypeTable[k], key, key)
|
||||||
result = key
|
result = key
|
||||||
of tyProc:
|
of tyProc:
|
||||||
# tyVar is not 100% correct, but would speeds things up a little:
|
|
||||||
if key.callConv != ccClosure:
|
if key.callConv != ccClosure:
|
||||||
result = key
|
result = key
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -705,7 +705,7 @@ proc cgsym(m: BModule, name: string): PRope =
|
||||||
var sym = magicsys.getCompilerProc(name)
|
var sym = magicsys.getCompilerProc(name)
|
||||||
if sym != nil:
|
if sym != nil:
|
||||||
case sym.kind
|
case sym.kind
|
||||||
of skProc, skMethod, skConverter, skIterator: genProc(m, sym)
|
of skProc, skMethod, skConverter, skIterators: genProc(m, sym)
|
||||||
of skVar, skResult, skLet: genVarPrototype(m, sym)
|
of skVar, skResult, skLet: genVarPrototype(m, sym)
|
||||||
of skType: discard getTypeDesc(m, sym.typ)
|
of skType: discard getTypeDesc(m, sym.typ)
|
||||||
else: internalError("cgsym: " & name)
|
else: internalError("cgsym: " & name)
|
||||||
|
|
@ -761,6 +761,8 @@ proc genProcAux(m: BModule, prc: PSym) =
|
||||||
var returnStmt: PRope = nil
|
var returnStmt: PRope = nil
|
||||||
assert(prc.ast != nil)
|
assert(prc.ast != nil)
|
||||||
if sfPure notin prc.flags and prc.typ.sons[0] != nil:
|
if sfPure notin prc.flags and prc.typ.sons[0] != nil:
|
||||||
|
if resultPos >= prc.ast.len:
|
||||||
|
internalError(prc.info, "proc has no result symbol")
|
||||||
var res = prc.ast.sons[resultPos].sym # get result symbol
|
var res = prc.ast.sons[resultPos].sym # get result symbol
|
||||||
if not isInvalidReturnType(prc.typ.sons[0]):
|
if not isInvalidReturnType(prc.typ.sons[0]):
|
||||||
if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
|
if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
|
||||||
|
|
@ -962,8 +964,8 @@ proc genMainProc(m: BModule) =
|
||||||
NimMainBody =
|
NimMainBody =
|
||||||
"N_CDECL(void, NimMain)(void) {$N" &
|
"N_CDECL(void, NimMain)(void) {$N" &
|
||||||
"\tPreMain();$N" &
|
"\tPreMain();$N" &
|
||||||
"$1$N" &
|
"$1" &
|
||||||
"}$N"
|
"}$N$N"
|
||||||
|
|
||||||
PosixNimMain =
|
PosixNimMain =
|
||||||
"int cmdCount;$N" &
|
"int cmdCount;$N" &
|
||||||
|
|
@ -977,20 +979,20 @@ proc genMainProc(m: BModule) =
|
||||||
"\tcmdCount = argc;$N" &
|
"\tcmdCount = argc;$N" &
|
||||||
"\tgEnv = env;$N" &
|
"\tgEnv = env;$N" &
|
||||||
MainProcsWithResult &
|
MainProcsWithResult &
|
||||||
"}$N"
|
"}$N$N"
|
||||||
|
|
||||||
StandaloneCMain =
|
StandaloneCMain =
|
||||||
"int main(void) {$N" &
|
"int main(void) {$N" &
|
||||||
MainProcs &
|
MainProcs &
|
||||||
"\treturn 0;$N" &
|
"\treturn 0;$N" &
|
||||||
"}$N"
|
"}$N$N"
|
||||||
|
|
||||||
WinNimMain = NimMainBody
|
WinNimMain = NimMainBody
|
||||||
|
|
||||||
WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $N" &
|
WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $N" &
|
||||||
" HINSTANCE hPrevInstance, $N" &
|
" HINSTANCE hPrevInstance, $N" &
|
||||||
" LPSTR lpCmdLine, int nCmdShow) {$N" &
|
" LPSTR lpCmdLine, int nCmdShow) {$N" &
|
||||||
MainProcsWithResult & "}$N"
|
MainProcsWithResult & "}$N$N"
|
||||||
|
|
||||||
WinNimDllMain = "N_LIB_EXPORT " & NimMainBody
|
WinNimDllMain = "N_LIB_EXPORT " & NimMainBody
|
||||||
|
|
||||||
|
|
@ -998,14 +1000,14 @@ proc genMainProc(m: BModule) =
|
||||||
"BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" &
|
"BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" &
|
||||||
" LPVOID lpvReserved) {$N" &
|
" LPVOID lpvReserved) {$N" &
|
||||||
"\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" &
|
"\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" &
|
||||||
"\treturn 1;$N}$N"
|
"\treturn 1;$N}$N$N"
|
||||||
|
|
||||||
PosixNimDllMain = WinNimDllMain
|
PosixNimDllMain = WinNimDllMain
|
||||||
|
|
||||||
PosixCDllMain =
|
PosixCDllMain =
|
||||||
"void NIM_POSIX_INIT NimMainInit(void) {$N" &
|
"void NIM_POSIX_INIT NimMainInit(void) {$N" &
|
||||||
MainProcs &
|
MainProcs &
|
||||||
"}$N"
|
"}$N$N"
|
||||||
|
|
||||||
var nimMain, otherMain: TFormatStr
|
var nimMain, otherMain: TFormatStr
|
||||||
if platform.targetOS == osWindows and
|
if platform.targetOS == osWindows and
|
||||||
|
|
@ -1034,7 +1036,7 @@ proc genMainProc(m: BModule) =
|
||||||
platform.targetOS == osStandalone: "".toRope
|
platform.targetOS == osStandalone: "".toRope
|
||||||
else: ropecg(m, "\t#initStackBottom();$N")
|
else: ropecg(m, "\t#initStackBottom();$N")
|
||||||
inc(m.labels)
|
inc(m.labels)
|
||||||
appcg(m, m.s[cfsProcs], "void PreMain() {$N" & PreMainBody & "}$N", [
|
appcg(m, m.s[cfsProcs], "void PreMain() {$N" & PreMainBody & "}$N$N", [
|
||||||
mainDatInit, initStackBottomCall, gBreakpoints, otherModsInit])
|
mainDatInit, initStackBottomCall, gBreakpoints, otherModsInit])
|
||||||
|
|
||||||
appcg(m, m.s[cfsProcs], nimMain, [mainModInit, toRope(m.labels)])
|
appcg(m, m.s[cfsProcs], nimMain, [mainModInit, toRope(m.labels)])
|
||||||
|
|
@ -1042,8 +1044,10 @@ proc genMainProc(m: BModule) =
|
||||||
appcg(m, m.s[cfsProcs], otherMain, [])
|
appcg(m, m.s[cfsProcs], otherMain, [])
|
||||||
|
|
||||||
proc getSomeInitName(m: PSym, suffix: string): PRope =
|
proc getSomeInitName(m: PSym, suffix: string): PRope =
|
||||||
|
assert m.kind == skModule
|
||||||
|
assert m.owner.kind == skPackage
|
||||||
if {sfSystemModule, sfMainModule} * m.flags == {}:
|
if {sfSystemModule, sfMainModule} * m.flags == {}:
|
||||||
result = m.info.toFullPath.getPackageName.mangle.toRope
|
result = m.owner.name.s.mangle.toRope
|
||||||
result.app m.name.s
|
result.app m.name.s
|
||||||
result.app suffix
|
result.app suffix
|
||||||
|
|
||||||
|
|
@ -1188,7 +1192,7 @@ proc nullify[T](arr: var T) =
|
||||||
for i in low(arr)..high(arr):
|
for i in low(arr)..high(arr):
|
||||||
arr[i] = nil
|
arr[i] = nil
|
||||||
|
|
||||||
proc resetModule*(m: var BModule) =
|
proc resetModule*(m: BModule) =
|
||||||
# between two compilations in CAAS mode, we can throw
|
# between two compilations in CAAS mode, we can throw
|
||||||
# away all the data that was written to disk
|
# away all the data that was written to disk
|
||||||
initLinkedList(m.headerFiles)
|
initLinkedList(m.headerFiles)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ type
|
||||||
ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
|
ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
|
||||||
ctFloat, ctFloat32, ctFloat64, ctFloat128,
|
ctFloat, ctFloat32, ctFloat64, ctFloat128,
|
||||||
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
|
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
|
||||||
ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString
|
ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
|
||||||
|
ctCString
|
||||||
TCFileSections* = array[TCFileSection, PRope] # represents a generated C file
|
TCFileSections* = array[TCFileSection, PRope] # represents a generated C file
|
||||||
TCProcSection* = enum # the sections a generated C proc consists of
|
TCProcSection* = enum # the sections a generated C proc consists of
|
||||||
cpsLocals, # section of local variables for C proc
|
cpsLocals, # section of local variables for C proc
|
||||||
|
|
@ -57,17 +58,20 @@ type
|
||||||
sections*: TCProcSections # the code beloging
|
sections*: TCProcSections # the code beloging
|
||||||
isLoop*: bool # whether block is a loop
|
isLoop*: bool # whether block is a loop
|
||||||
nestedTryStmts*: int16 # how many try statements is it nested into
|
nestedTryStmts*: int16 # how many try statements is it nested into
|
||||||
|
nestedExceptStmts*: int16 # how many except statements is it nested into
|
||||||
frameLen*: int16
|
frameLen*: int16
|
||||||
|
|
||||||
TCProc{.final.} = object # represents C proc that is currently generated
|
TCProc{.final.} = object # represents C proc that is currently generated
|
||||||
prc*: PSym # the Nimrod proc that this C proc belongs to
|
prc*: PSym # the Nimrod proc that this C proc belongs to
|
||||||
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
||||||
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
||||||
nestedTryStmts*: seq[PNode] # in how many nested try statements we are
|
nestedTryStmts*: seq[PNode] # in how many nested try statements we are
|
||||||
# (the vars must be volatile then)
|
# (the vars must be volatile then)
|
||||||
inExceptBlock*: int # are we currently inside an except block?
|
inExceptBlock*: int # are we currently inside an except block?
|
||||||
# leaving such scopes by raise or by return must
|
# leaving such scopes by raise or by return must
|
||||||
# execute any applicable finally blocks
|
# execute any applicable finally blocks
|
||||||
|
finallySafePoints*: seq[PRope] # For correctly cleaning up exceptions when
|
||||||
|
# using return in finally statements
|
||||||
labels*: Natural # for generating unique labels in the C proc
|
labels*: Natural # for generating unique labels in the C proc
|
||||||
blocks*: seq[TBlock] # nested blocks
|
blocks*: seq[TBlock] # nested blocks
|
||||||
breakIdx*: int # the block that will be exited
|
breakIdx*: int # the block that will be exited
|
||||||
|
|
@ -140,8 +144,9 @@ proc newProc*(prc: PSym, module: BModule): BProc =
|
||||||
else: result.options = gOptions
|
else: result.options = gOptions
|
||||||
newSeq(result.blocks, 1)
|
newSeq(result.blocks, 1)
|
||||||
result.nestedTryStmts = @[]
|
result.nestedTryStmts = @[]
|
||||||
|
result.finallySafePoints = @[]
|
||||||
|
|
||||||
iterator cgenModules*: var BModule =
|
iterator cgenModules*: BModule =
|
||||||
for i in 0..high(gModules):
|
for i in 0..high(gModules):
|
||||||
# ultimately, we are iterating over the file ids here.
|
# ultimately, we are iterating over the file ids here.
|
||||||
# some "files" won't have an associated cgen module (like stdin)
|
# some "files" won't have an associated cgen module (like stdin)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ proc initDefines*() =
|
||||||
defineSymbol("nimeffects")
|
defineSymbol("nimeffects")
|
||||||
defineSymbol("nimbabel")
|
defineSymbol("nimbabel")
|
||||||
defineSymbol("nimcomputedgoto")
|
defineSymbol("nimcomputedgoto")
|
||||||
|
defineSymbol("nimunion")
|
||||||
|
|
||||||
# add platform specific symbols:
|
# add platform specific symbols:
|
||||||
case targetCPU
|
case targetCPU
|
||||||
|
|
|
||||||
|
|
@ -42,17 +42,23 @@ proc compilerMsgHandler(filename: string, line, col: int,
|
||||||
of mwUnsupportedLanguage: k = warnLanguageXNotSupported
|
of mwUnsupportedLanguage: k = warnLanguageXNotSupported
|
||||||
globalError(newLineInfo(filename, line, col), k, arg)
|
globalError(newLineInfo(filename, line, col), k, arg)
|
||||||
|
|
||||||
|
proc docgenFindFile(s: string): string {.procvar.} =
|
||||||
|
result = options.findFile(s)
|
||||||
|
if result.len == 0:
|
||||||
|
result = getCurrentDir() / s
|
||||||
|
if not existsFile(result): result = ""
|
||||||
|
|
||||||
proc parseRst(text, filename: string,
|
proc parseRst(text, filename: string,
|
||||||
line, column: int, hasToc: var bool,
|
line, column: int, hasToc: var bool,
|
||||||
rstOptions: TRstParseOptions): PRstNode =
|
rstOptions: TRstParseOptions): PRstNode =
|
||||||
result = rstParse(text, filename, line, column, hasToc, rstOptions,
|
result = rstParse(text, filename, line, column, hasToc, rstOptions,
|
||||||
options.findFile, compilerMsgHandler)
|
docgenFindFile, compilerMsgHandler)
|
||||||
|
|
||||||
proc newDocumentor*(filename: string, config: PStringTable): PDoc =
|
proc newDocumentor*(filename: string, config: PStringTable): PDoc =
|
||||||
new(result)
|
new(result)
|
||||||
initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
|
initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
|
||||||
options.gConfigVars, filename, {roSupportRawDirective},
|
options.gConfigVars, filename, {roSupportRawDirective},
|
||||||
options.findFile, compilerMsgHandler)
|
docgenFindFile, compilerMsgHandler)
|
||||||
result.id = 100
|
result.id = 100
|
||||||
|
|
||||||
proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) =
|
proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) =
|
||||||
|
|
@ -360,7 +366,7 @@ proc generateJson(d: PDoc, n: PNode, jArray: PJsonNode = nil): PJsonNode =
|
||||||
proc genSection(d: PDoc, kind: TSymKind) =
|
proc genSection(d: PDoc, kind: TSymKind) =
|
||||||
const sectionNames: array[skModule..skTemplate, string] = [
|
const sectionNames: array[skModule..skTemplate, string] = [
|
||||||
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods",
|
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods",
|
||||||
"Iterators", "Converters", "Macros", "Templates"
|
"Iterators", "Iterators", "Converters", "Macros", "Templates"
|
||||||
]
|
]
|
||||||
if d.section[kind] == nil: return
|
if d.section[kind] == nil: return
|
||||||
var title = sectionNames[kind].toRope
|
var title = sectionNames[kind].toRope
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ proc getField(n: PNode; position: int): PSym =
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc packObject(x: PNode, typ: PType, res: pointer) =
|
proc packObject(x: PNode, typ: PType, res: pointer) =
|
||||||
InternalAssert x.kind in {nkObjConstr, nkPar}
|
internalAssert x.kind in {nkObjConstr, nkPar}
|
||||||
# compute the field's offsets:
|
# compute the field's offsets:
|
||||||
discard typ.getSize
|
discard typ.getSize
|
||||||
for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1):
|
for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1):
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import
|
||||||
type
|
type
|
||||||
TSystemCC* = enum
|
TSystemCC* = enum
|
||||||
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
||||||
ccTcc, ccPcc, ccUcc, ccIcl, ccGpp
|
ccTcc, ccPcc, ccUcc, ccIcl
|
||||||
TInfoCCProp* = enum # properties of the C compiler:
|
TInfoCCProp* = enum # properties of the C compiler:
|
||||||
hasSwitchRange, # CC allows ranges in switch statements (GNU C)
|
hasSwitchRange, # CC allows ranges in switch statements (GNU C)
|
||||||
hasComputedGoto, # CC has computed goto (GNU C extension)
|
hasComputedGoto, # CC has computed goto (GNU C extension)
|
||||||
|
|
@ -33,11 +33,12 @@ type
|
||||||
optSpeed: string, # the options for optimization for speed
|
optSpeed: string, # the options for optimization for speed
|
||||||
optSize: string, # the options for optimization for size
|
optSize: string, # the options for optimization for size
|
||||||
compilerExe: string, # the compiler's executable
|
compilerExe: string, # the compiler's executable
|
||||||
|
cppCompiler: string, # name of the C++ compiler's executable (if supported)
|
||||||
compileTmpl: string, # the compile command template
|
compileTmpl: string, # the compile command template
|
||||||
buildGui: string, # command to build a GUI application
|
buildGui: string, # command to build a GUI application
|
||||||
buildDll: string, # command to build a shared library
|
buildDll: string, # command to build a shared library
|
||||||
buildLib: string, # command to build a static library
|
buildLib: string, # command to build a static library
|
||||||
linkerExe: string, # the linker's executable
|
linkerExe: string, # the linker's executable (if not matching compiler's)
|
||||||
linkTmpl: string, # command to link files to produce an exe
|
linkTmpl: string, # command to link files to produce an exe
|
||||||
includeCmd: string, # command to add an include dir
|
includeCmd: string, # command to add an include dir
|
||||||
linkDirCmd: string, # command to add a lib dir
|
linkDirCmd: string, # command to add a lib dir
|
||||||
|
|
@ -46,6 +47,8 @@ type
|
||||||
pic: string, # command for position independent code
|
pic: string, # command for position independent code
|
||||||
# used on some platforms
|
# used on some platforms
|
||||||
asmStmtFrmt: string, # format of ASM statement
|
asmStmtFrmt: string, # format of ASM statement
|
||||||
|
structStmtFmt: string, # Format for struct statement
|
||||||
|
packedPragma: string, # Attribute/pragma to make struct packed (1-byte aligned)
|
||||||
props: TInfoCCProps] # properties of the C compiler
|
props: TInfoCCProps] # properties of the C compiler
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -63,11 +66,12 @@ compiler gcc:
|
||||||
optSpeed: " -O3 -ffast-math ",
|
optSpeed: " -O3 -ffast-math ",
|
||||||
optSize: " -Os -ffast-math ",
|
optSize: " -Os -ffast-math ",
|
||||||
compilerExe: "gcc",
|
compilerExe: "gcc",
|
||||||
|
cppCompiler: "g++",
|
||||||
compileTmpl: "-c $options $include -o $objfile $file",
|
compileTmpl: "-c $options $include -o $objfile $file",
|
||||||
buildGui: " -mwindows",
|
buildGui: " -mwindows",
|
||||||
buildDll: " -shared",
|
buildDll: " -shared",
|
||||||
buildLib: "ar rcs $libfile $objfiles",
|
buildLib: "ar rcs $libfile $objfiles",
|
||||||
linkerExe: "gcc",
|
linkerExe: "",
|
||||||
linkTmpl: "$buildgui $builddll -o $exefile $objfiles $options",
|
linkTmpl: "$buildgui $builddll -o $exefile $objfiles $options",
|
||||||
includeCmd: " -I",
|
includeCmd: " -I",
|
||||||
linkDirCmd: " -L",
|
linkDirCmd: " -L",
|
||||||
|
|
@ -75,34 +79,25 @@ compiler gcc:
|
||||||
debug: "",
|
debug: "",
|
||||||
pic: "-fPIC",
|
pic: "-fPIC",
|
||||||
asmStmtFrmt: "asm($1);$n",
|
asmStmtFrmt: "asm($1);$n",
|
||||||
|
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
|
||||||
|
packedPragma: "__attribute__((__packed__))",
|
||||||
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
||||||
hasNakedAttribute})
|
hasNakedAttribute})
|
||||||
|
|
||||||
compiler gpp:
|
|
||||||
result = gcc()
|
|
||||||
|
|
||||||
result.name = "gpp"
|
|
||||||
result.compilerExe = "g++"
|
|
||||||
result.linkerExe = "g++"
|
|
||||||
|
|
||||||
result.buildDll = " -mdll"
|
|
||||||
# XXX: Hmm, I'm keeping this from the previos version,
|
|
||||||
# but my gcc doesn't even have such an option (is this mingw?)
|
|
||||||
|
|
||||||
compiler llvmGcc:
|
compiler llvmGcc:
|
||||||
result = gcc()
|
result = gcc()
|
||||||
|
|
||||||
result.name = "llvm_gcc"
|
result.name = "llvm_gcc"
|
||||||
result.compilerExe = "llvm-gcc"
|
result.compilerExe = "llvm-gcc"
|
||||||
|
result.cppCompiler = "llvm-g++"
|
||||||
result.buildLib = "llvm-ar rcs $libfile $objfiles"
|
result.buildLib = "llvm-ar rcs $libfile $objfiles"
|
||||||
result.linkerExe = "llvm-gcc"
|
|
||||||
|
|
||||||
compiler clang:
|
compiler clang:
|
||||||
result = llvmGcc()
|
result = llvmGcc()
|
||||||
|
|
||||||
result.name = "clang"
|
result.name = "clang"
|
||||||
result.compilerExe = "clang"
|
result.compilerExe = "clang"
|
||||||
result.linkerExe = "clang"
|
result.cppCompiler = "clang++"
|
||||||
|
|
||||||
compiler vcc:
|
compiler vcc:
|
||||||
result = (
|
result = (
|
||||||
|
|
@ -111,6 +106,7 @@ compiler vcc:
|
||||||
optSpeed: " /Ogityb2 /G7 /arch:SSE2 ",
|
optSpeed: " /Ogityb2 /G7 /arch:SSE2 ",
|
||||||
optSize: " /O1 /G7 ",
|
optSize: " /O1 /G7 ",
|
||||||
compilerExe: "cl",
|
compilerExe: "cl",
|
||||||
|
cppCompiler: "cl",
|
||||||
compileTmpl: "/c $options $include /Fo$objfile $file",
|
compileTmpl: "/c $options $include /Fo$objfile $file",
|
||||||
buildGui: " /link /SUBSYSTEM:WINDOWS ",
|
buildGui: " /link /SUBSYSTEM:WINDOWS ",
|
||||||
buildDll: " /LD",
|
buildDll: " /LD",
|
||||||
|
|
@ -123,6 +119,8 @@ compiler vcc:
|
||||||
debug: " /GZ /Zi ",
|
debug: " /GZ /Zi ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$3$n$1 $2",
|
||||||
|
packedPragma: "#pragma pack(1)",
|
||||||
props: {hasCpp, hasAssume, hasNakedDeclspec})
|
props: {hasCpp, hasAssume, hasNakedDeclspec})
|
||||||
|
|
||||||
compiler icl:
|
compiler icl:
|
||||||
|
|
@ -143,6 +141,7 @@ compiler lcc:
|
||||||
optSpeed: " -O -p6 ",
|
optSpeed: " -O -p6 ",
|
||||||
optSize: " -O -p6 ",
|
optSize: " -O -p6 ",
|
||||||
compilerExe: "lcc",
|
compilerExe: "lcc",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "$options $include -Fo$objfile $file",
|
compileTmpl: "$options $include -Fo$objfile $file",
|
||||||
buildGui: " -subsystem windows",
|
buildGui: " -subsystem windows",
|
||||||
buildDll: " -dll",
|
buildDll: " -dll",
|
||||||
|
|
@ -155,6 +154,8 @@ compiler lcc:
|
||||||
debug: " -g5 ",
|
debug: " -g5 ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "_asm{$n$1$n}$n",
|
asmStmtFrmt: "_asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {})
|
props: {})
|
||||||
|
|
||||||
compiler bcc:
|
compiler bcc:
|
||||||
|
|
@ -164,6 +165,7 @@ compiler bcc:
|
||||||
optSpeed: " -O2 -6 ",
|
optSpeed: " -O2 -6 ",
|
||||||
optSize: " -O1 -6 ",
|
optSize: " -O1 -6 ",
|
||||||
compilerExe: "bcc32",
|
compilerExe: "bcc32",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -o$objfile $file",
|
compileTmpl: "-c $options $include -o$objfile $file",
|
||||||
buildGui: " -tW",
|
buildGui: " -tW",
|
||||||
buildDll: " -tWD",
|
buildDll: " -tWD",
|
||||||
|
|
@ -176,6 +178,8 @@ compiler bcc:
|
||||||
debug: "",
|
debug: "",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {hasCpp})
|
props: {hasCpp})
|
||||||
|
|
||||||
compiler dmc:
|
compiler dmc:
|
||||||
|
|
@ -185,6 +189,7 @@ compiler dmc:
|
||||||
optSpeed: " -ff -o -6 ",
|
optSpeed: " -ff -o -6 ",
|
||||||
optSize: " -ff -o -6 ",
|
optSize: " -ff -o -6 ",
|
||||||
compilerExe: "dmc",
|
compilerExe: "dmc",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -o$objfile $file",
|
compileTmpl: "-c $options $include -o$objfile $file",
|
||||||
buildGui: " -L/exet:nt/su:windows",
|
buildGui: " -L/exet:nt/su:windows",
|
||||||
buildDll: " -WD",
|
buildDll: " -WD",
|
||||||
|
|
@ -197,6 +202,8 @@ compiler dmc:
|
||||||
debug: " -g ",
|
debug: " -g ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$3$n$1 $2",
|
||||||
|
packedPragma: "#pragma pack(1)",
|
||||||
props: {hasCpp})
|
props: {hasCpp})
|
||||||
|
|
||||||
compiler wcc:
|
compiler wcc:
|
||||||
|
|
@ -206,6 +213,7 @@ compiler wcc:
|
||||||
optSpeed: " -ox -on -6 -d0 -fp6 -zW ",
|
optSpeed: " -ox -on -6 -d0 -fp6 -zW ",
|
||||||
optSize: "",
|
optSize: "",
|
||||||
compilerExe: "wcl386",
|
compilerExe: "wcl386",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -fo=$objfile $file",
|
compileTmpl: "-c $options $include -fo=$objfile $file",
|
||||||
buildGui: " -bw",
|
buildGui: " -bw",
|
||||||
buildDll: " -bd",
|
buildDll: " -bd",
|
||||||
|
|
@ -218,6 +226,8 @@ compiler wcc:
|
||||||
debug: " -d2 ",
|
debug: " -d2 ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {hasCpp})
|
props: {hasCpp})
|
||||||
|
|
||||||
compiler tcc:
|
compiler tcc:
|
||||||
|
|
@ -227,6 +237,7 @@ compiler tcc:
|
||||||
optSpeed: "",
|
optSpeed: "",
|
||||||
optSize: "",
|
optSize: "",
|
||||||
compilerExe: "tcc",
|
compilerExe: "tcc",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -o $objfile $file",
|
compileTmpl: "-c $options $include -o $objfile $file",
|
||||||
buildGui: "UNAVAILABLE!",
|
buildGui: "UNAVAILABLE!",
|
||||||
buildDll: " -shared",
|
buildDll: " -shared",
|
||||||
|
|
@ -239,6 +250,8 @@ compiler tcc:
|
||||||
debug: " -g ",
|
debug: " -g ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {hasSwitchRange, hasComputedGoto})
|
props: {hasSwitchRange, hasComputedGoto})
|
||||||
|
|
||||||
compiler pcc:
|
compiler pcc:
|
||||||
|
|
@ -249,6 +262,7 @@ compiler pcc:
|
||||||
optSpeed: " -Ox ",
|
optSpeed: " -Ox ",
|
||||||
optSize: " -Os ",
|
optSize: " -Os ",
|
||||||
compilerExe: "cc",
|
compilerExe: "cc",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -Fo$objfile $file",
|
compileTmpl: "-c $options $include -Fo$objfile $file",
|
||||||
buildGui: " -SUBSYSTEM:WINDOWS",
|
buildGui: " -SUBSYSTEM:WINDOWS",
|
||||||
buildDll: " -DLL",
|
buildDll: " -DLL",
|
||||||
|
|
@ -261,6 +275,8 @@ compiler pcc:
|
||||||
debug: " -Zi ",
|
debug: " -Zi ",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {})
|
props: {})
|
||||||
|
|
||||||
compiler ucc:
|
compiler ucc:
|
||||||
|
|
@ -270,6 +286,7 @@ compiler ucc:
|
||||||
optSpeed: " -O3 ",
|
optSpeed: " -O3 ",
|
||||||
optSize: " -O1 ",
|
optSize: " -O1 ",
|
||||||
compilerExe: "cc",
|
compilerExe: "cc",
|
||||||
|
cppCompiler: "",
|
||||||
compileTmpl: "-c $options $include -o $objfile $file",
|
compileTmpl: "-c $options $include -o $objfile $file",
|
||||||
buildGui: "",
|
buildGui: "",
|
||||||
buildDll: " -shared ",
|
buildDll: " -shared ",
|
||||||
|
|
@ -282,6 +299,8 @@ compiler ucc:
|
||||||
debug: "",
|
debug: "",
|
||||||
pic: "",
|
pic: "",
|
||||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||||
|
structStmtFmt: "$1 $2",
|
||||||
|
packedPragma: "", # XXX: not supported yet
|
||||||
props: {})
|
props: {})
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -297,8 +316,7 @@ const
|
||||||
tcc(),
|
tcc(),
|
||||||
pcc(),
|
pcc(),
|
||||||
ucc(),
|
ucc(),
|
||||||
icl(),
|
icl()]
|
||||||
gpp()]
|
|
||||||
|
|
||||||
const
|
const
|
||||||
hExt* = ".h"
|
hExt* = ".h"
|
||||||
|
|
@ -471,11 +489,21 @@ proc needsExeExt(): bool {.inline.} =
|
||||||
result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
|
result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
|
||||||
(platform.hostOS == osWindows)
|
(platform.hostOS == osWindows)
|
||||||
|
|
||||||
|
proc getCompilerExe(compiler: TSystemCC): string =
|
||||||
|
result = if gCmd == cmdCompileToCpp: CC[compiler].cppCompiler
|
||||||
|
else: CC[compiler].compilerExe
|
||||||
|
if result.len == 0:
|
||||||
|
rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name)
|
||||||
|
|
||||||
|
proc getLinkerExe(compiler: TSystemCC): string =
|
||||||
|
result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
|
||||||
|
else: compiler.getCompilerExe
|
||||||
|
|
||||||
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
||||||
var c = cCompiler
|
var c = cCompiler
|
||||||
var options = cFileSpecificOptions(cfilename)
|
var options = cFileSpecificOptions(cfilename)
|
||||||
var exe = getConfigVar(c, ".exe")
|
var exe = getConfigVar(c, ".exe")
|
||||||
if exe.len == 0: exe = CC[c].compilerExe
|
if exe.len == 0: exe = c.getCompilerExe
|
||||||
|
|
||||||
if needsExeExt(): exe = addFileExt(exe, "exe")
|
if needsExeExt(): exe = addFileExt(exe, "exe")
|
||||||
if optGenDynLib in gGlobalOptions and
|
if optGenDynLib in gGlobalOptions and
|
||||||
|
|
@ -493,7 +521,7 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
||||||
compilePattern = joinPath(ccompilerpath, exe)
|
compilePattern = joinPath(ccompilerpath, exe)
|
||||||
else:
|
else:
|
||||||
includeCmd = ""
|
includeCmd = ""
|
||||||
compilePattern = CC[c].compilerExe
|
compilePattern = c.getCompilerExe
|
||||||
|
|
||||||
var cfile = if noAbsolutePaths(): extractFilename(cfilename)
|
var cfile = if noAbsolutePaths(): extractFilename(cfilename)
|
||||||
else: cfilename
|
else: cfilename
|
||||||
|
|
@ -600,7 +628,7 @@ proc callCCompiler*(projectfile: string) =
|
||||||
if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd)
|
if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd)
|
||||||
else:
|
else:
|
||||||
var linkerExe = getConfigVar(c, ".linkerexe")
|
var linkerExe = getConfigVar(c, ".linkerexe")
|
||||||
if len(linkerExe) == 0: linkerExe = CC[c].linkerExe
|
if len(linkerExe) == 0: linkerExe = c.getLinkerExe
|
||||||
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
|
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
|
||||||
if noAbsolutePaths(): linkCmd = quoteShell(linkerExe)
|
if noAbsolutePaths(): linkCmd = quoteShell(linkerExe)
|
||||||
else: linkCmd = quoteShell(joinPath(ccompilerpath, linkerExe))
|
else: linkCmd = quoteShell(joinPath(ccompilerpath, linkerExe))
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
## This module implements the 'implies' relation for guards.
|
## This module implements the 'implies' relation for guards.
|
||||||
|
|
||||||
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer
|
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents
|
||||||
|
|
||||||
const
|
const
|
||||||
someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||||
|
|
@ -69,9 +69,23 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
|
||||||
|
|
||||||
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
|
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
|
||||||
|
|
||||||
proc swapArgs(fact: PNode, newOp: string, m: TMagic): PNode =
|
proc getMagicOp(name: string, m: TMagic): PSym =
|
||||||
|
result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
|
||||||
|
result.magic = m
|
||||||
|
|
||||||
|
let
|
||||||
|
opLe = getMagicOp("<=", mLeI)
|
||||||
|
opLt = getMagicOp("<", mLtI)
|
||||||
|
opAnd = getMagicOp("and", mAnd)
|
||||||
|
opOr = getMagicOp("or", mOr)
|
||||||
|
opNot = getMagicOp("not", mNot)
|
||||||
|
opIsNil = getMagicOp("isnil", mIsNil)
|
||||||
|
opContains = getMagicOp("contains", mInSet)
|
||||||
|
opEq = getMagicOp("==", mEqI)
|
||||||
|
|
||||||
|
proc swapArgs(fact: PNode, newOp: PSym): PNode =
|
||||||
result = newNodeI(nkCall, fact.info, 3)
|
result = newNodeI(nkCall, fact.info, 3)
|
||||||
result.sons[0] = newSymNode(getSysMagic(newOp, m))
|
result.sons[0] = newSymNode(newOp)
|
||||||
result.sons[1] = fact.sons[2]
|
result.sons[1] = fact.sons[2]
|
||||||
result.sons[2] = fact.sons[1]
|
result.sons[2] = fact.sons[1]
|
||||||
|
|
||||||
|
|
@ -82,9 +96,9 @@ proc neg(n: PNode): PNode =
|
||||||
result = n.sons[1]
|
result = n.sons[1]
|
||||||
of someLt:
|
of someLt:
|
||||||
# not (a < b) == a >= b == b <= a
|
# not (a < b) == a >= b == b <= a
|
||||||
result = swapArgs(n, "<=", mLeI)
|
result = swapArgs(n, opLe)
|
||||||
of someLe:
|
of someLe:
|
||||||
result = swapArgs(n, "<", mLtI)
|
result = swapArgs(n, opLt)
|
||||||
of mInSet:
|
of mInSet:
|
||||||
if n.sons[1].kind != nkCurly: return nil
|
if n.sons[1].kind != nkCurly: return nil
|
||||||
let t = n.sons[2].typ.skipTypes(abstractInst)
|
let t = n.sons[2].typ.skipTypes(abstractInst)
|
||||||
|
|
@ -110,7 +124,7 @@ proc neg(n: PNode): PNode =
|
||||||
b = n.sons[2].neg
|
b = n.sons[2].neg
|
||||||
if a != nil and b != nil:
|
if a != nil and b != nil:
|
||||||
result = newNodeI(nkCall, n.info, 3)
|
result = newNodeI(nkCall, n.info, 3)
|
||||||
result.sons[0] = newSymNode(getSysMagic("and", mAnd))
|
result.sons[0] = newSymNode(opAnd)
|
||||||
result.sons[1] = a
|
result.sons[1] = a
|
||||||
result.sons[2] = b
|
result.sons[2] = b
|
||||||
elif a != nil:
|
elif a != nil:
|
||||||
|
|
@ -120,12 +134,12 @@ proc neg(n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
# leave not (a == 4) as it is
|
# leave not (a == 4) as it is
|
||||||
result = newNodeI(nkCall, n.info, 2)
|
result = newNodeI(nkCall, n.info, 2)
|
||||||
result.sons[0] = newSymNode(getSysMagic("not", mNot))
|
result.sons[0] = newSymNode(opNot)
|
||||||
result.sons[1] = n
|
result.sons[1] = n
|
||||||
|
|
||||||
proc buildIsNil(arg: PNode): PNode =
|
proc buildIsNil(arg: PNode): PNode =
|
||||||
result = newNodeI(nkCall, arg.info, 2)
|
result = newNodeI(nkCall, arg.info, 2)
|
||||||
result.sons[0] = newSymNode(getSysMagic("isNil", mIsNil))
|
result.sons[0] = newSymNode(opIsNil)
|
||||||
result.sons[1] = arg
|
result.sons[1] = arg
|
||||||
|
|
||||||
proc usefulFact(n: PNode): PNode =
|
proc usefulFact(n: PNode): PNode =
|
||||||
|
|
@ -154,7 +168,7 @@ proc usefulFact(n: PNode): PNode =
|
||||||
b = usefulFact(n.sons[2])
|
b = usefulFact(n.sons[2])
|
||||||
if a != nil and b != nil:
|
if a != nil and b != nil:
|
||||||
result = newNodeI(nkCall, n.info, 3)
|
result = newNodeI(nkCall, n.info, 3)
|
||||||
result.sons[0] = newSymNode(getSysMagic("and", mAnd))
|
result.sons[0] = newSymNode(opAnd)
|
||||||
result.sons[1] = a
|
result.sons[1] = a
|
||||||
result.sons[2] = b
|
result.sons[2] = b
|
||||||
elif a != nil:
|
elif a != nil:
|
||||||
|
|
@ -177,7 +191,7 @@ proc usefulFact(n: PNode): PNode =
|
||||||
b = usefulFact(n.sons[2]).neg
|
b = usefulFact(n.sons[2]).neg
|
||||||
if a != nil and b != nil:
|
if a != nil and b != nil:
|
||||||
result = newNodeI(nkCall, n.info, 3)
|
result = newNodeI(nkCall, n.info, 3)
|
||||||
result.sons[0] = newSymNode(getSysMagic("and", mAnd))
|
result.sons[0] = newSymNode(opAnd)
|
||||||
result.sons[1] = a
|
result.sons[1] = a
|
||||||
result.sons[2] = b
|
result.sons[2] = b
|
||||||
result = result.neg
|
result = result.neg
|
||||||
|
|
@ -260,10 +274,6 @@ proc pred(n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
type
|
|
||||||
TImplication* = enum
|
|
||||||
impUnknown, impNo, impYes
|
|
||||||
|
|
||||||
proc impliesEq(fact, eq: PNode): TImplication =
|
proc impliesEq(fact, eq: PNode): TImplication =
|
||||||
let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
|
let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
|
||||||
|
|
||||||
|
|
@ -520,7 +530,7 @@ proc buildOf(it, loc: PNode): PNode =
|
||||||
s.typ = settype(loc)
|
s.typ = settype(loc)
|
||||||
for i in 0..it.len-2: s.sons[i] = it.sons[i]
|
for i in 0..it.len-2: s.sons[i] = it.sons[i]
|
||||||
result = newNodeI(nkCall, it.info, 3)
|
result = newNodeI(nkCall, it.info, 3)
|
||||||
result.sons[0] = newSymNode(getSysMagic("contains", mInSet))
|
result.sons[0] = newSymNode(opContains)
|
||||||
result.sons[1] = s
|
result.sons[1] = s
|
||||||
result.sons[2] = loc
|
result.sons[2] = loc
|
||||||
|
|
||||||
|
|
@ -532,20 +542,20 @@ proc buildElse(n: PNode): PNode =
|
||||||
for j in 0..branch.len-2:
|
for j in 0..branch.len-2:
|
||||||
s.add(branch.sons[j])
|
s.add(branch.sons[j])
|
||||||
result = newNodeI(nkCall, n.info, 3)
|
result = newNodeI(nkCall, n.info, 3)
|
||||||
result.sons[0] = newSymNode(getSysMagic("contains", mInSet))
|
result.sons[0] = newSymNode(opContains)
|
||||||
result.sons[1] = s
|
result.sons[1] = s
|
||||||
result.sons[2] = n.sons[0]
|
result.sons[2] = n.sons[0]
|
||||||
|
|
||||||
proc addDiscriminantFact*(m: var TModel, n: PNode) =
|
proc addDiscriminantFact*(m: var TModel, n: PNode) =
|
||||||
var fact = newNodeI(nkCall, n.info, 3)
|
var fact = newNodeI(nkCall, n.info, 3)
|
||||||
fact.sons[0] = newSymNode(getSysMagic("==", mEqI))
|
fact.sons[0] = newSymNode(opEq)
|
||||||
fact.sons[1] = n.sons[0]
|
fact.sons[1] = n.sons[0]
|
||||||
fact.sons[2] = n.sons[1]
|
fact.sons[2] = n.sons[1]
|
||||||
m.add fact
|
m.add fact
|
||||||
|
|
||||||
proc addAsgnFact*(m: var TModel, key, value: PNode) =
|
proc addAsgnFact*(m: var TModel, key, value: PNode) =
|
||||||
var fact = newNodeI(nkCall, key.info, 3)
|
var fact = newNodeI(nkCall, key.info, 3)
|
||||||
fact.sons[0] = newSymNode(getSysMagic("==", mEqI))
|
fact.sons[0] = newSymNode(opEq)
|
||||||
fact.sons[1] = key
|
fact.sons[1] = key
|
||||||
fact.sons[2] = value
|
fact.sons[2] = value
|
||||||
m.add fact
|
m.add fact
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ const
|
||||||
when debugIds:
|
when debugIds:
|
||||||
import intsets
|
import intsets
|
||||||
|
|
||||||
var usedIds = InitIntSet()
|
var usedIds = initIntSet()
|
||||||
|
|
||||||
proc registerID*(id: PIdObj) =
|
proc registerID*(id: PIdObj) =
|
||||||
when debugIds:
|
when debugIds:
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
|
||||||
internalError(n.info, "importSymbol: 2")
|
internalError(n.info, "importSymbol: 2")
|
||||||
# for an enumeration we have to add all identifiers
|
# for an enumeration we have to add all identifiers
|
||||||
case s.kind
|
case s.kind
|
||||||
of skProc, skMethod, skIterator, skMacro, skTemplate, skConverter:
|
of skProcKinds:
|
||||||
# for a overloadable syms add all overloaded routines
|
# for a overloadable syms add all overloaded routines
|
||||||
var it: TIdentIter
|
var it: TIdentIter
|
||||||
var e = initIdentIter(it, fromMod.tab, s.name)
|
var e = initIdentIter(it, fromMod.tab, s.name)
|
||||||
|
|
|
||||||
|
|
@ -275,11 +275,11 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64
|
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64
|
||||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
||||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
||||||
["AddU", "AddU", "AddU($1, $2)", "AddU($1, $2)"], # AddU
|
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
|
||||||
["SubU", "SubU", "SubU($1, $2)", "SubU($1, $2)"], # SubU
|
["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU
|
||||||
["MulU", "MulU", "MulU($1, $2)", "MulU($1, $2)"], # MulU
|
["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU
|
||||||
["DivU", "DivU", "DivU($1, $2)", "DivU($1, $2)"], # DivU
|
["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU
|
||||||
["ModU", "ModU", "ModU($1, $2)", "ModU($1, $2)"], # ModU
|
["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqI
|
["", "", "($1 == $2)", "($1 == $2)"], # EqI
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
|
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtI
|
["", "", "($1 < $2)", "($1 < $2)"], # LtI
|
||||||
|
|
@ -289,10 +289,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
|
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
|
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
|
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
|
||||||
["LeU", "LeU", "LeU($1, $2)", "LeU($1, $2)"], # LeU
|
["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU
|
||||||
["LtU", "LtU", "LtU($1, $2)", "LtU($1, $2)"], # LtU
|
["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU
|
||||||
["LeU64", "LeU64", "LeU64($1, $2)", "LeU64($1, $2)"], # LeU64
|
["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64
|
||||||
["LtU64", "LtU64", "LtU64($1, $2)", "LtU64($1, $2)"], # LtU64
|
["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
|
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
|
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
|
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
|
||||||
|
|
@ -309,10 +309,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
|
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
|
||||||
["", "", "($1 != $2)", "($1 != $2)"], # Xor
|
["", "", "($1 != $2)", "($1 != $2)"], # Xor
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
|
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
|
||||||
["NegInt", "", "NegInt($1)", "-($1)"], # UnaryMinusI
|
["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
|
||||||
["NegInt64", "", "NegInt64($1)", "-($1)"], # UnaryMinusI64
|
["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
|
||||||
["AbsInt", "", "AbsInt($1)", "Math.abs($1)"], # AbsI
|
["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
|
||||||
["AbsInt64", "", "AbsInt64($1)", "Math.abs($1)"], # AbsI64
|
["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
|
||||||
["", "", "!($1)", "!($1)"], # Not
|
["", "", "!($1)", "!($1)"], # Not
|
||||||
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
||||||
["", "", "~($1)", "~($1)"], # BitnotI
|
["", "", "~($1)", "~($1)"], # BitnotI
|
||||||
|
|
@ -327,9 +327,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
|
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
|
||||||
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
|
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
|
||||||
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
|
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
|
||||||
["ToU8", "ToU8", "ToU8($1)", "ToU8($1)"], # ToU8
|
["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
|
||||||
["ToU16", "ToU16", "ToU16($1)", "ToU16($1)"], # ToU16
|
["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
|
||||||
["ToU32", "ToU32", "ToU32($1)", "ToU32($1)"], # ToU32
|
["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
|
||||||
["", "", "$1", "$1"], # ToFloat
|
["", "", "$1", "$1"], # ToFloat
|
||||||
["", "", "$1", "$1"], # ToBiggestFloat
|
["", "", "$1", "$1"], # ToBiggestFloat
|
||||||
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
|
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
|
||||||
|
|
@ -375,11 +375,11 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64
|
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64
|
||||||
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
|
||||||
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
|
||||||
["AddU", "AddU", "AddU($1, $2)", "AddU($1, $2)"], # AddU
|
["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
|
||||||
["SubU", "SubU", "SubU($1, $2)", "SubU($1, $2)"], # SubU
|
["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU
|
||||||
["MulU", "MulU", "MulU($1, $2)", "MulU($1, $2)"], # MulU
|
["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU
|
||||||
["DivU", "DivU", "DivU($1, $2)", "DivU($1, $2)"], # DivU
|
["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU
|
||||||
["ModU", "ModU", "ModU($1, $2)", "ModU($1, $2)"], # ModU
|
["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqI
|
["", "", "($1 == $2)", "($1 == $2)"], # EqI
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
|
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtI
|
["", "", "($1 < $2)", "($1 < $2)"], # LtI
|
||||||
|
|
@ -389,10 +389,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
|
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
|
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
|
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
|
||||||
["LeU", "LeU", "LeU($1, $2)", "LeU($1, $2)"], # LeU
|
["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU
|
||||||
["LtU", "LtU", "LtU($1, $2)", "LtU($1, $2)"], # LtU
|
["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU
|
||||||
["LeU64", "LeU64", "LeU64($1, $2)", "LeU64($1, $2)"], # LeU64
|
["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64
|
||||||
["LtU64", "LtU64", "LtU64($1, $2)", "LtU64($1, $2)"], # LtU64
|
["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
|
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
|
||||||
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
|
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
|
||||||
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
|
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
|
||||||
|
|
@ -409,10 +409,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
|
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
|
||||||
["", "", "($1 != $2)", "($1 != $2)"], # Xor
|
["", "", "($1 != $2)", "($1 != $2)"], # Xor
|
||||||
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
|
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
|
||||||
["NegInt", "", "NegInt($1)", "-($1)"], # UnaryMinusI
|
["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
|
||||||
["NegInt64", "", "NegInt64($1)", "-($1)"], # UnaryMinusI64
|
["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
|
||||||
["AbsInt", "", "AbsInt($1)", "Math.abs($1)"], # AbsI
|
["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
|
||||||
["AbsInt64", "", "AbsInt64($1)", "Math.abs($1)"], # AbsI64
|
["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
|
||||||
["", "", "not ($1)", "not ($1)"], # Not
|
["", "", "not ($1)", "not ($1)"], # Not
|
||||||
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
["", "", "+($1)", "+($1)"], # UnaryPlusI
|
||||||
["", "", "~($1)", "~($1)"], # BitnotI
|
["", "", "~($1)", "~($1)"], # BitnotI
|
||||||
|
|
@ -427,9 +427,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
|
||||||
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
|
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
|
||||||
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
|
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
|
||||||
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
|
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
|
||||||
["ToU8", "ToU8", "ToU8($1)", "ToU8($1)"], # ToU8
|
["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
|
||||||
["ToU16", "ToU16", "ToU16($1)", "ToU16($1)"], # ToU16
|
["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
|
||||||
["ToU32", "ToU32", "ToU32($1)", "ToU32($1)"], # ToU32
|
["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
|
||||||
["", "", "$1", "$1"], # ToFloat
|
["", "", "$1", "$1"], # ToFloat
|
||||||
["", "", "$1", "$1"], # ToBiggestFloat
|
["", "", "$1", "$1"], # ToBiggestFloat
|
||||||
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
|
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
|
||||||
|
|
@ -812,8 +812,8 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
|
||||||
if needsNoCopy(y) or noCopyNeeded:
|
if needsNoCopy(y) or noCopyNeeded:
|
||||||
appf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
|
appf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
|
||||||
else:
|
else:
|
||||||
useMagic(p, "NimCopy")
|
useMagic(p, "nimCopy")
|
||||||
appf(p.body, "$1 = NimCopy($2, $3);$n",
|
appf(p.body, "$1 = nimCopy($2, $3);$n",
|
||||||
[a.res, b.res, genTypeInfo(p, y.typ)])
|
[a.res, b.res, genTypeInfo(p, y.typ)])
|
||||||
of etyBaseIndex:
|
of etyBaseIndex:
|
||||||
if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
|
if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
|
||||||
|
|
@ -1113,8 +1113,8 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope =
|
||||||
var length = int(lengthOrd(t))
|
var length = int(lengthOrd(t))
|
||||||
var e = elemType(t)
|
var e = elemType(t)
|
||||||
if length > 32:
|
if length > 32:
|
||||||
useMagic(p, "ArrayConstr")
|
useMagic(p, "arrayConstr")
|
||||||
result = ropef("ArrayConstr($1, $2, $3)", [toRope(length),
|
result = ropef("arrayConstr($1, $2, $3)", [toRope(length),
|
||||||
createVar(p, e, false), genTypeInfo(p, e)])
|
createVar(p, e, false), genTypeInfo(p, e)])
|
||||||
else:
|
else:
|
||||||
result = toRope("[")
|
result = toRope("[")
|
||||||
|
|
@ -1154,7 +1154,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope =
|
||||||
|
|
||||||
proc isIndirect(v: PSym): bool =
|
proc isIndirect(v: PSym): bool =
|
||||||
result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject) and
|
result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject) and
|
||||||
v.kind notin {skProc, skConverter, skMethod, skIterator}
|
v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator}
|
||||||
|
|
||||||
proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||||
var
|
var
|
||||||
|
|
@ -1171,8 +1171,8 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
|
||||||
if needsNoCopy(n):
|
if needsNoCopy(n):
|
||||||
s = a.res
|
s = a.res
|
||||||
else:
|
else:
|
||||||
useMagic(p, "NimCopy")
|
useMagic(p, "nimCopy")
|
||||||
s = ropef("NimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)])
|
s = ropef("nimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)])
|
||||||
of etyBaseIndex:
|
of etyBaseIndex:
|
||||||
if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit")
|
if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit")
|
||||||
if {sfAddrTaken, sfGlobal} * v.flags != {}:
|
if {sfAddrTaken, sfGlobal} * v.flags != {}:
|
||||||
|
|
@ -1600,7 +1600,6 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
if lfNoDecl in s.loc.flags or s.magic != mNone: discard
|
if lfNoDecl in s.loc.flags or s.magic != mNone: discard
|
||||||
elif not p.g.generatedSyms.containsOrIncl(s.id):
|
elif not p.g.generatedSyms.containsOrIncl(s.id):
|
||||||
app(p.locals, genProc(p, s))
|
app(p.locals, genProc(p, s))
|
||||||
of nkMetaNode: gen(p, n.sons[0], r)
|
|
||||||
of nkType: r.res = genTypeInfo(p, n.typ)
|
of nkType: r.res = genTypeInfo(p, n.typ)
|
||||||
of nkStmtList, nkStmtListExpr:
|
of nkStmtList, nkStmtListExpr:
|
||||||
# this shows the distinction is nice for backends and should be kept
|
# this shows the distinction is nice for backends and should be kept
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
|
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
|
||||||
idents, renderer, types, magicsys, rodread
|
idents, renderer, types, magicsys, rodread, lowerings
|
||||||
|
|
||||||
discard """
|
discard """
|
||||||
The basic approach is that captured vars need to be put on the heap and
|
The basic approach is that captured vars need to be put on the heap and
|
||||||
|
|
@ -232,7 +232,7 @@ proc newOuterContext(fn: PSym, up: POuterContext = nil): POuterContext =
|
||||||
initIdNodeTable(result.localsToAccess)
|
initIdNodeTable(result.localsToAccess)
|
||||||
initIdTable(result.localsToEnv)
|
initIdTable(result.localsToEnv)
|
||||||
initIdTable(result.lambdasToEnv)
|
initIdTable(result.lambdasToEnv)
|
||||||
result.isIter = fn.kind == skIterator and fn.typ.callConv == ccClosure
|
result.isIter = fn.kind == skClosureIterator
|
||||||
if result.isIter: initIterContext(result, fn)
|
if result.isIter: initIterContext(result, fn)
|
||||||
|
|
||||||
proc newInnerContext(fn: PSym): PInnerContext =
|
proc newInnerContext(fn: PSym): PInnerContext =
|
||||||
|
|
@ -292,8 +292,7 @@ proc newCall(a, b: PSym): PNode =
|
||||||
result.add newSymNode(b)
|
result.add newSymNode(b)
|
||||||
|
|
||||||
proc isInnerProc(s, outerProc: PSym): bool {.inline.} =
|
proc isInnerProc(s, outerProc: PSym): bool {.inline.} =
|
||||||
result = (s.kind in {skProc, skMethod, skConverter} or
|
result = s.kind in {skProc, skMethod, skConverter, skClosureIterator} and
|
||||||
s.kind == skIterator and s.typ.callConv == ccClosure) and
|
|
||||||
s.skipGenericOwner == outerProc
|
s.skipGenericOwner == outerProc
|
||||||
#s.typ.callConv == ccClosure
|
#s.typ.callConv == ccClosure
|
||||||
|
|
||||||
|
|
@ -357,7 +356,10 @@ proc captureVar(o: POuterContext, i: PInnerContext, local: PSym,
|
||||||
# it's in some upper environment:
|
# it's in some upper environment:
|
||||||
access = indirectAccess(access, addDep(e, it, i.fn), info)
|
access = indirectAccess(access, addDep(e, it, i.fn), info)
|
||||||
access = indirectAccess(access, local, info)
|
access = indirectAccess(access, local, info)
|
||||||
incl(o.capturedVars, local.id)
|
if o.isIter:
|
||||||
|
if not containsOrIncl(o.capturedVars, local.id): addField(o.tup, local)
|
||||||
|
else:
|
||||||
|
incl(o.capturedVars, local.id)
|
||||||
idNodeTablePut(i.localsToAccess, local, access)
|
idNodeTablePut(i.localsToAccess, local, access)
|
||||||
|
|
||||||
proc interestingVar(s: PSym): bool {.inline.} =
|
proc interestingVar(s: PSym): bool {.inline.} =
|
||||||
|
|
@ -519,7 +521,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) =
|
||||||
else:
|
else:
|
||||||
internalError(it.info, "transformOuter")
|
internalError(it.info, "transformOuter")
|
||||||
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
|
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
|
||||||
nkClosure:
|
nkClosure, nkTypeSection:
|
||||||
# don't recurse here:
|
# don't recurse here:
|
||||||
# XXX recurse here and setup 'up' pointers
|
# XXX recurse here and setup 'up' pointers
|
||||||
discard
|
discard
|
||||||
|
|
@ -537,13 +539,6 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
|
||||||
result.sons[0] = le
|
result.sons[0] = le
|
||||||
result.sons[1] = ri
|
result.sons[1] = ri
|
||||||
|
|
||||||
proc addVar*(father, v: PNode) =
|
|
||||||
var vpart = newNodeI(nkIdentDefs, v.info)
|
|
||||||
addSon(vpart, v)
|
|
||||||
addSon(vpart, ast.emptyNode)
|
|
||||||
addSon(vpart, ast.emptyNode)
|
|
||||||
addSon(father, vpart)
|
|
||||||
|
|
||||||
proc newClosureCreationVar(o: POuterContext; e: PEnv): PSym =
|
proc newClosureCreationVar(o: POuterContext; e: PEnv): PSym =
|
||||||
result = newSym(skVar, getIdent(envName), o.fn, e.attachedNode.info)
|
result = newSym(skVar, getIdent(envName), o.fn, e.attachedNode.info)
|
||||||
incl(result.flags, sfShadowed)
|
incl(result.flags, sfShadowed)
|
||||||
|
|
@ -653,7 +648,7 @@ proc outerProcSons(o: POuterContext, n: PNode) =
|
||||||
proc liftIterSym*(n: PNode): PNode =
|
proc liftIterSym*(n: PNode): PNode =
|
||||||
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
|
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
|
||||||
let iter = n.sym
|
let iter = n.sym
|
||||||
assert iter.kind == skIterator
|
assert iter.kind == skClosureIterator
|
||||||
|
|
||||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||||
|
|
||||||
|
|
@ -679,7 +674,7 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
|
||||||
|
|
||||||
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
|
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
|
||||||
|
|
||||||
if local.kind == skIterator and local.typ.callConv == ccClosure:
|
if local.kind == skClosureIterator:
|
||||||
# consider: [i1, i2, i1] Since we merged the iterator's closure
|
# consider: [i1, i2, i1] Since we merged the iterator's closure
|
||||||
# with the captured owning variables, we need to generate the
|
# with the captured owning variables, we need to generate the
|
||||||
# closure generation code again:
|
# closure generation code again:
|
||||||
|
|
@ -843,10 +838,10 @@ proc liftForLoop*(body: PNode): PNode =
|
||||||
|
|
||||||
# static binding?
|
# static binding?
|
||||||
var env: PSym
|
var env: PSym
|
||||||
if call[0].kind == nkSym and call[0].sym.kind == skIterator:
|
if call[0].kind == nkSym and call[0].sym.kind == skClosureIterator:
|
||||||
# createClosure()
|
# createClosure()
|
||||||
let iter = call[0].sym
|
let iter = call[0].sym
|
||||||
assert iter.kind == skIterator
|
assert iter.kind == skClosureIterator
|
||||||
env = copySym(getHiddenParam(iter))
|
env = copySym(getHiddenParam(iter))
|
||||||
|
|
||||||
var v = newNodeI(nkVarSection, body.info)
|
var v = newNodeI(nkVarSection, body.info)
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ type
|
||||||
base10, # base10 is listed as the first element,
|
base10, # base10 is listed as the first element,
|
||||||
# so that it is the correct default value
|
# so that it is the correct default value
|
||||||
base2, base8, base16
|
base2, base8, base16
|
||||||
|
|
||||||
TToken* = object # a Nimrod token
|
TToken* = object # a Nimrod token
|
||||||
tokType*: TTokType # the type of the token
|
tokType*: TTokType # the type of the token
|
||||||
indent*: int # the indentation; != -1 if the token has been
|
indent*: int # the indentation; != -1 if the token has been
|
||||||
|
|
@ -110,6 +111,8 @@ type
|
||||||
fNumber*: BiggestFloat # the parsed floating point literal
|
fNumber*: BiggestFloat # the parsed floating point literal
|
||||||
base*: TNumericalBase # the numerical base; only valid for int
|
base*: TNumericalBase # the numerical base; only valid for int
|
||||||
# or float literals
|
# or float literals
|
||||||
|
strongSpaceA*: int8 # leading spaces of an operator
|
||||||
|
strongSpaceB*: int8 # trailing spaces of an operator
|
||||||
literal*: string # the parsed (string) literal; and
|
literal*: string # the parsed (string) literal; and
|
||||||
# documentation comments are here too
|
# documentation comments are here too
|
||||||
line*, col*: int
|
line*, col*: int
|
||||||
|
|
@ -119,6 +122,8 @@ type
|
||||||
indentAhead*: int # if > 0 an indendation has already been read
|
indentAhead*: int # if > 0 an indendation has already been read
|
||||||
# this is needed because scanning comments
|
# this is needed because scanning comments
|
||||||
# needs so much look-ahead
|
# needs so much look-ahead
|
||||||
|
currLineIndent*: int
|
||||||
|
strongSpaces*: bool
|
||||||
|
|
||||||
|
|
||||||
var gLinesCompiled*: int # all lines that have been compiled
|
var gLinesCompiled*: int # all lines that have been compiled
|
||||||
|
|
@ -173,6 +178,7 @@ proc prettyTok*(tok: TToken): string =
|
||||||
else: result = tokToStr(tok)
|
else: result = tokToStr(tok)
|
||||||
|
|
||||||
proc printTok*(tok: TToken) =
|
proc printTok*(tok: TToken) =
|
||||||
|
write(stdout, tok.line, ":", tok.col, "\t")
|
||||||
write(stdout, TokTypeToStr[tok.tokType])
|
write(stdout, TokTypeToStr[tok.tokType])
|
||||||
write(stdout, " ")
|
write(stdout, " ")
|
||||||
writeln(stdout, tokToStr(tok))
|
writeln(stdout, tokToStr(tok))
|
||||||
|
|
@ -183,6 +189,7 @@ proc initToken*(L: var TToken) =
|
||||||
L.tokType = tkInvalid
|
L.tokType = tkInvalid
|
||||||
L.iNumber = 0
|
L.iNumber = 0
|
||||||
L.indent = 0
|
L.indent = 0
|
||||||
|
L.strongSpaceA = 0
|
||||||
L.literal = ""
|
L.literal = ""
|
||||||
L.fNumber = 0.0
|
L.fNumber = 0.0
|
||||||
L.base = base10
|
L.base = base10
|
||||||
|
|
@ -192,6 +199,7 @@ proc fillToken(L: var TToken) =
|
||||||
L.tokType = tkInvalid
|
L.tokType = tkInvalid
|
||||||
L.iNumber = 0
|
L.iNumber = 0
|
||||||
L.indent = 0
|
L.indent = 0
|
||||||
|
L.strongSpaceA = 0
|
||||||
setLen(L.literal, 0)
|
setLen(L.literal, 0)
|
||||||
L.fNumber = 0.0
|
L.fNumber = 0.0
|
||||||
L.base = base10
|
L.base = base10
|
||||||
|
|
@ -201,6 +209,7 @@ proc openLexer(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) =
|
||||||
openBaseLexer(lex, inputstream)
|
openBaseLexer(lex, inputstream)
|
||||||
lex.fileIdx = fileidx
|
lex.fileIdx = fileidx
|
||||||
lex.indentAhead = - 1
|
lex.indentAhead = - 1
|
||||||
|
lex.currLineIndent = 0
|
||||||
inc(lex.lineNumber, inputstream.lineOffset)
|
inc(lex.lineNumber, inputstream.lineOffset)
|
||||||
|
|
||||||
proc closeLexer(lex: var TLexer) =
|
proc closeLexer(lex: var TLexer) =
|
||||||
|
|
@ -634,6 +643,14 @@ proc getOperator(L: var TLexer, tok: var TToken) =
|
||||||
h = h !& ord(c)
|
h = h !& ord(c)
|
||||||
inc(pos)
|
inc(pos)
|
||||||
endOperator(L, tok, pos, h)
|
endOperator(L, tok, pos, h)
|
||||||
|
# advance pos but don't store it in L.bufpos so the next token (which might
|
||||||
|
# be an operator too) gets the preceeding spaces:
|
||||||
|
tok.strongSpaceB = 0
|
||||||
|
while buf[pos] == ' ':
|
||||||
|
inc pos
|
||||||
|
inc tok.strongSpaceB
|
||||||
|
if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
||||||
|
tok.strongSpaceB = -1
|
||||||
|
|
||||||
proc scanComment(L: var TLexer, tok: var TToken) =
|
proc scanComment(L: var TLexer, tok: var TToken) =
|
||||||
var pos = L.bufpos
|
var pos = L.bufpos
|
||||||
|
|
@ -677,10 +694,12 @@ proc scanComment(L: var TLexer, tok: var TToken) =
|
||||||
proc skip(L: var TLexer, tok: var TToken) =
|
proc skip(L: var TLexer, tok: var TToken) =
|
||||||
var pos = L.bufpos
|
var pos = L.bufpos
|
||||||
var buf = L.buf
|
var buf = L.buf
|
||||||
|
tok.strongSpaceA = 0
|
||||||
while true:
|
while true:
|
||||||
case buf[pos]
|
case buf[pos]
|
||||||
of ' ':
|
of ' ':
|
||||||
inc(pos)
|
inc(pos)
|
||||||
|
inc(tok.strongSpaceA)
|
||||||
of Tabulator:
|
of Tabulator:
|
||||||
lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
|
lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
|
||||||
inc(pos)
|
inc(pos)
|
||||||
|
|
@ -691,8 +710,10 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||||
while buf[pos] == ' ':
|
while buf[pos] == ' ':
|
||||||
inc(pos)
|
inc(pos)
|
||||||
inc(indent)
|
inc(indent)
|
||||||
|
tok.strongSpaceA = 0
|
||||||
if buf[pos] > ' ':
|
if buf[pos] > ' ':
|
||||||
tok.indent = indent
|
tok.indent = indent
|
||||||
|
L.currLineIndent = indent
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
break # EndOfFile also leaves the loop
|
break # EndOfFile also leaves the loop
|
||||||
|
|
@ -702,6 +723,7 @@ proc rawGetTok(L: var TLexer, tok: var TToken) =
|
||||||
fillToken(tok)
|
fillToken(tok)
|
||||||
if L.indentAhead >= 0:
|
if L.indentAhead >= 0:
|
||||||
tok.indent = L.indentAhead
|
tok.indent = L.indentAhead
|
||||||
|
L.currLineIndent = L.indentAhead
|
||||||
L.indentAhead = -1
|
L.indentAhead = -1
|
||||||
else:
|
else:
|
||||||
tok.indent = -1
|
tok.indent = -1
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ proc considerAcc*(n: PNode): PIdent =
|
||||||
of nkSym: id.add(x.sym.name.s)
|
of nkSym: id.add(x.sym.name.s)
|
||||||
else: globalError(n.info, errIdentifierExpected, renderTree(n))
|
else: globalError(n.info, errIdentifierExpected, renderTree(n))
|
||||||
result = getIdent(id)
|
result = getIdent(id)
|
||||||
|
of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name
|
||||||
else:
|
else:
|
||||||
globalError(n.info, errIdentifierExpected, renderTree(n))
|
globalError(n.info, errIdentifierExpected, renderTree(n))
|
||||||
|
|
||||||
|
|
@ -91,7 +92,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
|
||||||
result.typ = errorType(c)
|
result.typ = errorType(c)
|
||||||
incl(result.flags, sfDiscardable)
|
incl(result.flags, sfDiscardable)
|
||||||
# pretend it's imported from some unknown module to prevent cascading errors:
|
# pretend it's imported from some unknown module to prevent cascading errors:
|
||||||
if gCmd != cmdInteractive:
|
if gCmd != cmdInteractive and c.inCompilesContext == 0:
|
||||||
c.importTable.addSym(result)
|
c.importTable.addSym(result)
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
@ -108,7 +109,7 @@ type
|
||||||
|
|
||||||
proc getSymRepr*(s: PSym): string =
|
proc getSymRepr*(s: PSym): string =
|
||||||
case s.kind
|
case s.kind
|
||||||
of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s)
|
of skProc, skMethod, skConverter, skIterators: result = getProcHeader(s)
|
||||||
else: result = s.name.s
|
else: result = s.name.s
|
||||||
|
|
||||||
proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
|
proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
|
||||||
|
|
@ -126,7 +127,10 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
|
||||||
elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
|
elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
|
||||||
# BUGFIX: check options in s!
|
# BUGFIX: check options in s!
|
||||||
if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
|
if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
|
||||||
message(s.info, hintXDeclaredButNotUsed, getSymRepr(s))
|
# XXX: implicit type params are currently skTypes
|
||||||
|
# maybe they can be made skGenericParam as well.
|
||||||
|
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags:
|
||||||
|
message(s.info, hintXDeclaredButNotUsed, getSymRepr(s))
|
||||||
s = nextIter(it, scope.symbols)
|
s = nextIter(it, scope.symbols)
|
||||||
|
|
||||||
proc wrongRedefinition*(info: TLineInfo, s: string) =
|
proc wrongRedefinition*(info: TLineInfo, s: string) =
|
||||||
|
|
|
||||||
52
compiler/lowerings.nim
Normal file
52
compiler/lowerings.nim
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nimrod Compiler
|
||||||
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module implements common simple lowerings.
|
||||||
|
|
||||||
|
const
|
||||||
|
genPrefix* = ":tmp" # prefix for generated names
|
||||||
|
|
||||||
|
import ast, types, idents, magicsys
|
||||||
|
|
||||||
|
proc newTupleAccess*(tup: PNode, i: int): PNode =
|
||||||
|
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
|
||||||
|
abstractInst).sons[i])
|
||||||
|
addSon(result, copyTree(tup))
|
||||||
|
var lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt))
|
||||||
|
lit.intVal = i
|
||||||
|
addSon(result, lit)
|
||||||
|
|
||||||
|
proc addVar*(father, v: PNode) =
|
||||||
|
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
||||||
|
vpart.sons[0] = v
|
||||||
|
vpart.sons[1] = ast.emptyNode
|
||||||
|
vpart.sons[2] = ast.emptyNode
|
||||||
|
addSon(father, vpart)
|
||||||
|
|
||||||
|
proc newAsgnStmt(le, ri: PNode): PNode =
|
||||||
|
result = newNodeI(nkAsgn, le.info, 2)
|
||||||
|
result.sons[0] = le
|
||||||
|
result.sons[1] = ri
|
||||||
|
|
||||||
|
proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
|
||||||
|
assert n.kind == nkVarTuple
|
||||||
|
let value = n.lastSon
|
||||||
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
|
||||||
|
var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info)
|
||||||
|
temp.typ = skipTypes(value.typ, abstractInst)
|
||||||
|
incl(temp.flags, sfFromGeneric)
|
||||||
|
|
||||||
|
var v = newNodeI(nkVarSection, value.info)
|
||||||
|
v.addVar(newSymNode(temp))
|
||||||
|
result.add(v)
|
||||||
|
|
||||||
|
result.add newAsgnStmt(newSymNode(temp), value)
|
||||||
|
for i in 0 .. n.len-3:
|
||||||
|
result.add newAsgnStmt(n.sons[i], newTupleAccess(value, i))
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# The Nimrod Compiler
|
# The Nimrod Compiler
|
||||||
# (c) Copyright 2013 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -20,7 +20,7 @@ type
|
||||||
TModuleInMemory* = object
|
TModuleInMemory* = object
|
||||||
compiledAt*: float
|
compiledAt*: float
|
||||||
crc*: TCrc32
|
crc*: TCrc32
|
||||||
deps*: seq[int32] ## XXX: slurped files are not currently tracked
|
deps*: seq[int32] ## XXX: slurped files are currently not tracked
|
||||||
needsRecompile*: TNeedRecompile
|
needsRecompile*: TNeedRecompile
|
||||||
crcStatus*: TCrcStatus
|
crcStatus*: TCrcStatus
|
||||||
|
|
||||||
|
|
@ -83,7 +83,7 @@ proc resetAllModules* =
|
||||||
for i in 0..gCompiledModules.high:
|
for i in 0..gCompiledModules.high:
|
||||||
if gCompiledModules[i] != nil:
|
if gCompiledModules[i] != nil:
|
||||||
resetModule(i.int32)
|
resetModule(i.int32)
|
||||||
|
resetPackageCache()
|
||||||
# for m in cgenModules(): echo "CGEN MODULE FOUND"
|
# for m in cgenModules(): echo "CGEN MODULE FOUND"
|
||||||
|
|
||||||
proc checkDepMem(fileIdx: int32): TNeedRecompile =
|
proc checkDepMem(fileIdx: int32): TNeedRecompile =
|
||||||
|
|
@ -120,8 +120,9 @@ proc newModule(fileIdx: int32): PSym =
|
||||||
if not isNimrodIdentifier(result.name.s):
|
if not isNimrodIdentifier(result.name.s):
|
||||||
rawMessage(errInvalidModuleName, result.name.s)
|
rawMessage(errInvalidModuleName, result.name.s)
|
||||||
|
|
||||||
result.owner = result # a module belongs to itself
|
|
||||||
result.info = newLineInfo(fileIdx, 1, 1)
|
result.info = newLineInfo(fileIdx, 1, 1)
|
||||||
|
result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil,
|
||||||
|
result.info)
|
||||||
result.position = fileIdx
|
result.position = fileIdx
|
||||||
|
|
||||||
growCache gMemCacheData, fileIdx
|
growCache gMemCacheData, fileIdx
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ type
|
||||||
errAmbiguousCallXYZ, errWrongNumberOfArguments,
|
errAmbiguousCallXYZ, errWrongNumberOfArguments,
|
||||||
errXCannotBePassedToProcVar,
|
errXCannotBePassedToProcVar,
|
||||||
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed,
|
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed,
|
||||||
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValue,
|
errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
|
||||||
errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
|
errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
|
||||||
errInvalidOrderInArrayConstructor,
|
errInvalidOrderInArrayConstructor,
|
||||||
errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,
|
errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,
|
||||||
|
|
@ -89,12 +89,14 @@ type
|
||||||
errInvalidIndexValueForTuple, errCommandExpectsFilename,
|
errInvalidIndexValueForTuple, errCommandExpectsFilename,
|
||||||
errMainModuleMustBeSpecified,
|
errMainModuleMustBeSpecified,
|
||||||
errXExpected,
|
errXExpected,
|
||||||
|
errTIsNotAConcreteType,
|
||||||
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
|
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
|
||||||
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
|
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
|
||||||
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
|
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
|
||||||
errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
|
errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
|
||||||
|
errMacroBodyDependsOnGenericTypes,
|
||||||
errDestructorNotGenericEnough,
|
errDestructorNotGenericEnough,
|
||||||
|
errInlineIteratorsAsProcParams,
|
||||||
errXExpectsTwoArguments,
|
errXExpectsTwoArguments,
|
||||||
errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
|
errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
|
||||||
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
|
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
|
||||||
|
|
@ -103,6 +105,10 @@ type
|
||||||
errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
|
errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
|
||||||
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
|
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
|
||||||
errXCannotBeClosure, errXMustBeCompileTime,
|
errXCannotBeClosure, errXMustBeCompileTime,
|
||||||
|
errCannotInferTypeOfTheLiteral,
|
||||||
|
errCannotInferReturnType,
|
||||||
|
errGenericLambdaNotAllowed,
|
||||||
|
errCompilerDoesntSupportTarget,
|
||||||
errUser,
|
errUser,
|
||||||
warnCannotOpenFile,
|
warnCannotOpenFile,
|
||||||
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
|
||||||
|
|
@ -196,7 +202,7 @@ const
|
||||||
errXExpectsArrayType: "\'$1\' expects an array type",
|
errXExpectsArrayType: "\'$1\' expects an array type",
|
||||||
errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet",
|
errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet",
|
||||||
errExprXAmbiguous: "expression '$1' ambiguous in this context",
|
errExprXAmbiguous: "expression '$1' ambiguous in this context",
|
||||||
errConstantDivisionByZero: "constant division by zero",
|
errConstantDivisionByZero: "division by zero",
|
||||||
errOrdinalTypeExpected: "ordinal type expected",
|
errOrdinalTypeExpected: "ordinal type expected",
|
||||||
errOrdinalOrFloatTypeExpected: "ordinal or float type expected",
|
errOrdinalOrFloatTypeExpected: "ordinal or float type expected",
|
||||||
errOverOrUnderflow: "over- or underflow",
|
errOverOrUnderflow: "over- or underflow",
|
||||||
|
|
@ -263,7 +269,7 @@ const
|
||||||
errImplOfXNotAllowed: "implementation of \'$1\' is not allowed",
|
errImplOfXNotAllowed: "implementation of \'$1\' is not allowed",
|
||||||
errImplOfXexpected: "implementation of \'$1\' expected",
|
errImplOfXexpected: "implementation of \'$1\' expected",
|
||||||
errNoSymbolToBorrowFromFound: "no symbol to borrow from found",
|
errNoSymbolToBorrowFromFound: "no symbol to borrow from found",
|
||||||
errDiscardValue: "value returned by statement has to be discarded",
|
errDiscardValueX: "value of type '$1' has to be discarded",
|
||||||
errInvalidDiscard: "statement returns no value that can be discarded",
|
errInvalidDiscard: "statement returns no value that can be discarded",
|
||||||
errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid",
|
errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid",
|
||||||
errCannotBindXTwice: "cannot bind parameter \'$1\' twice",
|
errCannotBindXTwice: "cannot bind parameter \'$1\' twice",
|
||||||
|
|
@ -312,6 +318,7 @@ const
|
||||||
errCommandExpectsFilename: "command expects a filename argument",
|
errCommandExpectsFilename: "command expects a filename argument",
|
||||||
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
|
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
|
||||||
errXExpected: "\'$1\' expected",
|
errXExpected: "\'$1\' expected",
|
||||||
|
errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
|
||||||
errInvalidSectionStart: "invalid section start",
|
errInvalidSectionStart: "invalid section start",
|
||||||
errGridTableNotImplemented: "grid table is not implemented",
|
errGridTableNotImplemented: "grid table is not implemented",
|
||||||
errGeneralParseError: "general parse error",
|
errGeneralParseError: "general parse error",
|
||||||
|
|
@ -323,8 +330,12 @@ const
|
||||||
errInstantiateXExplicitely: "instantiate '$1' explicitely",
|
errInstantiateXExplicitely: "instantiate '$1' explicitely",
|
||||||
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
|
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
|
||||||
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
|
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
|
||||||
|
errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
|
||||||
|
"because the parameter '$1' has a generic type",
|
||||||
errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
|
errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
|
||||||
"A destructor must be associated will all instantiations of a generic type",
|
"A destructor must be associated will all instantiations of a generic type",
|
||||||
|
errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
|
||||||
|
"templates, macros and other inline iterators",
|
||||||
errXExpectsTwoArguments: "\'$1\' expects two arguments",
|
errXExpectsTwoArguments: "\'$1\' expects two arguments",
|
||||||
errXExpectsObjectTypes: "\'$1\' expects object types",
|
errXExpectsObjectTypes: "\'$1\' expects object types",
|
||||||
errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype",
|
errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype",
|
||||||
|
|
@ -346,6 +357,12 @@ const
|
||||||
errIllegalCaptureX: "illegal capture '$1'",
|
errIllegalCaptureX: "illegal capture '$1'",
|
||||||
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
|
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
|
||||||
errXMustBeCompileTime: "'$1' can only be used in compile-time context",
|
errXMustBeCompileTime: "'$1' can only be used in compile-time context",
|
||||||
|
errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
|
||||||
|
errCannotInferReturnType: "cannot infer the return type of the proc",
|
||||||
|
errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " &
|
||||||
|
"it is used as an operand to another routine and the types " &
|
||||||
|
"of the generic paramers can be infered from the expected signature.",
|
||||||
|
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
|
||||||
errUser: "$1",
|
errUser: "$1",
|
||||||
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
|
||||||
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@ proc execute*(program: string) =
|
||||||
passes.gIncludeFile = includeModule
|
passes.gIncludeFile = includeModule
|
||||||
passes.gImportModule = importModule
|
passes.gImportModule = importModule
|
||||||
initDefines()
|
initDefines()
|
||||||
LoadConfigs(DefaultConfig)
|
loadConfigs(DefaultConfig)
|
||||||
|
|
||||||
initDefines()
|
initDefines()
|
||||||
DefineSymbol("nimrodvm")
|
defineSymbol("nimrodvm")
|
||||||
when hasFFI: DefineSymbol("nimffi")
|
when hasFFI: defineSymbol("nimffi")
|
||||||
registerPass(verbosePass)
|
registerPass(verbosePass)
|
||||||
registerPass(semPass)
|
registerPass(semPass)
|
||||||
registerPass(vmPass)
|
registerPass(vmPass)
|
||||||
|
|
@ -30,4 +30,4 @@ proc execute*(program: string) =
|
||||||
compileSystemModule()
|
compileSystemModule()
|
||||||
var m = makeStdinModule()
|
var m = makeStdinModule()
|
||||||
incl(m.flags, sfMainModule)
|
incl(m.flags, sfMainModule)
|
||||||
processModule(m, LLStreamOpen(program), nil)
|
processModule(m, llStreamOpen(program), nil)
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,6 @@ import:testability
|
||||||
cincludes: "$lib/wrappers/libffi/common"
|
cincludes: "$lib/wrappers/libffi/common"
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
define:useStdoutAsStdmsg
|
||||||
|
|
||||||
cs:partial
|
cs:partial
|
||||||
|
|
|
||||||
|
|
@ -209,21 +209,42 @@ proc getGeneratedPath: string =
|
||||||
result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
|
result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
|
||||||
genSubDir
|
genSubDir
|
||||||
|
|
||||||
|
template newPackageCache(): expr =
|
||||||
|
newStringTable(when FileSystemCaseSensitive:
|
||||||
|
modeCaseInsensitive
|
||||||
|
else:
|
||||||
|
modeCaseSensitive)
|
||||||
|
|
||||||
|
var packageCache = newPackageCache()
|
||||||
|
|
||||||
|
proc resetPackageCache*() = packageCache = newPackageCache()
|
||||||
|
|
||||||
|
iterator myParentDirs(p: string): string =
|
||||||
|
# XXX os's parentDirs is stupid (multiple yields) and triggers an old bug...
|
||||||
|
var current = p
|
||||||
|
while true:
|
||||||
|
current = current.parentDir
|
||||||
|
if current.len == 0: break
|
||||||
|
yield current
|
||||||
|
|
||||||
proc getPackageName*(path: string): string =
|
proc getPackageName*(path: string): string =
|
||||||
var q = 1
|
var parents = 0
|
||||||
var b = 0
|
block packageSearch:
|
||||||
if path[len(path)-1] in {DirSep, AltSep}: q = 2
|
for d in myParentDirs(path):
|
||||||
for i in countdown(len(path)-q, 0):
|
if packageCache.hasKey(d):
|
||||||
if path[i] in {DirSep, AltSep}:
|
#echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name
|
||||||
if b == 0: b = i
|
return packageCache[d]
|
||||||
else:
|
inc parents
|
||||||
let x = path.substr(i+1, b-1)
|
for file in walkFiles(d / "*.babel"):
|
||||||
case x.normalize
|
result = file.splitFile.name
|
||||||
of "lib", "src", "source", "package", "pckg", "library", "private":
|
break packageSearch
|
||||||
b = i
|
# we also store if we didn't find anything:
|
||||||
else:
|
if result.isNil: result = ""
|
||||||
return x.replace('.', '_')
|
for d in myParentDirs(path):
|
||||||
result = ""
|
#echo "set cache ", d, " |", result, "|", parents
|
||||||
|
packageCache[d] = result
|
||||||
|
dec parents
|
||||||
|
if parents <= 0: break
|
||||||
|
|
||||||
proc withPackageName*(path: string): string =
|
proc withPackageName*(path: string): string =
|
||||||
let x = path.getPackageName
|
let x = path.getPackageName
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ type
|
||||||
inSemiStmtList: int
|
inSemiStmtList: int
|
||||||
|
|
||||||
proc parseAll*(p: var TParser): PNode
|
proc parseAll*(p: var TParser): PNode
|
||||||
proc openParser*(p: var TParser, filename: string, inputstream: PLLStream)
|
|
||||||
proc closeParser*(p: var TParser)
|
proc closeParser*(p: var TParser)
|
||||||
proc parseTopLevelStmt*(p: var TParser): PNode
|
proc parseTopLevelStmt*(p: var TParser): PNode
|
||||||
# implements an iterator. Returns the next top-level statement or
|
# implements an iterator. Returns the next top-level statement or
|
||||||
|
|
@ -50,7 +49,6 @@ proc parseString*(s: string, filename: string = "", line: int = 0): PNode
|
||||||
# correct error messages referring to the original source.
|
# correct error messages referring to the original source.
|
||||||
|
|
||||||
# helpers for the other parsers
|
# helpers for the other parsers
|
||||||
proc getPrecedence*(tok: TToken): int
|
|
||||||
proc isOperator*(tok: TToken): bool
|
proc isOperator*(tok: TToken): bool
|
||||||
proc getTok*(p: var TParser)
|
proc getTok*(p: var TParser)
|
||||||
proc parMessage*(p: TParser, msg: TMsgKind, arg: string = "")
|
proc parMessage*(p: TParser, msg: TMsgKind, arg: string = "")
|
||||||
|
|
@ -69,7 +67,7 @@ proc optPar*(p: var TParser)
|
||||||
proc optInd*(p: var TParser, n: PNode)
|
proc optInd*(p: var TParser, n: PNode)
|
||||||
proc indAndComment*(p: var TParser, n: PNode)
|
proc indAndComment*(p: var TParser, n: PNode)
|
||||||
proc setBaseFlags*(n: PNode, base: TNumericalBase)
|
proc setBaseFlags*(n: PNode, base: TNumericalBase)
|
||||||
proc parseSymbol*(p: var TParser): PNode
|
proc parseSymbol*(p: var TParser, allowNil = false): PNode
|
||||||
proc parseTry(p: var TParser): PNode
|
proc parseTry(p: var TParser): PNode
|
||||||
proc parseCase(p: var TParser): PNode
|
proc parseCase(p: var TParser): PNode
|
||||||
# implementation
|
# implementation
|
||||||
|
|
@ -77,14 +75,17 @@ proc parseCase(p: var TParser): PNode
|
||||||
proc getTok(p: var TParser) =
|
proc getTok(p: var TParser) =
|
||||||
rawGetTok(p.lex, p.tok)
|
rawGetTok(p.lex, p.tok)
|
||||||
|
|
||||||
proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream) =
|
proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream,
|
||||||
|
strongSpaces=false) =
|
||||||
initToken(p.tok)
|
initToken(p.tok)
|
||||||
openLexer(p.lex, fileIdx, inputStream)
|
openLexer(p.lex, fileIdx, inputStream)
|
||||||
getTok(p) # read the first token
|
getTok(p) # read the first token
|
||||||
p.firstTok = true
|
p.firstTok = true
|
||||||
|
p.strongSpaces = strongSpaces
|
||||||
|
|
||||||
proc openParser*(p: var TParser, filename: string, inputStream: PLLStream) =
|
proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
|
||||||
openParser(p, filename.fileInfoIdx, inputstream)
|
strongSpaces=false) =
|
||||||
|
openParser(p, filename.fileInfoIdx, inputstream, strongSpaces)
|
||||||
|
|
||||||
proc closeParser(p: var TParser) =
|
proc closeParser(p: var TParser) =
|
||||||
closeLexer(p.lex)
|
closeLexer(p.lex)
|
||||||
|
|
@ -193,34 +194,52 @@ proc isSigilLike(tok: TToken): bool {.inline.} =
|
||||||
proc isLeftAssociative(tok: TToken): bool {.inline.} =
|
proc isLeftAssociative(tok: TToken): bool {.inline.} =
|
||||||
result = tok.tokType != tkOpr or relevantOprChar(tok.ident) != '^'
|
result = tok.tokType != tkOpr or relevantOprChar(tok.ident) != '^'
|
||||||
|
|
||||||
proc getPrecedence(tok: TToken): int =
|
proc getPrecedence(tok: TToken, strongSpaces: bool): int =
|
||||||
|
template considerStrongSpaces(x): expr =
|
||||||
|
x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0)
|
||||||
|
|
||||||
case tok.tokType
|
case tok.tokType
|
||||||
of tkOpr:
|
of tkOpr:
|
||||||
let L = tok.ident.s.len
|
let L = tok.ident.s.len
|
||||||
let relevantChar = relevantOprChar(tok.ident)
|
let relevantChar = relevantOprChar(tok.ident)
|
||||||
|
|
||||||
template considerAsgn(value: expr) =
|
template considerAsgn(value: expr) =
|
||||||
result = if tok.ident.s[L-1] == '=': 1 else: value
|
result = if tok.ident.s[L-1] == '=': 1 else: considerStrongSpaces(value)
|
||||||
|
|
||||||
case relevantChar
|
case relevantChar
|
||||||
of '$', '^': considerAsgn(10)
|
of '$', '^': considerAsgn(10)
|
||||||
of '*', '%', '/', '\\': considerAsgn(9)
|
of '*', '%', '/', '\\': considerAsgn(9)
|
||||||
of '~': result = 8
|
of '~': result = considerStrongSpaces(8)
|
||||||
of '+', '-', '|': considerAsgn(8)
|
of '+', '-', '|': considerAsgn(8)
|
||||||
of '&': considerAsgn(7)
|
of '&': considerAsgn(7)
|
||||||
of '=', '<', '>', '!': result = 5
|
of '=', '<', '>', '!': result = considerStrongSpaces(5)
|
||||||
of '.': considerAsgn(6)
|
of '.': considerAsgn(6)
|
||||||
of '?': result = 2
|
of '?': result = considerStrongSpaces(2)
|
||||||
else: considerAsgn(2)
|
else: considerAsgn(2)
|
||||||
of tkDiv, tkMod, tkShl, tkShr: result = 9
|
of tkDiv, tkMod, tkShl, tkShr: result = 9
|
||||||
of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5
|
of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5
|
||||||
of tkDotDot: result = 6
|
of tkDotDot: result = considerStrongSpaces(6)
|
||||||
of tkAnd: result = 4
|
of tkAnd: result = 4
|
||||||
of tkOr, tkXor: result = 3
|
of tkOr, tkXor: result = 3
|
||||||
else: result = - 10
|
else: result = -10
|
||||||
|
|
||||||
proc isOperator(tok: TToken): bool =
|
proc isOperator(tok: TToken): bool =
|
||||||
result = getPrecedence(tok) >= 0
|
tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
|
||||||
|
tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor}
|
||||||
|
|
||||||
|
proc isUnary(p: TParser): bool =
|
||||||
|
p.strongSpaces and p.tok.tokType in {tkOpr, tkDotDot} and
|
||||||
|
p.tok.strongSpaceB == 0 and
|
||||||
|
p.tok.strongSpaceA > 0
|
||||||
|
|
||||||
|
proc checkBinary(p: TParser) {.inline.} =
|
||||||
|
# we don't check '..' here as that's too annoying
|
||||||
|
if p.strongSpaces and p.tok.tokType == tkOpr:
|
||||||
|
if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB:
|
||||||
|
parMessage(p, errGenerated, "number of spaces around '$#' not consistent"%
|
||||||
|
prettyTok(p.tok))
|
||||||
|
elif p.tok.strongSpaceA notin {0,1,2,4,8}:
|
||||||
|
parMessage(p, errGenerated, "number of spaces must be 0,1,2,4 or 8")
|
||||||
|
|
||||||
#| module = stmt ^* (';' / IND{=})
|
#| module = stmt ^* (';' / IND{=})
|
||||||
#|
|
#|
|
||||||
|
|
@ -254,7 +273,7 @@ proc colcom(p: var TParser, n: PNode) =
|
||||||
eat(p, tkColon)
|
eat(p, tkColon)
|
||||||
skipComment(p, n)
|
skipComment(p, n)
|
||||||
|
|
||||||
proc parseSymbol(p: var TParser): PNode =
|
proc parseSymbol(p: var TParser, allowNil = false): PNode =
|
||||||
#| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`'
|
#| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`'
|
||||||
#| | IDENT
|
#| | IDENT
|
||||||
case p.tok.tokType
|
case p.tok.tokType
|
||||||
|
|
@ -281,7 +300,7 @@ proc parseSymbol(p: var TParser): PNode =
|
||||||
add(result, newIdentNodeP(getIdent"{}", p))
|
add(result, newIdentNodeP(getIdent"{}", p))
|
||||||
getTok(p)
|
getTok(p)
|
||||||
eat(p, tkCurlyRi)
|
eat(p, tkCurlyRi)
|
||||||
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDotDot:
|
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDot, tkDotDot:
|
||||||
add(result, newIdentNodeP(p.tok.ident, p))
|
add(result, newIdentNodeP(p.tok.ident, p))
|
||||||
getTok(p)
|
getTok(p)
|
||||||
of tkIntLit..tkCharLit:
|
of tkIntLit..tkCharLit:
|
||||||
|
|
@ -293,9 +312,13 @@ proc parseSymbol(p: var TParser): PNode =
|
||||||
break
|
break
|
||||||
eat(p, tkAccent)
|
eat(p, tkAccent)
|
||||||
else:
|
else:
|
||||||
parMessage(p, errIdentifierExpected, p.tok)
|
if allowNil and p.tok.tokType == tkNil:
|
||||||
getTok(p) # BUGFIX: We must consume a token here to prevent endless loops!
|
result = newNodeP(nkNilLit, p)
|
||||||
result = ast.emptyNode
|
getTok(p)
|
||||||
|
else:
|
||||||
|
parMessage(p, errIdentifierExpected, p.tok)
|
||||||
|
getTok(p) # BUGFIX: We must consume a token here to prevent endless loops!
|
||||||
|
result = ast.emptyNode
|
||||||
|
|
||||||
proc indexExpr(p: var TParser): PNode =
|
proc indexExpr(p: var TParser): PNode =
|
||||||
#| indexExpr = expr
|
#| indexExpr = expr
|
||||||
|
|
@ -502,6 +525,8 @@ proc parsePar(p: var TParser): PNode =
|
||||||
asgn.sons[0] = a
|
asgn.sons[0] = a
|
||||||
asgn.sons[1] = b
|
asgn.sons[1] = b
|
||||||
result.add(asgn)
|
result.add(asgn)
|
||||||
|
if p.tok.tokType == tkSemiColon:
|
||||||
|
semiStmtList(p, result)
|
||||||
elif p.tok.tokType == tkSemiColon:
|
elif p.tok.tokType == tkSemiColon:
|
||||||
# stmt context:
|
# stmt context:
|
||||||
result.add(a)
|
result.add(a)
|
||||||
|
|
@ -639,7 +664,7 @@ proc namedParams(p: var TParser, callee: PNode,
|
||||||
exprColonEqExprListAux(p, endTok, result)
|
exprColonEqExprListAux(p, endTok, result)
|
||||||
|
|
||||||
proc parseMacroColon(p: var TParser, x: PNode): PNode
|
proc parseMacroColon(p: var TParser, x: PNode): PNode
|
||||||
proc primarySuffix(p: var TParser, r: PNode): PNode =
|
proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
|
||||||
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
|
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
|
||||||
#| | doBlocks
|
#| | doBlocks
|
||||||
#| | '.' optInd ('type' | 'addr' | symbol) generalizedLit?
|
#| | '.' optInd ('type' | 'addr' | symbol) generalizedLit?
|
||||||
|
|
@ -647,9 +672,11 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
|
||||||
#| | '{' optInd indexExprList optPar '}'
|
#| | '{' optInd indexExprList optPar '}'
|
||||||
#| | &( '`'|IDENT|literal|'cast') expr # command syntax
|
#| | &( '`'|IDENT|literal|'cast') expr # command syntax
|
||||||
result = r
|
result = r
|
||||||
while p.tok.indent < 0:
|
while p.tok.indent < 0 or
|
||||||
|
(p.tok.tokType == tkDot and p.tok.indent >= baseIndent):
|
||||||
case p.tok.tokType
|
case p.tok.tokType
|
||||||
of tkParLe:
|
of tkParLe:
|
||||||
|
if p.strongSpaces and p.tok.strongSpaceA > 0: break
|
||||||
result = namedParams(p, result, nkCall, tkParRi)
|
result = namedParams(p, result, nkCall, tkParRi)
|
||||||
if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
|
if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
|
||||||
result.kind = nkObjConstr
|
result.kind = nkObjConstr
|
||||||
|
|
@ -664,8 +691,10 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
|
||||||
result = dotExpr(p, result)
|
result = dotExpr(p, result)
|
||||||
result = parseGStrLit(p, result)
|
result = parseGStrLit(p, result)
|
||||||
of tkBracketLe:
|
of tkBracketLe:
|
||||||
|
if p.strongSpaces and p.tok.strongSpaceA > 0: break
|
||||||
result = namedParams(p, result, nkBracketExpr, tkBracketRi)
|
result = namedParams(p, result, nkBracketExpr, tkBracketRi)
|
||||||
of tkCurlyLe:
|
of tkCurlyLe:
|
||||||
|
if p.strongSpaces and p.tok.strongSpaceA > 0: break
|
||||||
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
|
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
|
||||||
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast:
|
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast:
|
||||||
if p.inPragma == 0:
|
if p.inPragma == 0:
|
||||||
|
|
@ -691,14 +720,17 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
|
||||||
break
|
break
|
||||||
|
|
||||||
proc primary(p: var TParser, mode: TPrimaryMode): PNode
|
proc primary(p: var TParser, mode: TPrimaryMode): PNode
|
||||||
|
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode
|
||||||
|
|
||||||
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
|
proc parseOperators(p: var TParser, headNode: PNode,
|
||||||
result = primary(p, mode)
|
limit: int, mode: TPrimaryMode): PNode =
|
||||||
|
result = headNode
|
||||||
# expand while operators have priorities higher than 'limit'
|
# expand while operators have priorities higher than 'limit'
|
||||||
var opPrec = getPrecedence(p.tok)
|
var opPrec = getPrecedence(p.tok, p.strongSpaces)
|
||||||
let modeB = if mode == pmTypeDef: pmTypeDesc else: mode
|
let modeB = if mode == pmTypeDef: pmTypeDesc else: mode
|
||||||
# the operator itself must not start on a new line:
|
# the operator itself must not start on a new line:
|
||||||
while opPrec >= limit and p.tok.indent < 0:
|
while opPrec >= limit and p.tok.indent < 0 and not isUnary(p):
|
||||||
|
checkBinary(p)
|
||||||
var leftAssoc = ord(isLeftAssociative(p.tok))
|
var leftAssoc = ord(isLeftAssociative(p.tok))
|
||||||
var a = newNodeP(nkInfix, p)
|
var a = newNodeP(nkInfix, p)
|
||||||
var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
|
var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
|
||||||
|
|
@ -710,7 +742,11 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
|
||||||
addSon(a, result)
|
addSon(a, result)
|
||||||
addSon(a, b)
|
addSon(a, b)
|
||||||
result = a
|
result = a
|
||||||
opPrec = getPrecedence(p.tok)
|
opPrec = getPrecedence(p.tok, p.strongSpaces)
|
||||||
|
|
||||||
|
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
|
||||||
|
result = primary(p, mode)
|
||||||
|
result = parseOperators(p, result, limit, mode)
|
||||||
|
|
||||||
proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
|
proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
|
||||||
result = simpleExprAux(p, -1, mode)
|
result = simpleExprAux(p, -1, mode)
|
||||||
|
|
@ -935,6 +971,15 @@ proc isExprStart(p: TParser): bool =
|
||||||
result = true
|
result = true
|
||||||
else: result = false
|
else: result = false
|
||||||
|
|
||||||
|
proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) =
|
||||||
|
while true:
|
||||||
|
var s = parseSymbol(p, allowNil)
|
||||||
|
if s.kind == nkEmpty: break
|
||||||
|
addSon(result, s)
|
||||||
|
if p.tok.tokType != tkComma: break
|
||||||
|
getTok(p)
|
||||||
|
optInd(p, s)
|
||||||
|
|
||||||
proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
|
proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
|
||||||
mode: TPrimaryMode): PNode =
|
mode: TPrimaryMode): PNode =
|
||||||
result = newNodeP(kind, p)
|
result = newNodeP(kind, p)
|
||||||
|
|
@ -942,6 +987,13 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
|
||||||
optInd(p, result)
|
optInd(p, result)
|
||||||
if not isOperator(p.tok) and isExprStart(p):
|
if not isOperator(p.tok) and isExprStart(p):
|
||||||
addSon(result, primary(p, mode))
|
addSon(result, primary(p, mode))
|
||||||
|
if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}:
|
||||||
|
let nodeKind = if p.tok.tokType == tkWith: nkWith
|
||||||
|
else: nkWithout
|
||||||
|
getTok(p)
|
||||||
|
let list = newNodeP(nodeKind, p)
|
||||||
|
result.addSon list
|
||||||
|
parseSymbolList(p, list, allowNil = true)
|
||||||
|
|
||||||
proc parseExpr(p: var TParser): PNode =
|
proc parseExpr(p: var TParser): PNode =
|
||||||
#| expr = (ifExpr
|
#| expr = (ifExpr
|
||||||
|
|
@ -958,7 +1010,6 @@ proc parseExpr(p: var TParser): PNode =
|
||||||
|
|
||||||
proc parseEnum(p: var TParser): PNode
|
proc parseEnum(p: var TParser): PNode
|
||||||
proc parseObject(p: var TParser): PNode
|
proc parseObject(p: var TParser): PNode
|
||||||
proc parseDistinct(p: var TParser): PNode
|
|
||||||
proc parseTypeClass(p: var TParser): PNode
|
proc parseTypeClass(p: var TParser): PNode
|
||||||
|
|
||||||
proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
|
|
@ -978,8 +1029,9 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
optInd(p, a)
|
optInd(p, a)
|
||||||
if isSigil:
|
if isSigil:
|
||||||
#XXX prefix operators
|
#XXX prefix operators
|
||||||
|
let baseInd = p.lex.currLineIndent
|
||||||
addSon(result, primary(p, pmSkipSuffix))
|
addSon(result, primary(p, pmSkipSuffix))
|
||||||
result = primarySuffix(p, result)
|
result = primarySuffix(p, result, baseInd)
|
||||||
else:
|
else:
|
||||||
addSon(result, primary(p, pmNormal))
|
addSon(result, primary(p, pmNormal))
|
||||||
return
|
return
|
||||||
|
|
@ -1042,9 +1094,10 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
optInd(p, result)
|
optInd(p, result)
|
||||||
addSon(result, primary(p, pmNormal))
|
addSon(result, primary(p, pmNormal))
|
||||||
else:
|
else:
|
||||||
|
let baseInd = p.lex.currLineIndent
|
||||||
result = identOrLiteral(p, mode)
|
result = identOrLiteral(p, mode)
|
||||||
if mode != pmSkipSuffix:
|
if mode != pmSkipSuffix:
|
||||||
result = primarySuffix(p, result)
|
result = primarySuffix(p, result, baseInd)
|
||||||
|
|
||||||
proc parseTypeDesc(p: var TParser): PNode =
|
proc parseTypeDesc(p: var TParser): PNode =
|
||||||
#| typeDesc = simpleExpr
|
#| typeDesc = simpleExpr
|
||||||
|
|
@ -1478,7 +1531,7 @@ proc parseSection(p: var TParser, kind: TNodeKind,
|
||||||
defparser: TDefParser): PNode =
|
defparser: TDefParser): PNode =
|
||||||
#| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED)
|
#| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED)
|
||||||
result = newNodeP(kind, p)
|
result = newNodeP(kind, p)
|
||||||
getTok(p)
|
if kind != nkTypeSection: getTok(p)
|
||||||
skipComment(p, result)
|
skipComment(p, result)
|
||||||
if realInd(p):
|
if realInd(p):
|
||||||
withInd(p):
|
withInd(p):
|
||||||
|
|
@ -1711,13 +1764,6 @@ proc parseTypeClass(p: var TParser): PNode =
|
||||||
else:
|
else:
|
||||||
addSon(result, parseStmt(p))
|
addSon(result, parseStmt(p))
|
||||||
|
|
||||||
proc parseDistinct(p: var TParser): PNode =
|
|
||||||
#| distinct = 'distinct' optInd typeDesc
|
|
||||||
result = newNodeP(nkDistinctTy, p)
|
|
||||||
getTok(p)
|
|
||||||
optInd(p, result)
|
|
||||||
addSon(result, parseTypeDesc(p))
|
|
||||||
|
|
||||||
proc parseTypeDef(p: var TParser): PNode =
|
proc parseTypeDef(p: var TParser): PNode =
|
||||||
#| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
|
#| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux
|
||||||
#| indAndComment?
|
#| indAndComment?
|
||||||
|
|
@ -1839,7 +1885,16 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
|
||||||
of tkMacro: result = parseRoutine(p, nkMacroDef)
|
of tkMacro: result = parseRoutine(p, nkMacroDef)
|
||||||
of tkTemplate: result = parseRoutine(p, nkTemplateDef)
|
of tkTemplate: result = parseRoutine(p, nkTemplateDef)
|
||||||
of tkConverter: result = parseRoutine(p, nkConverterDef)
|
of tkConverter: result = parseRoutine(p, nkConverterDef)
|
||||||
of tkType: result = parseSection(p, nkTypeSection, parseTypeDef)
|
of tkType:
|
||||||
|
getTok(p)
|
||||||
|
if p.tok.tokType == tkParLe:
|
||||||
|
getTok(p)
|
||||||
|
result = newNodeP(nkTypeOfExpr, p)
|
||||||
|
result.addSon(primary(p, pmTypeDesc))
|
||||||
|
eat(p, tkParRi)
|
||||||
|
result = parseOperators(p, result, -1, pmNormal)
|
||||||
|
else:
|
||||||
|
result = parseSection(p, nkTypeSection, parseTypeDef)
|
||||||
of tkConst: result = parseSection(p, nkConstSection, parseConstant)
|
of tkConst: result = parseSection(p, nkConstSection, parseConstant)
|
||||||
of tkLet: result = parseSection(p, nkLetSection, parseVariable)
|
of tkLet: result = parseSection(p, nkLetSection, parseVariable)
|
||||||
of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
|
of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
|
||||||
|
|
@ -1863,7 +1918,7 @@ proc parseStmt(p: var TParser): PNode =
|
||||||
if p.tok.indent < 0 or p.tok.indent == p.currInd: discard
|
if p.tok.indent < 0 or p.tok.indent == p.currInd: discard
|
||||||
else: break
|
else: break
|
||||||
else:
|
else:
|
||||||
if p.tok.indent > p.currInd:
|
if p.tok.indent > p.currInd and p.tok.tokType != tkDot:
|
||||||
parMessage(p, errInvalidIndentation)
|
parMessage(p, errInvalidIndentation)
|
||||||
break
|
break
|
||||||
if p.tok.tokType in {tkCurlyRi, tkParRi, tkCurlyDotRi, tkBracketRi}:
|
if p.tok.tokType in {tkCurlyRi, tkParRi, tkCurlyDotRi, tkBracketRi}:
|
||||||
|
|
@ -1890,7 +1945,8 @@ proc parseStmt(p: var TParser): PNode =
|
||||||
else:
|
else:
|
||||||
result = newNodeP(nkStmtList, p)
|
result = newNodeP(nkStmtList, p)
|
||||||
while true:
|
while true:
|
||||||
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
|
if p.tok.indent >= 0:
|
||||||
|
parMessage(p, errInvalidIndentation)
|
||||||
let a = simpleStmt(p)
|
let a = simpleStmt(p)
|
||||||
if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
||||||
result.add(a)
|
result.add(a)
|
||||||
|
|
@ -1933,7 +1989,9 @@ proc parseString(s: string, filename: string = "", line: int = 0): PNode =
|
||||||
stream.lineOffset = line
|
stream.lineOffset = line
|
||||||
|
|
||||||
var parser: TParser
|
var parser: TParser
|
||||||
openParser(parser, filename, stream)
|
# XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
|
||||||
|
# spaces...
|
||||||
|
openParser(parser, filename, stream, false)
|
||||||
|
|
||||||
result = parser.parseAll
|
result = parser.parseAll
|
||||||
closeParser(parser)
|
closeParser(parser)
|
||||||
|
|
|
||||||
|
|
@ -342,7 +342,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
|
||||||
h = h +% ord(c)
|
h = h +% ord(c)
|
||||||
h = h +% h shl 10
|
h = h +% h shl 10
|
||||||
h = h xor (h shr 6)
|
h = h xor (h shr 6)
|
||||||
of '_': nil
|
of '_': discard
|
||||||
else: break
|
else: break
|
||||||
inc(pos)
|
inc(pos)
|
||||||
h = h +% h shl 3
|
h = h +% h shl 3
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,7 @@ proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind,
|
||||||
|
|
||||||
proc setBaseFlags(n: PNode, base: TNumericalBase) =
|
proc setBaseFlags(n: PNode, base: TNumericalBase) =
|
||||||
case base
|
case base
|
||||||
of base10: nil
|
of base10: discard
|
||||||
of base2: incl(n.flags, nfBase2)
|
of base2: incl(n.flags, nfBase2)
|
||||||
of base8: incl(n.flags, nfBase8)
|
of base8: incl(n.flags, nfBase8)
|
||||||
of base16: incl(n.flags, nfBase16)
|
of base16: incl(n.flags, nfBase16)
|
||||||
|
|
@ -466,7 +466,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
|
||||||
eat(p, pxCurlyDirRi)
|
eat(p, pxCurlyDirRi)
|
||||||
opNode.ident = getIdent("&")
|
opNode.ident = getIdent("&")
|
||||||
else:
|
else:
|
||||||
nil
|
discard
|
||||||
of pxMinus:
|
of pxMinus:
|
||||||
if p.tok.xkind == pxPer:
|
if p.tok.xkind == pxPer:
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
@ -477,7 +477,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
|
||||||
of pxNeq:
|
of pxNeq:
|
||||||
opNode.ident = getIdent("!=")
|
opNode.ident = getIdent("!=")
|
||||||
else:
|
else:
|
||||||
nil
|
discard
|
||||||
skipCom(p, opNode) # read sub-expression with higher priority
|
skipCom(p, opNode) # read sub-expression with higher priority
|
||||||
nextop = lowestExprAux(p, v2, opPred)
|
nextop = lowestExprAux(p, v2, opPred)
|
||||||
addSon(node, opNode)
|
addSon(node, opNode)
|
||||||
|
|
@ -505,7 +505,7 @@ proc fixExpr(n: PNode): PNode =
|
||||||
(n.sons[2].kind in {nkCharLit, nkStrLit}):
|
(n.sons[2].kind in {nkCharLit, nkStrLit}):
|
||||||
n.sons[0].ident = getIdent("&") # fix operator
|
n.sons[0].ident = getIdent("&") # fix operator
|
||||||
else:
|
else:
|
||||||
nil
|
discard
|
||||||
if not (n.kind in {nkEmpty..nkNilLit}):
|
if not (n.kind in {nkEmpty..nkNilLit}):
|
||||||
for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i])
|
for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i])
|
||||||
|
|
||||||
|
|
@ -603,7 +603,7 @@ proc parseStmtList(p: var TParser): PNode =
|
||||||
of pxCurlyDirLe, pxStarDirLe:
|
of pxCurlyDirLe, pxStarDirLe:
|
||||||
if not isHandledDirective(p): break
|
if not isHandledDirective(p): break
|
||||||
else:
|
else:
|
||||||
nil
|
discard
|
||||||
addSon(result, parseStmt(p))
|
addSon(result, parseStmt(p))
|
||||||
if sonsLen(result) == 1: result = result.sons[0]
|
if sonsLen(result) == 1: result = result.sons[0]
|
||||||
|
|
||||||
|
|
@ -732,7 +732,7 @@ proc parseRepeat(p: var TParser): PNode =
|
||||||
addSon(b, c)
|
addSon(b, c)
|
||||||
addSon(a, b)
|
addSon(a, b)
|
||||||
if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id:
|
if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id:
|
||||||
nil
|
discard
|
||||||
else:
|
else:
|
||||||
addSon(s, a)
|
addSon(s, a)
|
||||||
addSon(result, s)
|
addSon(result, s)
|
||||||
|
|
@ -840,7 +840,7 @@ proc parseParam(p: var TParser): PNode =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
v = newNodeP(nkVarTy, p)
|
v = newNodeP(nkVarTy, p)
|
||||||
else:
|
else:
|
||||||
nil
|
discard
|
||||||
while true:
|
while true:
|
||||||
case p.tok.xkind
|
case p.tok.xkind
|
||||||
of pxSymbol: a = createIdentNodeP(p.tok.ident, p)
|
of pxSymbol: a = createIdentNodeP(p.tok.ident, p)
|
||||||
|
|
@ -1133,7 +1133,7 @@ proc parseRecordPart(p: var TParser): PNode =
|
||||||
proc exSymbol(n: var PNode) =
|
proc exSymbol(n: var PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkPostfix:
|
of nkPostfix:
|
||||||
nil
|
discard
|
||||||
of nkPragmaExpr:
|
of nkPragmaExpr:
|
||||||
exSymbol(n.sons[0])
|
exSymbol(n.sons[0])
|
||||||
of nkIdent, nkAccQuoted:
|
of nkIdent, nkAccQuoted:
|
||||||
|
|
@ -1154,7 +1154,7 @@ proc fixRecordDef(n: var PNode) =
|
||||||
for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i])
|
for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i])
|
||||||
of nkIdentDefs:
|
of nkIdentDefs:
|
||||||
for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i])
|
for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i])
|
||||||
of nkNilLit, nkEmpty: nil
|
of nkNilLit, nkEmpty: discard
|
||||||
else: internalError(n.info, "fixRecordDef(): " & $n.kind)
|
else: internalError(n.info, "fixRecordDef(): " & $n.kind)
|
||||||
|
|
||||||
proc addPragmaToIdent(ident: var PNode, pragma: PNode) =
|
proc addPragmaToIdent(ident: var PNode, pragma: PNode) =
|
||||||
|
|
@ -1191,7 +1191,7 @@ proc parseRecordBody(p: var TParser, result, definition: PNode) =
|
||||||
if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p))
|
if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p))
|
||||||
else: internalError(result.info, "anonymous record is not supported")
|
else: internalError(result.info, "anonymous record is not supported")
|
||||||
else:
|
else:
|
||||||
nil
|
discard
|
||||||
opt(p, pxSemicolon)
|
opt(p, pxSemicolon)
|
||||||
skipCom(p, result)
|
skipCom(p, result)
|
||||||
|
|
||||||
|
|
@ -1399,7 +1399,7 @@ proc fixVarSection(p: var TParser, counter: PNode) =
|
||||||
|
|
||||||
proc exSymbols(n: PNode) =
|
proc exSymbols(n: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkEmpty..nkNilLit: nil
|
of nkEmpty..nkNilLit: discard
|
||||||
of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos])
|
of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos])
|
||||||
of nkWhenStmt, nkStmtList:
|
of nkWhenStmt, nkStmtList:
|
||||||
for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i])
|
for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i])
|
||||||
|
|
@ -1410,7 +1410,7 @@ proc exSymbols(n: PNode) =
|
||||||
exSymbol(n.sons[i].sons[0])
|
exSymbol(n.sons[i].sons[0])
|
||||||
if n.sons[i].sons[2].kind == nkObjectTy:
|
if n.sons[i].sons[2].kind == nkObjectTy:
|
||||||
fixRecordDef(n.sons[i].sons[2])
|
fixRecordDef(n.sons[i].sons[2])
|
||||||
else: nil
|
else: discard
|
||||||
|
|
||||||
proc parseBegin(p: var TParser, result: PNode) =
|
proc parseBegin(p: var TParser, result: PNode) =
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ const
|
||||||
wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
|
wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
|
||||||
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
|
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
|
||||||
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
|
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
|
||||||
wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
|
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
|
||||||
wGensym, wInject, wRaises, wTags, wOperator, wDelegator}
|
wGensym, wInject, wRaises, wTags, wOperator, wDelegator}
|
||||||
converterPragmas* = procPragmas
|
converterPragmas* = procPragmas
|
||||||
methodPragmas* = procPragmas
|
methodPragmas* = procPragmas
|
||||||
|
|
@ -47,12 +47,13 @@ const
|
||||||
wInjectStmt}
|
wInjectStmt}
|
||||||
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
||||||
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
|
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
|
||||||
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wNoStackFrame,
|
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
|
||||||
wRaises, wTags}
|
wRaises, wTags}
|
||||||
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
|
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
|
||||||
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
|
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
|
||||||
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
|
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
|
||||||
wInheritable, wGensym, wInject, wRequiresInit}
|
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
||||||
|
wBorrow}
|
||||||
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
||||||
wImportCpp, wImportObjC, wError}
|
wImportCpp, wImportObjC, wError}
|
||||||
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
||||||
|
|
@ -97,8 +98,22 @@ proc makeExternImport(s: PSym, extname: string) =
|
||||||
incl(s.flags, sfImportc)
|
incl(s.flags, sfImportc)
|
||||||
excl(s.flags, sfForward)
|
excl(s.flags, sfForward)
|
||||||
|
|
||||||
proc makeExternExport(s: PSym, extname: string) =
|
proc validateExternCName(s: PSym, info: TLineInfo) =
|
||||||
|
## Validates that the symbol name in s.loc.r is a valid C identifier.
|
||||||
|
##
|
||||||
|
## Valid identifiers are those alphanumeric including the underscore not
|
||||||
|
## starting with a number. If the check fails, a generic error will be
|
||||||
|
## displayed to the user.
|
||||||
|
let target = ropeToStr(s.loc.r)
|
||||||
|
if target.len < 1 or target[0] notin IdentStartChars or
|
||||||
|
not target.allCharsInSet(IdentChars):
|
||||||
|
localError(info, errGenerated, "invalid exported symbol")
|
||||||
|
|
||||||
|
proc makeExternExport(s: PSym, extname: string, info: TLineInfo) =
|
||||||
setExternName(s, extname)
|
setExternName(s, extname)
|
||||||
|
# XXX to fix make it work with nimrtl.
|
||||||
|
#if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC}:
|
||||||
|
# validateExternCName(s, info)
|
||||||
incl(s.flags, sfExportc)
|
incl(s.flags, sfExportc)
|
||||||
|
|
||||||
proc processImportCompilerProc(s: PSym, extname: string) =
|
proc processImportCompilerProc(s: PSym, extname: string) =
|
||||||
|
|
@ -498,6 +513,13 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) =
|
||||||
else:
|
else:
|
||||||
invalidPragma(n)
|
invalidPragma(n)
|
||||||
|
|
||||||
|
proc typeBorrow(sym: PSym, n: PNode) =
|
||||||
|
if n.kind == nkExprColonExpr:
|
||||||
|
let it = n.sons[1]
|
||||||
|
if it.kind != nkAccQuoted:
|
||||||
|
localError(n.info, "a type can only borrow `.` for now")
|
||||||
|
incl(sym.typ.flags, tfBorrowDot)
|
||||||
|
|
||||||
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
validPragmas: TSpecialWords): bool =
|
validPragmas: TSpecialWords): bool =
|
||||||
var it = n.sons[i]
|
var it = n.sons[i]
|
||||||
|
|
@ -515,7 +537,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
if k in validPragmas:
|
if k in validPragmas:
|
||||||
case k
|
case k
|
||||||
of wExportc:
|
of wExportc:
|
||||||
makeExternExport(sym, getOptionalStr(c, it, "$1"))
|
makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
|
||||||
incl(sym.flags, sfUsed) # avoid wrong hints
|
incl(sym.flags, sfUsed) # avoid wrong hints
|
||||||
of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
|
of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
|
||||||
of wImportCompilerProc:
|
of wImportCompilerProc:
|
||||||
|
|
@ -548,9 +570,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
of wNodecl:
|
of wNodecl:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
incl(sym.loc.flags, lfNoDecl)
|
incl(sym.loc.flags, lfNoDecl)
|
||||||
of wPure, wNoStackFrame:
|
of wPure, wAsmNoStackFrame:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
if sym != nil: incl(sym.flags, sfPure)
|
if sym != nil:
|
||||||
|
if k == wPure and sym.kind in routineKinds: invalidPragma(it)
|
||||||
|
else: incl(sym.flags, sfPure)
|
||||||
of wVolatile:
|
of wVolatile:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
incl(sym.flags, sfVolatile)
|
incl(sym.flags, sfVolatile)
|
||||||
|
|
@ -601,7 +625,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
processDynLib(c, it, sym)
|
processDynLib(c, it, sym)
|
||||||
of wCompilerproc:
|
of wCompilerproc:
|
||||||
noVal(it) # compilerproc may not get a string!
|
noVal(it) # compilerproc may not get a string!
|
||||||
makeExternExport(sym, "$1")
|
makeExternExport(sym, "$1", it.info)
|
||||||
incl(sym.flags, sfCompilerProc)
|
incl(sym.flags, sfCompilerProc)
|
||||||
incl(sym.flags, sfUsed) # suppress all those stupid warnings
|
incl(sym.flags, sfUsed) # suppress all those stupid warnings
|
||||||
registerCompilerProc(sym)
|
registerCompilerProc(sym)
|
||||||
|
|
@ -617,8 +641,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
if sym.typ == nil: invalidPragma(it)
|
if sym.typ == nil: invalidPragma(it)
|
||||||
else: incl(sym.typ.flags, tfVarargs)
|
else: incl(sym.typ.flags, tfVarargs)
|
||||||
of wBorrow:
|
of wBorrow:
|
||||||
noVal(it)
|
if sym.kind == skType:
|
||||||
incl(sym.flags, sfBorrow)
|
typeBorrow(sym, it)
|
||||||
|
else:
|
||||||
|
noVal(it)
|
||||||
|
incl(sym.flags, sfBorrow)
|
||||||
of wFinal:
|
of wFinal:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
if sym.typ == nil: invalidPragma(it)
|
if sym.typ == nil: invalidPragma(it)
|
||||||
|
|
@ -640,6 +667,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
incl(sym.flags, sfThread)
|
incl(sym.flags, sfThread)
|
||||||
incl(sym.flags, sfProcvar)
|
incl(sym.flags, sfProcvar)
|
||||||
if sym.typ != nil: incl(sym.typ.flags, tfThread)
|
if sym.typ != nil: incl(sym.typ.flags, tfThread)
|
||||||
|
of wPacked:
|
||||||
|
noVal(it)
|
||||||
|
if sym.typ == nil: invalidPragma(it)
|
||||||
|
else: incl(sym.typ.flags, tfPacked)
|
||||||
of wHint: message(it.info, hintUser, expectStrLit(c, it))
|
of wHint: message(it.info, hintUser, expectStrLit(c, it))
|
||||||
of wWarning: message(it.info, warnUser, expectStrLit(c, it))
|
of wWarning: message(it.info, warnUser, expectStrLit(c, it))
|
||||||
of wError:
|
of wError:
|
||||||
|
|
@ -699,6 +730,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
noVal(it)
|
noVal(it)
|
||||||
if sym.typ == nil: invalidPragma(it)
|
if sym.typ == nil: invalidPragma(it)
|
||||||
else: incl(sym.typ.flags, tfIncompleteStruct)
|
else: incl(sym.typ.flags, tfIncompleteStruct)
|
||||||
|
of wUnchecked:
|
||||||
|
noVal(it)
|
||||||
|
if sym.typ == nil: invalidPragma(it)
|
||||||
|
else: incl(sym.typ.flags, tfUncheckedArray)
|
||||||
|
of wUnion:
|
||||||
|
noVal(it)
|
||||||
|
if sym.typ == nil: invalidPragma(it)
|
||||||
|
else: incl(sym.typ.flags, tfUnion)
|
||||||
of wRequiresInit:
|
of wRequiresInit:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
if sym.typ == nil: invalidPragma(it)
|
if sym.typ == nil: invalidPragma(it)
|
||||||
|
|
@ -732,8 +771,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
else: invalidPragma(it)
|
else: invalidPragma(it)
|
||||||
else: processNote(c, it)
|
else: processNote(c, it)
|
||||||
|
|
||||||
proc implictPragmas*(c: PContext, sym: PSym, n: PNode,
|
proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
|
||||||
validPragmas: TSpecialWords) =
|
validPragmas: TSpecialWords) =
|
||||||
if sym != nil and sym.kind != skModule:
|
if sym != nil and sym.kind != skModule:
|
||||||
var it = POptionEntry(c.optionStack.head)
|
var it = POptionEntry(c.optionStack.head)
|
||||||
while it != nil:
|
while it != nil:
|
||||||
|
|
@ -753,8 +792,19 @@ proc implictPragmas*(c: PContext, sym: PSym, n: PNode,
|
||||||
addToLib(lib, sym)
|
addToLib(lib, sym)
|
||||||
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)
|
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)
|
||||||
|
|
||||||
|
proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
|
||||||
|
if n == nil or n.sons == nil:
|
||||||
|
return false
|
||||||
|
|
||||||
|
for p in n.sons:
|
||||||
|
var key = if p.kind == nkExprColonExpr: p[0] else: p
|
||||||
|
if key.kind == nkIdent and whichKeyword(key.ident) == pragma:
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
|
||||||
if n == nil: return
|
if n == nil: return
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
if singlePragma(c, sym, n, i, validPragmas): break
|
if singlePragma(c, sym, n, i, validPragmas): break
|
||||||
implictPragmas(c, sym, n, validPragmas)
|
implicitPragmas(c, sym, n, validPragmas)
|
||||||
|
|
|
||||||
|
|
@ -424,8 +424,11 @@ proc lsub(n: PNode): int =
|
||||||
of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref")
|
of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref")
|
||||||
of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr")
|
of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr")
|
||||||
of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var")
|
of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var")
|
||||||
of nkDistinctTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) +
|
of nkDistinctTy:
|
||||||
len("Distinct")
|
result = len("distinct") + (if n.len > 0: lsub(n.sons[0])+1 else: 0)
|
||||||
|
if n.len > 1:
|
||||||
|
result += (if n[1].kind == nkWith: len("_with_") else: len("_without_"))
|
||||||
|
result += lcomma(n[1])
|
||||||
of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) +
|
of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) +
|
||||||
len("static[]")
|
len("static[]")
|
||||||
of nkTypeDef: result = lsons(n) + 3
|
of nkTypeDef: result = lsons(n) + 3
|
||||||
|
|
@ -558,15 +561,18 @@ proc longMode(n: PNode, start: int = 0, theEnd: int = - 1): bool =
|
||||||
result = true
|
result = true
|
||||||
break
|
break
|
||||||
|
|
||||||
proc gstmts(g: var TSrcGen, n: PNode, c: TContext) =
|
proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
|
||||||
if n.kind == nkEmpty: return
|
if n.kind == nkEmpty: return
|
||||||
if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
|
if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
|
||||||
indentNL(g)
|
if doIndent: indentNL(g)
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
optNL(g)
|
optNL(g)
|
||||||
gsub(g, n.sons[i])
|
if n.sons[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
|
||||||
|
gstmts(g, n.sons[i], c, doIndent=false)
|
||||||
|
else:
|
||||||
|
gsub(g, n.sons[i])
|
||||||
gcoms(g)
|
gcoms(g)
|
||||||
dedent(g)
|
if doIndent: dedent(g)
|
||||||
else:
|
else:
|
||||||
if rfLongMode in c.flags: indentNL(g)
|
if rfLongMode in c.flags: indentNL(g)
|
||||||
gsub(g, n)
|
gsub(g, n)
|
||||||
|
|
@ -1017,9 +1023,15 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
else:
|
else:
|
||||||
put(g, tkVar, "var")
|
put(g, tkVar, "var")
|
||||||
of nkDistinctTy:
|
of nkDistinctTy:
|
||||||
if sonsLen(n) > 0:
|
if n.len > 0:
|
||||||
putWithSpace(g, tkDistinct, "distinct")
|
putWithSpace(g, tkDistinct, "distinct")
|
||||||
gsub(g, n.sons[0])
|
gsub(g, n.sons[0])
|
||||||
|
if n.len > 1:
|
||||||
|
if n[1].kind == nkWith:
|
||||||
|
putWithSpace(g, tkWith, " with")
|
||||||
|
else:
|
||||||
|
putWithSpace(g, tkWithout, " without")
|
||||||
|
gcomma(g, n[1])
|
||||||
else:
|
else:
|
||||||
put(g, tkDistinct, "distinct")
|
put(g, tkDistinct, "distinct")
|
||||||
of nkTypeDef:
|
of nkTypeDef:
|
||||||
|
|
@ -1268,7 +1280,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||||
put(g, tkBracketLe, "[")
|
put(g, tkBracketLe, "[")
|
||||||
gcomma(g, n)
|
gcomma(g, n)
|
||||||
put(g, tkBracketRi, "]")
|
put(g, tkBracketRi, "]")
|
||||||
of nkMetaNode:
|
of nkMetaNode_Obsolete:
|
||||||
put(g, tkParLe, "(META|")
|
put(g, tkParLe, "(META|")
|
||||||
gsub(g, n.sons[0])
|
gsub(g, n.sons[0])
|
||||||
put(g, tkParRi, ")")
|
put(g, tkParRi, ")")
|
||||||
|
|
|
||||||
|
|
@ -890,7 +890,7 @@ proc loadStub*(s: PSym) =
|
||||||
|
|
||||||
# deactivate the GC here because we do a deep recursion and generate no
|
# deactivate the GC here because we do a deep recursion and generate no
|
||||||
# garbage when restoring parts of the object graph anyway.
|
# garbage when restoring parts of the object graph anyway.
|
||||||
# Since we die with internal errors if this fails, so no try-finally is
|
# Since we die with internal errors if this fails, no try-finally is
|
||||||
# necessary.
|
# necessary.
|
||||||
GC_disable()
|
GC_disable()
|
||||||
rawLoadStub(s)
|
rawLoadStub(s)
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,9 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
|
||||||
result = copyTree(arg)
|
result = copyTree(arg)
|
||||||
result.typ = formal
|
result.typ = formal
|
||||||
|
|
||||||
|
proc inferWithMetatype(c: PContext, formal: PType,
|
||||||
|
arg: PNode, coerceDistincts = false): PNode
|
||||||
|
|
||||||
var commonTypeBegin = PType(kind: tyExpr)
|
var commonTypeBegin = PType(kind: tyExpr)
|
||||||
|
|
||||||
proc commonType*(x, y: PType): PType =
|
proc commonType*(x, y: PType): PType =
|
||||||
|
|
@ -120,7 +123,8 @@ proc commonType*(x, y: PType): PType =
|
||||||
if a.kind == tyObject and b.kind == tyObject:
|
if a.kind == tyObject and b.kind == tyObject:
|
||||||
result = commonSuperclass(a, b)
|
result = commonSuperclass(a, b)
|
||||||
# this will trigger an error later:
|
# this will trigger an error later:
|
||||||
if result.isNil: return x
|
if result.isNil or result == a: return x
|
||||||
|
if result == b: return y
|
||||||
if k != tyNone:
|
if k != tyNone:
|
||||||
let r = result
|
let r = result
|
||||||
result = newType(k, r.owner)
|
result = newType(k, r.owner)
|
||||||
|
|
@ -138,6 +142,10 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
|
||||||
result = n.sym
|
result = n.sym
|
||||||
internalAssert sfGenSym in result.flags
|
internalAssert sfGenSym in result.flags
|
||||||
internalAssert result.kind == kind
|
internalAssert result.kind == kind
|
||||||
|
# when there is a nested proc inside a template, semtmpl
|
||||||
|
# will assign a wrong owner during the first pass over the
|
||||||
|
# template; we must fix it here: see #909
|
||||||
|
result.owner = getCurrOwner()
|
||||||
else:
|
else:
|
||||||
result = newSym(kind, considerAcc(n), getCurrOwner(), n.info)
|
result = newSym(kind, considerAcc(n), getCurrOwner(), n.info)
|
||||||
|
|
||||||
|
|
@ -193,7 +201,8 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
|
||||||
result = semExprWithType(c, evaluated)
|
result = semExprWithType(c, evaluated)
|
||||||
else:
|
else:
|
||||||
result = evaluated
|
result = evaluated
|
||||||
semmacrosanity.annotateType(result, eOrig.typ)
|
let expectedType = eOrig.typ.skipTypes({tyStatic})
|
||||||
|
semmacrosanity.annotateType(result, expectedType)
|
||||||
else:
|
else:
|
||||||
result = semExprWithType(c, evaluated)
|
result = semExprWithType(c, evaluated)
|
||||||
#result = fitNode(c, e.typ, result) inlined with special case:
|
#result = fitNode(c, e.typ, result) inlined with special case:
|
||||||
|
|
@ -213,14 +222,26 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
|
||||||
result = getConstExpr(c.module, e)
|
result = getConstExpr(c.module, e)
|
||||||
if result != nil: return
|
if result != nil: return
|
||||||
|
|
||||||
|
let oldErrorCount = msgs.gErrorCounter
|
||||||
|
let oldErrorMax = msgs.gErrorMax
|
||||||
|
let oldErrorOutputs = errorOutputs
|
||||||
|
|
||||||
|
errorOutputs = {}
|
||||||
|
msgs.gErrorMax = high(int)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = evalConstExpr(c.module, e)
|
result = evalConstExpr(c.module, e)
|
||||||
if result == nil or result.kind == nkEmpty:
|
if result == nil or result.kind == nkEmpty:
|
||||||
return nil
|
result = nil
|
||||||
|
else:
|
||||||
|
result = fixupTypeAfterEval(c, result, e)
|
||||||
|
|
||||||
result = fixupTypeAfterEval(c, result, e)
|
except ERecoverableError:
|
||||||
except:
|
result = nil
|
||||||
return nil
|
|
||||||
|
msgs.gErrorCounter = oldErrorCount
|
||||||
|
msgs.gErrorMax = oldErrorMax
|
||||||
|
errorOutputs = oldErrorOutputs
|
||||||
|
|
||||||
proc semConstExpr(c: PContext, n: PNode): PNode =
|
proc semConstExpr(c: PContext, n: PNode): PNode =
|
||||||
var e = semExprWithType(c, n)
|
var e = semExprWithType(c, n)
|
||||||
|
|
@ -332,6 +353,8 @@ proc myOpen(module: PSym): PPassContext =
|
||||||
c.semOperand = semOperand
|
c.semOperand = semOperand
|
||||||
c.semConstBoolExpr = semConstBoolExpr
|
c.semConstBoolExpr = semConstBoolExpr
|
||||||
c.semOverloadedCall = semOverloadedCall
|
c.semOverloadedCall = semOverloadedCall
|
||||||
|
c.semInferredLambda = semInferredLambda
|
||||||
|
c.semGenerateInstance = generateInstance
|
||||||
c.semTypeNode = semTypeNode
|
c.semTypeNode = semTypeNode
|
||||||
pushProcCon(c, module)
|
pushProcCon(c, module)
|
||||||
pushOwner(c.module)
|
pushOwner(c.module)
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||||
errors[errors.len - 1].add("\n " & err)
|
errors[errors.len - 1].add("\n " & err)
|
||||||
if z.state == csMatch:
|
if z.state == csMatch:
|
||||||
# little hack so that iterators are preferred over everything else:
|
# little hack so that iterators are preferred over everything else:
|
||||||
if sym.kind == skIterator: inc(z.exactMatches, 200)
|
if sym.kind in skIterators: inc(z.exactMatches, 200)
|
||||||
case best.state
|
case best.state
|
||||||
of csEmpty, csNoMatch: best = z
|
of csEmpty, csNoMatch: best = z
|
||||||
of csMatch:
|
of csMatch:
|
||||||
|
|
@ -82,7 +82,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) =
|
||||||
# fail fast:
|
# fail fast:
|
||||||
globalError(n.info, errTypeMismatch, "")
|
globalError(n.info, errTypeMismatch, "")
|
||||||
var result = msgKindToString(errTypeMismatch)
|
var result = msgKindToString(errTypeMismatch)
|
||||||
add(result, describeArgs(c, n, 1 + ord(nfDelegate in n.flags)))
|
add(result, describeArgs(c, n, 1))
|
||||||
add(result, ')')
|
add(result, ')')
|
||||||
|
|
||||||
var candidates = ""
|
var candidates = ""
|
||||||
|
|
@ -138,15 +138,33 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||||
|
|
||||||
let overloadsState = result.state
|
let overloadsState = result.state
|
||||||
if overloadsState != csMatch:
|
if overloadsState != csMatch:
|
||||||
if nfDelegate in n.flags:
|
if nfDotField in n.flags:
|
||||||
internalAssert f.kind == nkIdent
|
internalAssert f.kind == nkIdent and n.sonsLen >= 2
|
||||||
let calleeName = newStrNode(nkStrLit, f.ident.s)
|
let calleeName = newStrNode(nkStrLit, f.ident.s).withInfo(n.info)
|
||||||
calleeName.info = n.info
|
|
||||||
|
|
||||||
let callOp = newIdentNode(idDelegator, n.info)
|
# leave the op head symbol empty,
|
||||||
n.sons[0..0] = [callOp, calleeName]
|
# we are going to try multiple variants
|
||||||
orig.sons[0..0] = [callOp, calleeName]
|
n.sons[0..1] = [nil, n[1], calleeName]
|
||||||
|
orig.sons[0..1] = [nil, orig[1], calleeName]
|
||||||
|
|
||||||
|
template tryOp(x) =
|
||||||
|
let op = newIdentNode(getIdent(x), n.info)
|
||||||
|
n.sons[0] = op
|
||||||
|
orig.sons[0] = op
|
||||||
|
pickBest(op)
|
||||||
|
|
||||||
|
if nfExplicitCall in n.flags:
|
||||||
|
tryOp ".()"
|
||||||
|
|
||||||
|
if result.state in {csEmpty, csNoMatch}:
|
||||||
|
tryOp "."
|
||||||
|
|
||||||
|
elif nfDotSetter in n.flags:
|
||||||
|
internalAssert f.kind == nkIdent and n.sonsLen == 3
|
||||||
|
let calleeName = newStrNode(nkStrLit, f.ident.s[0.. -2]).withInfo(n.info)
|
||||||
|
let callOp = newIdentNode(getIdent".=", n.info)
|
||||||
|
n.sons[0..1] = [callOp, n[1], calleeName]
|
||||||
|
orig.sons[0..1] = [callOp, orig[1], calleeName]
|
||||||
pickBest(callOp)
|
pickBest(callOp)
|
||||||
|
|
||||||
if overloadsState == csEmpty and result.state == csEmpty:
|
if overloadsState == csEmpty and result.state == csEmpty:
|
||||||
|
|
@ -157,9 +175,15 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||||
localError(n.info, errExprXCannotBeCalled,
|
localError(n.info, errExprXCannotBeCalled,
|
||||||
renderTree(n, {renderNoComments}))
|
renderTree(n, {renderNoComments}))
|
||||||
else:
|
else:
|
||||||
|
if {nfDotField, nfDotSetter} * n.flags != {}:
|
||||||
|
# clean up the inserted ops
|
||||||
|
n.sons.delete(2)
|
||||||
|
n.sons[0] = f
|
||||||
|
|
||||||
errors = @[]
|
errors = @[]
|
||||||
pickBest(f)
|
pickBest(f)
|
||||||
notFoundError(c, n, errors)
|
notFoundError(c, n, errors)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if alt.state == csMatch and cmpCandidates(result, alt) == 0 and
|
if alt.state == csMatch and cmpCandidates(result, alt) == 0 and
|
||||||
|
|
@ -204,12 +228,25 @@ proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
|
||||||
if m.genericConverter and result != nil:
|
if m.genericConverter and result != nil:
|
||||||
instGenericConvertersArg(c, result, m)
|
instGenericConvertersArg(c, result, m)
|
||||||
|
|
||||||
proc convertTo*(c: PContext, f: PType, n: PNode): PNode =
|
proc inferWithMetatype(c: PContext, formal: PType,
|
||||||
|
arg: PNode, coerceDistincts = false): PNode =
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
initCandidate(c, m, f)
|
initCandidate(c, m, formal)
|
||||||
result = paramTypesMatch(m, f, n.typ, n, nil)
|
m.coerceDistincts = coerceDistincts
|
||||||
|
result = paramTypesMatch(m, formal, arg.typ, arg, nil)
|
||||||
if m.genericConverter and result != nil:
|
if m.genericConverter and result != nil:
|
||||||
instGenericConvertersArg(c, result, m)
|
instGenericConvertersArg(c, result, m)
|
||||||
|
if result != nil:
|
||||||
|
# This almost exactly replicates the steps taken by the compiler during
|
||||||
|
# param matching. It performs an embarassing ammount of back-and-forth
|
||||||
|
# type jugling, but it's the price to pay for consistency and correctness
|
||||||
|
result.typ = generateTypeInstance(c, m.bindings, arg.info,
|
||||||
|
formal.skipTypes({tyCompositeTypeClass}))
|
||||||
|
else:
|
||||||
|
typeMismatch(arg, formal, arg.typ)
|
||||||
|
# error correction:
|
||||||
|
result = copyTree(arg)
|
||||||
|
result.typ = formal
|
||||||
|
|
||||||
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
||||||
assert x.state == csMatch
|
assert x.state == csMatch
|
||||||
|
|
@ -271,8 +308,9 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
result = newNodeI(a.kind, n.info)
|
result = newNodeI(a.kind, n.info)
|
||||||
for i in countup(0, len(a)-1):
|
for i in countup(0, len(a)-1):
|
||||||
var candidate = a.sons[i].sym
|
var candidate = a.sons[i].sym
|
||||||
if candidate.kind in {skProc, skMethod, skConverter, skIterator}:
|
if candidate.kind in {skProc, skMethod, skConverter,
|
||||||
# if suffices that the candidate has the proper number of generic
|
skIterator, skClosureIterator}:
|
||||||
|
# it suffices that the candidate has the proper number of generic
|
||||||
# type parameters:
|
# type parameters:
|
||||||
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
|
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
|
||||||
result.add(explicitGenericSym(c, n, candidate))
|
result.add(explicitGenericSym(c, n, candidate))
|
||||||
|
|
@ -288,7 +326,7 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||||
# for borrowing the sym in the symbol table is returned, else nil.
|
# for borrowing the sym in the symbol table is returned, else nil.
|
||||||
# New approach: generate fn(x, y, z) where x, y, z have the proper types
|
# New approach: generate fn(x, y, z) where x, y, z have the proper types
|
||||||
# and use the overloading resolution mechanism:
|
# and use the overloading resolution mechanism:
|
||||||
var call = newNode(nkCall)
|
var call = newNodeI(nkCall, fn.info)
|
||||||
var hasDistinct = false
|
var hasDistinct = false
|
||||||
call.add(newIdentNode(fn.name, fn.info))
|
call.add(newIdentNode(fn.name, fn.info))
|
||||||
for i in 1.. <fn.typ.n.len:
|
for i in 1.. <fn.typ.n.len:
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ type
|
||||||
|
|
||||||
TExprFlag* = enum
|
TExprFlag* = enum
|
||||||
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
|
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
|
||||||
efAllowDestructor, efWantValue
|
efAllowDestructor, efWantValue, efOperand
|
||||||
TExprFlags* = set[TExprFlag]
|
TExprFlags* = set[TExprFlag]
|
||||||
|
|
||||||
PContext* = ref TContext
|
PContext* = ref TContext
|
||||||
|
|
@ -81,6 +81,9 @@ type
|
||||||
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
|
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
|
||||||
filter: TSymKinds): PNode {.nimcall.}
|
filter: TSymKinds): PNode {.nimcall.}
|
||||||
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
|
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
|
||||||
|
semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode
|
||||||
|
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
|
||||||
|
info: TLineInfo): PSym
|
||||||
includedFiles*: TIntSet # used to detect recursive include files
|
includedFiles*: TIntSet # used to detect recursive include files
|
||||||
userPragmas*: TStrTable
|
userPragmas*: TStrTable
|
||||||
evalContext*: PEvalContext
|
evalContext*: PEvalContext
|
||||||
|
|
@ -211,7 +214,6 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||||
|
|
||||||
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
||||||
let typedesc = makeTypeDesc(c, typ)
|
let typedesc = makeTypeDesc(c, typ)
|
||||||
rawAddSon(typedesc, newTypeS(tyNone, c))
|
|
||||||
let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
|
let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
|
||||||
return newSymNode(sym, info)
|
return newSymNode(sym, info)
|
||||||
|
|
||||||
|
|
@ -234,17 +236,41 @@ proc makeAndType*(c: PContext, t1, t2: PType): PType =
|
||||||
result.sons = @[t1, t2]
|
result.sons = @[t1, t2]
|
||||||
propagateToOwner(result, t1)
|
propagateToOwner(result, t1)
|
||||||
propagateToOwner(result, t2)
|
propagateToOwner(result, t2)
|
||||||
|
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||||
|
|
||||||
proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
proc makeOrType*(c: PContext, t1, t2: PType): PType =
|
||||||
result = newTypeS(tyOr, c)
|
result = newTypeS(tyOr, c)
|
||||||
result.sons = @[t1, t2]
|
result.sons = @[t1, t2]
|
||||||
propagateToOwner(result, t1)
|
propagateToOwner(result, t1)
|
||||||
propagateToOwner(result, t2)
|
propagateToOwner(result, t2)
|
||||||
|
result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
|
||||||
|
|
||||||
proc makeNotType*(c: PContext, t1: PType): PType =
|
proc makeNotType*(c: PContext, t1: PType): PType =
|
||||||
result = newTypeS(tyNot, c)
|
result = newTypeS(tyNot, c)
|
||||||
result.sons = @[t1]
|
result.sons = @[t1]
|
||||||
propagateToOwner(result, t1)
|
propagateToOwner(result, t1)
|
||||||
|
result.flags.incl(t1.flags * {tfHasStatic})
|
||||||
|
|
||||||
|
proc nMinusOne*(n: PNode): PNode =
|
||||||
|
result = newNode(nkCall, n.info, @[
|
||||||
|
newSymNode(getSysMagic("<", mUnaryLt)),
|
||||||
|
n])
|
||||||
|
|
||||||
|
# Remember to fix the procs below this one when you make changes!
|
||||||
|
proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
|
||||||
|
let intType = getSysType(tyInt)
|
||||||
|
result = newTypeS(tyRange, c)
|
||||||
|
result.sons = @[intType]
|
||||||
|
result.n = newNode(nkRange, n.info, @[
|
||||||
|
newIntTypeNode(nkIntLit, 0, intType),
|
||||||
|
makeStaticExpr(c, n.nMinusOne)])
|
||||||
|
|
||||||
|
template rangeHasStaticIf*(t: PType): bool =
|
||||||
|
# this accepts the ranges's node
|
||||||
|
t.n[1].kind == nkStaticExpr
|
||||||
|
|
||||||
|
template getStaticTypeFromRange*(t: PType): PType =
|
||||||
|
t.n[1][0][1].typ
|
||||||
|
|
||||||
proc newTypeS(kind: TTypeKind, c: PContext): PType =
|
proc newTypeS(kind: TTypeKind, c: PContext): PType =
|
||||||
result = newType(kind, getCurrOwner())
|
result = newType(kind, getCurrOwner())
|
||||||
|
|
@ -272,7 +298,7 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt;
|
||||||
addSonSkipIntLit(result, intType) # basetype of range
|
addSonSkipIntLit(result, intType) # basetype of range
|
||||||
|
|
||||||
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
|
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
|
||||||
if s.kind in {skProc, skConverter, skMethod, skIterator}:
|
if s.kind in {skProc, skConverter, skMethod, skIterator, skClosureIterator}:
|
||||||
incl(s.flags, sfAddrTaken)
|
incl(s.flags, sfAddrTaken)
|
||||||
# XXX add to 'c' for global analysis
|
# XXX add to 'c' for global analysis
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,9 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
|
||||||
useSym(destructableT.destructor),
|
useSym(destructableT.destructor),
|
||||||
n.sons[paramsPos][1][0]]))
|
n.sons[paramsPos][1][0]]))
|
||||||
|
|
||||||
proc destroyField(c: PContext, field: PSym, holder: PNode): PNode =
|
proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode
|
||||||
|
|
||||||
|
proc destroySym(c: PContext, field: PSym, holder: PNode): PNode =
|
||||||
let destructableT = instantiateDestructor(c, field.typ)
|
let destructableT = instantiateDestructor(c, field.typ)
|
||||||
if destructableT != nil:
|
if destructableT != nil:
|
||||||
result = newNode(nkCall, field.info, @[
|
result = newNode(nkCall, field.info, @[
|
||||||
|
|
@ -70,56 +72,49 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
|
||||||
for i in countup(1, n.len - 1):
|
for i in countup(1, n.len - 1):
|
||||||
# of A, B:
|
# of A, B:
|
||||||
var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
|
var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
|
||||||
let recList = n[i].lastSon
|
|
||||||
var destroyRecList = newNode(nkStmtList, n[i].info, @[])
|
|
||||||
template addField(f: expr): stmt =
|
|
||||||
let stmt = destroyField(c, f, holder)
|
|
||||||
if stmt != nil:
|
|
||||||
destroyRecList.addSon(stmt)
|
|
||||||
inc nonTrivialFields
|
|
||||||
|
|
||||||
case recList.kind
|
let stmt = destroyFieldOrFields(c, n[i].lastSon, holder)
|
||||||
of nkSym:
|
if stmt == nil:
|
||||||
addField(recList.sym)
|
caseBranch.addSon(newNode(nkStmtList, n[i].info, @[]))
|
||||||
of nkRecList:
|
|
||||||
for j in countup(0, recList.len - 1):
|
|
||||||
addField(recList[j].sym)
|
|
||||||
else:
|
else:
|
||||||
internalAssert false
|
caseBranch.addSon(stmt)
|
||||||
|
nonTrivialFields += stmt.len
|
||||||
|
|
||||||
caseBranch.addSon(destroyRecList)
|
|
||||||
result.addSon(caseBranch)
|
result.addSon(caseBranch)
|
||||||
|
|
||||||
# maybe no fields were destroyed?
|
# maybe no fields were destroyed?
|
||||||
if nonTrivialFields == 0:
|
if nonTrivialFields == 0:
|
||||||
result = nil
|
result = nil
|
||||||
|
|
||||||
|
proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode =
|
||||||
|
template maybeAddLine(e: expr): stmt =
|
||||||
|
let stmt = e
|
||||||
|
if stmt != nil:
|
||||||
|
if result == nil: result = newNode(nkStmtList)
|
||||||
|
result.addSon(stmt)
|
||||||
|
|
||||||
|
case field.kind
|
||||||
|
of nkRecCase:
|
||||||
|
maybeAddLine destroyCase(c, field, holder)
|
||||||
|
of nkSym:
|
||||||
|
maybeAddLine destroySym(c, field.sym, holder)
|
||||||
|
of nkRecList:
|
||||||
|
for son in field:
|
||||||
|
maybeAddLine destroyFieldOrFields(c, son, holder)
|
||||||
|
else:
|
||||||
|
internalAssert false
|
||||||
|
|
||||||
proc generateDestructor(c: PContext, t: PType): PNode =
|
proc generateDestructor(c: PContext, t: PType): PNode =
|
||||||
## generate a destructor for a user-defined object or tuple type
|
## generate a destructor for a user-defined object or tuple type
|
||||||
## returns nil if the destructor turns out to be trivial
|
## returns nil if the destructor turns out to be trivial
|
||||||
|
|
||||||
template addLine(e: expr): stmt =
|
|
||||||
if result == nil: result = newNode(nkStmtList)
|
|
||||||
result.addSon(e)
|
|
||||||
|
|
||||||
# XXX: This may be true for some C-imported types such as
|
# XXX: This may be true for some C-imported types such as
|
||||||
# Tposix_spawnattr
|
# Tposix_spawnattr
|
||||||
if t.n == nil or t.n.sons == nil: return
|
if t.n == nil or t.n.sons == nil: return
|
||||||
internalAssert t.n.kind == nkRecList
|
internalAssert t.n.kind == nkRecList
|
||||||
let destructedObj = newIdentNode(destructorParam, unknownLineInfo())
|
let destructedObj = newIdentNode(destructorParam, unknownLineInfo())
|
||||||
# call the destructods of all fields
|
# call the destructods of all fields
|
||||||
for s in countup(0, t.n.sons.len - 1):
|
result = destroyFieldOrFields(c, t.n, destructedObj)
|
||||||
case t.n.sons[s].kind
|
|
||||||
of nkRecCase:
|
|
||||||
let stmt = destroyCase(c, t.n.sons[s], destructedObj)
|
|
||||||
if stmt != nil: addLine(stmt)
|
|
||||||
of nkSym:
|
|
||||||
let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
|
|
||||||
if stmt != nil: addLine(stmt)
|
|
||||||
else:
|
|
||||||
# XXX just skip it for now so that the compiler doesn't crash, but
|
|
||||||
# please zahary fix it! arbitrary nesting of nkRecList/nkRecCase is
|
|
||||||
# possible. Any thread example seems to trigger this.
|
|
||||||
discard
|
|
||||||
# base classes' destructors will be automatically called by
|
# base classes' destructors will be automatically called by
|
||||||
# semProcAux for both auto-generated and user-defined destructors
|
# semProcAux for both auto-generated and user-defined destructors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
|
||||||
|
|
||||||
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
# same as 'semExprWithType' but doesn't check for proc vars
|
# same as 'semExprWithType' but doesn't check for proc vars
|
||||||
result = semExpr(c, n, flags)
|
result = semExpr(c, n, flags + {efOperand})
|
||||||
if result.kind == nkEmpty:
|
if result.kind == nkEmpty:
|
||||||
# do not produce another redundant error message:
|
# do not produce another redundant error message:
|
||||||
#raiseRecoverableError("")
|
#raiseRecoverableError("")
|
||||||
|
|
@ -117,10 +117,12 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||||
elif s.ast != nil:
|
elif s.ast != nil:
|
||||||
result = semExpr(c, s.ast)
|
result = semExpr(c, s.ast)
|
||||||
else:
|
else:
|
||||||
internalError(n.info, "no default for")
|
n.typ = s.typ
|
||||||
result = emptyNode
|
return n
|
||||||
of skType:
|
of skType:
|
||||||
markUsed(n, s)
|
markUsed(n, s)
|
||||||
|
if s.typ.kind == tyStatic and s.typ.n != nil:
|
||||||
|
return s.typ.n
|
||||||
result = newSymNode(s, n.info)
|
result = newSymNode(s, n.info)
|
||||||
result.typ = makeTypeDesc(c, s.typ)
|
result.typ = makeTypeDesc(c, s.typ)
|
||||||
else:
|
else:
|
||||||
|
|
@ -191,20 +193,47 @@ proc isCastable(dst, src: PType): bool =
|
||||||
proc isSymChoice(n: PNode): bool {.inline.} =
|
proc isSymChoice(n: PNode): bool {.inline.} =
|
||||||
result = n.kind in nkSymChoices
|
result = n.kind in nkSymChoices
|
||||||
|
|
||||||
|
proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
|
||||||
|
# XXX: liftParamType started to perform addDecl
|
||||||
|
# we could do that instead in semTypeNode by snooping for added
|
||||||
|
# gnrc. params, then it won't be necessary to open a new scope here
|
||||||
|
openScope(c)
|
||||||
|
var lifted = liftParamType(c, skType, newNodeI(nkArgList, info),
|
||||||
|
t, ":anon", info)
|
||||||
|
closeScope(c)
|
||||||
|
if lifted != nil: t = lifted
|
||||||
|
|
||||||
proc semConv(c: PContext, n: PNode): PNode =
|
proc semConv(c: PContext, n: PNode): PNode =
|
||||||
if sonsLen(n) != 2:
|
if sonsLen(n) != 2:
|
||||||
localError(n.info, errConvNeedsOneArg)
|
localError(n.info, errConvNeedsOneArg)
|
||||||
return n
|
return n
|
||||||
|
|
||||||
result = newNodeI(nkConv, n.info)
|
result = newNodeI(nkConv, n.info)
|
||||||
result.typ = semTypeNode(c, n.sons[0], nil).skipTypes({tyGenericInst})
|
var targetType = semTypeNode(c, n.sons[0], nil)
|
||||||
addSon(result, copyTree(n.sons[0]))
|
maybeLiftType(targetType, c, n[0].info)
|
||||||
addSon(result, semExprWithType(c, n.sons[1]))
|
result.addSon copyTree(n.sons[0])
|
||||||
var op = result.sons[1]
|
var op = semExprWithType(c, n.sons[1])
|
||||||
|
|
||||||
|
if targetType.isMetaType:
|
||||||
|
let final = inferWithMetatype(c, targetType, op, true)
|
||||||
|
result.addSon final
|
||||||
|
result.typ = final.typ
|
||||||
|
return
|
||||||
|
|
||||||
|
result.typ = targetType
|
||||||
|
addSon(result, op)
|
||||||
|
|
||||||
if not isSymChoice(op):
|
if not isSymChoice(op):
|
||||||
let status = checkConvertible(c, result.typ, op.typ)
|
let status = checkConvertible(c, result.typ, op.typ)
|
||||||
case status
|
case status
|
||||||
of convOK: discard
|
of convOK:
|
||||||
|
# handle SomeProcType(SomeGenericProc)
|
||||||
|
# XXX: This needs fixing. checkConvertible uses typeRel internally, but
|
||||||
|
# doesn't bother to perform the work done in paramTypeMatchAux/fitNode
|
||||||
|
# so we are redoing the typeRel work here. Why does semConv exist as a
|
||||||
|
# separate proc from fitNode?
|
||||||
|
if op.kind == nkSym and op.sym.isGenericRoutine:
|
||||||
|
result.sons[1] = fitNode(c, result.typ, result.sons[1])
|
||||||
of convNotNeedeed:
|
of convNotNeedeed:
|
||||||
message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
|
message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
|
||||||
of convNotLegal:
|
of convNotLegal:
|
||||||
|
|
@ -214,7 +243,7 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||||
for i in countup(0, sonsLen(op) - 1):
|
for i in countup(0, sonsLen(op) - 1):
|
||||||
let it = op.sons[i]
|
let it = op.sons[i]
|
||||||
let status = checkConvertible(c, result.typ, it.typ)
|
let status = checkConvertible(c, result.typ, it.typ)
|
||||||
if status == convOK:
|
if status in {convOK, convNotNeedeed}:
|
||||||
markUsed(n, it.sym)
|
markUsed(n, it.sym)
|
||||||
markIndirect(c, it.sym)
|
markIndirect(c, it.sym)
|
||||||
return it
|
return it
|
||||||
|
|
@ -316,16 +345,9 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
|
||||||
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
||||||
t.callConv == ccClosure and
|
t.callConv == ccClosure and
|
||||||
tfIterator notin t.flags))
|
tfIterator notin t.flags))
|
||||||
of "iterator":
|
|
||||||
let t = skipTypes(t1, abstractRange)
|
|
||||||
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
|
||||||
t.callConv == ccClosure and
|
|
||||||
tfIterator in t.flags))
|
|
||||||
else:
|
else:
|
||||||
var t2 = n[2].typ.skipTypes({tyTypeDesc})
|
var t2 = n[2].typ.skipTypes({tyTypeDesc})
|
||||||
let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info),
|
maybeLiftType(t2, c, n.info)
|
||||||
t2, ":anon", n.info)
|
|
||||||
if lifted != nil: t2 = lifted
|
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
initCandidate(c, m, t2)
|
initCandidate(c, m, t2)
|
||||||
let match = typeRel(m, t2, t1) != isNone
|
let match = typeRel(m, t2, t1) != isNone
|
||||||
|
|
@ -340,21 +362,20 @@ proc semIs(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
n.typ = getSysType(tyBool)
|
n.typ = getSysType(tyBool)
|
||||||
|
|
||||||
n.sons[1] = semExprWithType(c, n[1], {efDetermineType})
|
n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
|
||||||
|
|
||||||
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
|
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
|
||||||
let t2 = semTypeNode(c, n[2], nil)
|
let t2 = semTypeNode(c, n[2], nil)
|
||||||
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
|
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
|
||||||
|
|
||||||
if n[1].typ.kind != tyTypeDesc:
|
let lhsType = n[1].typ
|
||||||
n.sons[1] = makeTypeSymNode(c, n[1].typ, n[1].info)
|
if lhsType.kind != tyTypeDesc:
|
||||||
elif n[1].typ.sonsLen == 0:
|
n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
|
||||||
|
elif lhsType.base.kind == tyNone:
|
||||||
# this is a typedesc variable, leave for evals
|
# this is a typedesc variable, leave for evals
|
||||||
return
|
return
|
||||||
|
|
||||||
let t1 = n[1].typ.sons[0]
|
|
||||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||||
if not containsGenericType(t1): result = isOpImpl(c, n)
|
if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n)
|
||||||
|
|
||||||
proc semOpAux(c: PContext, n: PNode) =
|
proc semOpAux(c: PContext, n: PNode) =
|
||||||
const flags = {efDetermineType}
|
const flags = {efDetermineType}
|
||||||
|
|
@ -612,6 +633,18 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||||
else: return result
|
else: return result
|
||||||
result.typ = semfold.getIntervalType(callee.magic, call)
|
result.typ = semfold.getIntervalType(callee.magic, call)
|
||||||
|
|
||||||
|
block maybeLabelAsStatic:
|
||||||
|
# XXX: temporary work-around needed for tlateboundstatic.
|
||||||
|
# This is certainly not correct, but it will get the job
|
||||||
|
# done until we have a more robust infrastructure for
|
||||||
|
# implicit statics.
|
||||||
|
if n.len > 1:
|
||||||
|
for i in 1 .. <n.len:
|
||||||
|
if n[i].typ.kind != tyStatic or tfUnresolved notin n[i].typ.flags:
|
||||||
|
break maybeLabelAsStatic
|
||||||
|
n.typ = newTypeWithSons(c, tyStatic, @[n.typ])
|
||||||
|
n.typ.flags.incl tfUnresolved
|
||||||
|
|
||||||
# optimization pass: not necessary for correctness of the semantic pass
|
# optimization pass: not necessary for correctness of the semantic pass
|
||||||
if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and
|
if {sfNoSideEffect, sfCompileTime} * callee.flags != {} and
|
||||||
{sfForward, sfImportc} * callee.flags == {}:
|
{sfForward, sfImportc} * callee.flags == {}:
|
||||||
|
|
@ -635,9 +668,11 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
|
||||||
result = evalStaticExpr(c.module, call, c.p.owner)
|
result = evalStaticExpr(c.module, call, c.p.owner)
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
localError(n.info, errCannotInterpretNodeX, renderTree(call))
|
localError(n.info, errCannotInterpretNodeX, renderTree(call))
|
||||||
|
else: result = fixupTypeAfterEval(c, result, n)
|
||||||
else:
|
else:
|
||||||
result = evalConstExpr(c.module, call)
|
result = evalConstExpr(c.module, call)
|
||||||
if result.isNil: result = n
|
if result.isNil: result = n
|
||||||
|
else: result = fixupTypeAfterEval(c, result, n)
|
||||||
#if result != n:
|
#if result != n:
|
||||||
# echo "SUCCESS evaluated at compile time: ", call.renderTree
|
# echo "SUCCESS evaluated at compile time: ", call.renderTree
|
||||||
|
|
||||||
|
|
@ -647,16 +682,18 @@ proc semStaticExpr(c: PContext, n: PNode): PNode =
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
localError(n.info, errCannotInterpretNodeX, renderTree(n))
|
localError(n.info, errCannotInterpretNodeX, renderTree(n))
|
||||||
result = emptyNode
|
result = emptyNode
|
||||||
|
else:
|
||||||
|
result = fixupTypeAfterEval(c, result, a)
|
||||||
|
|
||||||
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
||||||
flags: TExprFlags): PNode =
|
flags: TExprFlags): PNode =
|
||||||
if flags*{efInTypeof, efWantIterator} != {}:
|
if flags*{efInTypeof, efWantIterator} != {}:
|
||||||
# consider: 'for x in pReturningArray()' --> we don't want the restriction
|
# consider: 'for x in pReturningArray()' --> we don't want the restriction
|
||||||
# to 'skIterator' anymore; skIterator is preferred in sigmatch already for
|
# to 'skIterators' anymore; skIterators are preferred in sigmatch already
|
||||||
# typeof support.
|
# for typeof support.
|
||||||
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
||||||
result = semOverloadedCall(c, n, nOrig,
|
result = semOverloadedCall(c, n, nOrig,
|
||||||
{skProc, skMethod, skConverter, skMacro, skTemplate, skIterator})
|
{skProc, skMethod, skConverter, skMacro, skTemplate}+skIterators)
|
||||||
else:
|
else:
|
||||||
result = semOverloadedCall(c, n, nOrig,
|
result = semOverloadedCall(c, n, nOrig,
|
||||||
{skProc, skMethod, skConverter, skMacro, skTemplate})
|
{skProc, skMethod, skConverter, skMacro, skTemplate})
|
||||||
|
|
@ -669,7 +706,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
||||||
case callee.kind
|
case callee.kind
|
||||||
of skMacro, skTemplate: discard
|
of skMacro, skTemplate: discard
|
||||||
else:
|
else:
|
||||||
if (callee.kind == skIterator) and (callee.id == c.p.owner.id):
|
if (callee.kind in skIterators) and (callee.id == c.p.owner.id):
|
||||||
localError(n.info, errRecursiveDependencyX, callee.name.s)
|
localError(n.info, errRecursiveDependencyX, callee.name.s)
|
||||||
if sfNoSideEffect notin callee.flags:
|
if sfNoSideEffect notin callee.flags:
|
||||||
if {sfImportc, sfSideEffect} * callee.flags != {}:
|
if {sfImportc, sfSideEffect} * callee.flags != {}:
|
||||||
|
|
@ -687,6 +724,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
# it is a static call!
|
# it is a static call!
|
||||||
result = n.sons[0]
|
result = n.sons[0]
|
||||||
result.kind = nkCall
|
result.kind = nkCall
|
||||||
|
result.flags.incl nfExplicitCall
|
||||||
for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
|
for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
|
||||||
return semExpr(c, result, flags)
|
return semExpr(c, result, flags)
|
||||||
else:
|
else:
|
||||||
|
|
@ -893,6 +931,26 @@ proc makeDeref(n: PNode): PNode =
|
||||||
addSon(result, a)
|
addSon(result, a)
|
||||||
t = skipTypes(t.sons[0], {tyGenericInst})
|
t = skipTypes(t.sons[0], {tyGenericInst})
|
||||||
|
|
||||||
|
const
|
||||||
|
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
|
||||||
|
tyDotOpTransparent = {tyVar, tyPtr, tyRef}
|
||||||
|
|
||||||
|
proc readTypeParameter(c: PContext, typ: PType,
|
||||||
|
paramName: PIdent, info: TLineInfo): PNode =
|
||||||
|
let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias
|
||||||
|
else: (internalAssert(typ.kind == tyCompositeTypeClass); typ.sons[1])
|
||||||
|
|
||||||
|
let tbody = ty.sons[0]
|
||||||
|
for s in countup(0, tbody.len-2):
|
||||||
|
let tParam = tbody.sons[s]
|
||||||
|
if tParam.sym.name == paramName:
|
||||||
|
let rawTyp = ty.sons[s + 1]
|
||||||
|
if rawTyp.kind == tyStatic:
|
||||||
|
return rawTyp.n
|
||||||
|
else:
|
||||||
|
let foundTyp = makeTypeDesc(c, rawTyp)
|
||||||
|
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
|
||||||
|
|
||||||
proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
## returns nil if it's not a built-in field access
|
## returns nil if it's not a built-in field access
|
||||||
checkSonsLen(n, 2)
|
checkSonsLen(n, 2)
|
||||||
|
|
@ -910,8 +968,9 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
var ty = n.sons[0].typ
|
var ty = n.sons[0].typ
|
||||||
var f: PSym = nil
|
var f: PSym = nil
|
||||||
result = nil
|
result = nil
|
||||||
if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.len == 1:
|
if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone):
|
||||||
if ty.kind == tyTypeDesc: ty = ty.sons[0]
|
if ty.kind == tyTypeDesc: ty = ty.base
|
||||||
|
ty = ty.skipTypes(tyDotOpTransparent)
|
||||||
case ty.kind
|
case ty.kind
|
||||||
of tyEnum:
|
of tyEnum:
|
||||||
# look up if the identifier belongs to the enum:
|
# look up if the identifier belongs to the enum:
|
||||||
|
|
@ -925,22 +984,11 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
result.typ = ty
|
result.typ = ty
|
||||||
markUsed(n, f)
|
markUsed(n, f)
|
||||||
return
|
return
|
||||||
of tyGenericInst:
|
of tyTypeParamsHolders:
|
||||||
assert ty.sons[0].kind == tyGenericBody
|
return readTypeParameter(c, ty, i, n.info)
|
||||||
let tbody = ty.sons[0]
|
|
||||||
for s in countup(0, tbody.len-2):
|
|
||||||
let tParam = tbody.sons[s]
|
|
||||||
if tParam.sym.name == i:
|
|
||||||
let rawTyp = ty.sons[s + 1]
|
|
||||||
if rawTyp.kind == tyStatic:
|
|
||||||
return rawTyp.n
|
|
||||||
else:
|
|
||||||
let foundTyp = makeTypeDesc(c, rawTyp)
|
|
||||||
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info)
|
|
||||||
return
|
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
if ty.n.kind == nkRecList:
|
if ty.n.kind == nkRecList:
|
||||||
for field in ty.n.sons:
|
for field in ty.n:
|
||||||
if field.sym.name == i:
|
if field.sym.name == i:
|
||||||
n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ])
|
n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ])
|
||||||
n.typ.n = copyTree(n)
|
n.typ.n = copyTree(n)
|
||||||
|
|
@ -952,8 +1000,9 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
# XXX: This is probably not relevant any more
|
# XXX: This is probably not relevant any more
|
||||||
# reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
|
# reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
|
||||||
ty = n.sons[0].typ
|
ty = n.sons[0].typ
|
||||||
|
return nil
|
||||||
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef})
|
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef})
|
||||||
|
while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct})
|
||||||
var check: PNode = nil
|
var check: PNode = nil
|
||||||
if ty.kind == tyObject:
|
if ty.kind == tyObject:
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -984,6 +1033,12 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
n.typ = f.typ
|
n.typ = f.typ
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
|
# we didn't find any field, let's look for a generic param
|
||||||
|
if result == nil:
|
||||||
|
let t = n.sons[0].typ.skipTypes(tyDotOpTransparent)
|
||||||
|
if t.kind in tyTypeParamsHolders:
|
||||||
|
result = readTypeParameter(c, t, i, n.info)
|
||||||
|
|
||||||
proc dotTransformation(c: PContext, n: PNode): PNode =
|
proc dotTransformation(c: PContext, n: PNode): PNode =
|
||||||
if isSymChoice(n.sons[1]):
|
if isSymChoice(n.sons[1]):
|
||||||
result = newNodeI(nkDotCall, n.info)
|
result = newNodeI(nkDotCall, n.info)
|
||||||
|
|
@ -992,7 +1047,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
var i = considerAcc(n.sons[1])
|
var i = considerAcc(n.sons[1])
|
||||||
result = newNodeI(nkDotCall, n.info)
|
result = newNodeI(nkDotCall, n.info)
|
||||||
result.flags.incl nfDelegate
|
result.flags.incl nfDotField
|
||||||
addSon(result, newIdentNode(i, n[1].info))
|
addSon(result, newIdentNode(i, n[1].info))
|
||||||
addSon(result, copyTree(n[0]))
|
addSon(result, copyTree(n[0]))
|
||||||
|
|
||||||
|
|
@ -1075,12 +1130,13 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
|
|
||||||
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
||||||
var id = considerAcc(a[1])
|
var id = considerAcc(a[1])
|
||||||
let setterId = newIdentNode(getIdent(id.s & '='), n.info)
|
var setterId = newIdentNode(getIdent(id.s & '='), n.info)
|
||||||
# a[0] is already checked for semantics, that does ``builtinFieldAccess``
|
# a[0] is already checked for semantics, that does ``builtinFieldAccess``
|
||||||
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
|
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
|
||||||
# nodes?
|
# nodes?
|
||||||
let aOrig = nOrig[0]
|
let aOrig = nOrig[0]
|
||||||
result = newNode(nkCall, n.info, sons = @[setterId, a[0], semExpr(c, n[1])])
|
result = newNode(nkCall, n.info, sons = @[setterId, a[0], semExpr(c, n[1])])
|
||||||
|
result.flags.incl nfDotSetter
|
||||||
let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]])
|
let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]])
|
||||||
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
|
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
|
||||||
|
|
||||||
|
|
@ -1112,6 +1168,9 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
|
||||||
n.sons[0] = x # 'result[]' --> 'result'
|
n.sons[0] = x # 'result[]' --> 'result'
|
||||||
n.sons[1] = takeImplicitAddr(c, ri)
|
n.sons[1] = takeImplicitAddr(c, ri)
|
||||||
|
|
||||||
|
template resultTypeIsInferrable(typ: PType): expr =
|
||||||
|
typ.isMetaType and typ.kind != tyTypeDesc
|
||||||
|
|
||||||
proc semAsgn(c: PContext, n: PNode): PNode =
|
proc semAsgn(c: PContext, n: PNode): PNode =
|
||||||
checkSonsLen(n, 2)
|
checkSonsLen(n, 2)
|
||||||
var a = n.sons[0]
|
var a = n.sons[0]
|
||||||
|
|
@ -1163,7 +1222,7 @@ proc semAsgn(c: PContext, n: PNode): PNode =
|
||||||
if lhsIsResult: {efAllowDestructor} else: {})
|
if lhsIsResult: {efAllowDestructor} else: {})
|
||||||
if lhsIsResult:
|
if lhsIsResult:
|
||||||
n.typ = enforceVoidContext
|
n.typ = enforceVoidContext
|
||||||
if lhs.sym.typ.isMetaType and lhs.sym.typ.kind != tyTypeDesc:
|
if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
|
||||||
if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric:
|
if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric:
|
||||||
internalAssert c.p.resultSym != nil
|
internalAssert c.p.resultSym != nil
|
||||||
lhs.typ = rhs.typ
|
lhs.typ = rhs.typ
|
||||||
|
|
@ -1181,7 +1240,7 @@ proc semReturn(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or
|
if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or
|
||||||
(c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure):
|
c.p.owner.kind == skClosureIterator:
|
||||||
if n.sons[0].kind != nkEmpty:
|
if n.sons[0].kind != nkEmpty:
|
||||||
# transform ``return expr`` to ``result = expr; return``
|
# transform ``return expr`` to ``result = expr; return``
|
||||||
if c.p.resultSym != nil:
|
if c.p.resultSym != nil:
|
||||||
|
|
@ -1199,6 +1258,7 @@ proc semReturn(c: PContext, n: PNode): PNode =
|
||||||
|
|
||||||
proc semProcBody(c: PContext, n: PNode): PNode =
|
proc semProcBody(c: PContext, n: PNode): PNode =
|
||||||
openScope(c)
|
openScope(c)
|
||||||
|
|
||||||
result = semExpr(c, n)
|
result = semExpr(c, n)
|
||||||
if c.p.resultSym != nil and not isEmptyType(result.typ):
|
if c.p.resultSym != nil and not isEmptyType(result.typ):
|
||||||
# transform ``expr`` to ``result = expr``, but not if the expr is already
|
# transform ``expr`` to ``result = expr``, but not if the expr is already
|
||||||
|
|
@ -1222,6 +1282,11 @@ proc semProcBody(c: PContext, n: PNode): PNode =
|
||||||
result = semAsgn(c, a)
|
result = semAsgn(c, a)
|
||||||
else:
|
else:
|
||||||
discardCheck(c, result)
|
discardCheck(c, result)
|
||||||
|
|
||||||
|
if c.p.owner.kind notin {skMacro, skTemplate} and
|
||||||
|
c.p.resultSym != nil and c.p.resultSym.typ.isMetaType:
|
||||||
|
localError(c.p.resultSym.info, errCannotInferReturnType)
|
||||||
|
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
|
|
||||||
proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
|
proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
|
||||||
|
|
@ -1246,17 +1311,28 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
|
||||||
proc semYield(c: PContext, n: PNode): PNode =
|
proc semYield(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
if c.p.owner == nil or c.p.owner.kind != skIterator:
|
if c.p.owner == nil or c.p.owner.kind notin skIterators:
|
||||||
localError(n.info, errYieldNotAllowedHere)
|
localError(n.info, errYieldNotAllowedHere)
|
||||||
elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
|
elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
|
||||||
localError(n.info, errYieldNotAllowedInTryStmt)
|
localError(n.info, errYieldNotAllowedInTryStmt)
|
||||||
elif n.sons[0].kind != nkEmpty:
|
elif n.sons[0].kind != nkEmpty:
|
||||||
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
|
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
|
||||||
var restype = c.p.owner.typ.sons[0]
|
var iterType = c.p.owner.typ
|
||||||
|
var restype = iterType.sons[0]
|
||||||
if restype != nil:
|
if restype != nil:
|
||||||
n.sons[0] = fitNode(c, restype, n.sons[0])
|
let adjustedRes = if c.p.owner.kind == skIterator: restype.base
|
||||||
|
else: restype
|
||||||
|
n.sons[0] = fitNode(c, adjustedRes, n.sons[0])
|
||||||
if n.sons[0].typ == nil: internalError(n.info, "semYield")
|
if n.sons[0].typ == nil: internalError(n.info, "semYield")
|
||||||
semYieldVarResult(c, n, restype)
|
|
||||||
|
if resultTypeIsInferrable(adjustedRes):
|
||||||
|
let inferred = n.sons[0].typ
|
||||||
|
if c.p.owner.kind == skIterator:
|
||||||
|
iterType.sons[0].sons[0] = inferred
|
||||||
|
else:
|
||||||
|
iterType.sons[0] = inferred
|
||||||
|
|
||||||
|
semYieldVarResult(c, n, adjustedRes)
|
||||||
else:
|
else:
|
||||||
localError(n.info, errCannotReturnExpr)
|
localError(n.info, errCannotReturnExpr)
|
||||||
elif c.p.owner.typ.sons[0] != nil:
|
elif c.p.owner.typ.sons[0] != nil:
|
||||||
|
|
@ -1337,7 +1413,7 @@ proc expectString(c: PContext, n: PNode): string =
|
||||||
localError(n.info, errStringLiteralExpected)
|
localError(n.info, errStringLiteralExpected)
|
||||||
|
|
||||||
proc getMagicSym(magic: TMagic): PSym =
|
proc getMagicSym(magic: TMagic): PSym =
|
||||||
result = newSym(skProc, getIdent($magic), getCurrOwner(), gCodegenLineInfo)
|
result = newSym(skProc, getIdent($magic), systemModule, gCodegenLineInfo)
|
||||||
result.magic = magic
|
result.magic = magic
|
||||||
|
|
||||||
proc newAnonSym(kind: TSymKind, info: TLineInfo,
|
proc newAnonSym(kind: TSymKind, info: TLineInfo,
|
||||||
|
|
@ -1405,9 +1481,8 @@ proc processQuotations(n: var PNode, op: string,
|
||||||
elif n.kind == nkAccQuoted and op == "``":
|
elif n.kind == nkAccQuoted and op == "``":
|
||||||
returnQuote n[0]
|
returnQuote n[0]
|
||||||
|
|
||||||
if not n.isAtom:
|
for i in 0 .. <n.safeLen:
|
||||||
for i in 0 .. <n.len:
|
processQuotations(n.sons[i], op, quotes, ids)
|
||||||
processQuotations(n.sons[i], op, quotes, ids)
|
|
||||||
|
|
||||||
proc semQuoteAst(c: PContext, n: PNode): PNode =
|
proc semQuoteAst(c: PContext, n: PNode): PNode =
|
||||||
internalAssert n.len == 2 or n.len == 3
|
internalAssert n.len == 2 or n.len == 3
|
||||||
|
|
@ -1770,22 +1845,6 @@ proc semBlock(c: PContext, n: PNode): PNode =
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
dec(c.p.nestedBlockCounter)
|
dec(c.p.nestedBlockCounter)
|
||||||
|
|
||||||
proc buildCall(n: PNode): PNode =
|
|
||||||
if n.kind == nkDotExpr and n.len == 2:
|
|
||||||
# x.y --> y(x)
|
|
||||||
result = newNodeI(nkCall, n.info, 2)
|
|
||||||
result.sons[0] = n.sons[1]
|
|
||||||
result.sons[1] = n.sons[0]
|
|
||||||
elif n.kind in nkCallKinds and n.sons[0].kind == nkDotExpr:
|
|
||||||
# x.y(a) -> y(x, a)
|
|
||||||
let a = n.sons[0]
|
|
||||||
result = newNodeI(nkCall, n.info, n.len+1)
|
|
||||||
result.sons[0] = a.sons[1]
|
|
||||||
result.sons[1] = a.sons[0]
|
|
||||||
for i in 1 .. <n.len: result.sons[i+1] = n.sons[i]
|
|
||||||
else:
|
|
||||||
result = n
|
|
||||||
|
|
||||||
proc doBlockIsStmtList(n: PNode): bool =
|
proc doBlockIsStmtList(n: PNode): bool =
|
||||||
result = n.kind == nkDo and
|
result = n.kind == nkDo and
|
||||||
n[paramsPos].sonsLen == 1 and
|
n[paramsPos].sonsLen == 1 and
|
||||||
|
|
@ -1832,13 +1891,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
var s = lookUp(c, n)
|
var s = lookUp(c, n)
|
||||||
semCaptureSym(s, c.p.owner)
|
semCaptureSym(s, c.p.owner)
|
||||||
result = semSym(c, n, s, flags)
|
result = semSym(c, n, s, flags)
|
||||||
if s.kind in {skProc, skMethod, skIterator, skConverter}:
|
if s.kind in {skProc, skMethod, skConverter}+skIterators:
|
||||||
#performProcvarCheck(c, n, s)
|
#performProcvarCheck(c, n, s)
|
||||||
result = symChoice(c, n, s, scClosed)
|
result = symChoice(c, n, s, scClosed)
|
||||||
if result.kind == nkSym:
|
if result.kind == nkSym:
|
||||||
markIndirect(c, result.sym)
|
markIndirect(c, result.sym)
|
||||||
if isGenericRoutine(result.sym):
|
# if isGenericRoutine(result.sym):
|
||||||
localError(n.info, errInstantiateXExplicitely, s.name.s)
|
# localError(n.info, errInstantiateXExplicitely, s.name.s)
|
||||||
of nkSym:
|
of nkSym:
|
||||||
# because of the changed symbol binding, this does not mean that we
|
# because of the changed symbol binding, this does not mean that we
|
||||||
# don't have to check the symbol for semantics here again!
|
# don't have to check the symbol for semantics here again!
|
||||||
|
|
@ -1888,13 +1947,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
message(n.info, warnDeprecated, "bind")
|
message(n.info, warnDeprecated, "bind")
|
||||||
result = semExpr(c, n.sons[0], flags)
|
result = semExpr(c, n.sons[0], flags)
|
||||||
of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy:
|
of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy:
|
||||||
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
|
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
|
||||||
result.typ = makeTypeDesc(c, typ)
|
result.typ = makeTypeDesc(c, typ)
|
||||||
#result = symNodeFromType(c, typ, n.info)
|
#result = symNodeFromType(c, typ, n.info)
|
||||||
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
|
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
|
||||||
# check if it is an expression macro:
|
# check if it is an expression macro:
|
||||||
checkMinSonsLen(n, 1)
|
checkMinSonsLen(n, 1)
|
||||||
let mode = if nfDelegate in n.flags: {} else: {checkUndeclared}
|
let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
|
||||||
var s = qualifiedLookUp(c, n.sons[0], mode)
|
var s = qualifiedLookUp(c, n.sons[0], mode)
|
||||||
if s != nil:
|
if s != nil:
|
||||||
if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr:
|
if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr:
|
||||||
|
|
@ -1922,7 +1981,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
localError(n.info, errUseQualifier, s.name.s)
|
localError(n.info, errUseQualifier, s.name.s)
|
||||||
elif s.magic == mNone: result = semDirectOp(c, n, flags)
|
elif s.magic == mNone: result = semDirectOp(c, n, flags)
|
||||||
else: result = semMagic(c, n, s, flags)
|
else: result = semMagic(c, n, s, flags)
|
||||||
of skProc, skMethod, skConverter, skIterator:
|
of skProc, skMethod, skConverter, skIterators:
|
||||||
if s.magic == mNone: result = semDirectOp(c, n, flags)
|
if s.magic == mNone: result = semDirectOp(c, n, flags)
|
||||||
else: result = semMagic(c, n, s, flags)
|
else: result = semMagic(c, n, s, flags)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1933,7 +1992,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
# the 'newSeq[T](x)' bug
|
# the 'newSeq[T](x)' bug
|
||||||
setGenericParams(c, n.sons[0])
|
setGenericParams(c, n.sons[0])
|
||||||
result = semDirectOp(c, n, flags)
|
result = semDirectOp(c, n, flags)
|
||||||
elif isSymChoice(n.sons[0]) or nfDelegate in n.flags:
|
elif isSymChoice(n.sons[0]) or nfDotField in n.flags:
|
||||||
result = semDirectOp(c, n, flags)
|
result = semDirectOp(c, n, flags)
|
||||||
else:
|
else:
|
||||||
result = semIndirectOp(c, n, flags)
|
result = semIndirectOp(c, n, flags)
|
||||||
|
|
@ -1946,7 +2005,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
checkMinSonsLen(n, 1)
|
checkMinSonsLen(n, 1)
|
||||||
var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared})
|
var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared})
|
||||||
if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}:
|
if (s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators) or
|
||||||
|
n[0].kind in nkSymChoices:
|
||||||
# type parameters: partial generic specialization
|
# type parameters: partial generic specialization
|
||||||
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
|
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
|
||||||
result = explicitGenericInstantiation(c, n, s)
|
result = explicitGenericInstantiation(c, n, s)
|
||||||
|
|
|
||||||
|
|
@ -537,7 +537,7 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode =
|
||||||
if result.kind == nkExprColonExpr: result = result.sons[1]
|
if result.kind == nkExprColonExpr: result = result.sons[1]
|
||||||
else:
|
else:
|
||||||
localError(n.info, errIndexOutOfBounds)
|
localError(n.info, errIndexOutOfBounds)
|
||||||
of nkBracket, nkMetaNode:
|
of nkBracket:
|
||||||
if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)]
|
if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)]
|
||||||
else: localError(n.info, errIndexOutOfBounds)
|
else: localError(n.info, errIndexOutOfBounds)
|
||||||
of nkStrLit..nkTripleStrLit:
|
of nkStrLit..nkTripleStrLit:
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
of skUnknown:
|
of skUnknown:
|
||||||
# Introduced in this pass! Leave it as an identifier.
|
# Introduced in this pass! Leave it as an identifier.
|
||||||
result = n
|
result = n
|
||||||
of skProc, skMethod, skIterator, skConverter:
|
of skProc, skMethod, skIterators, skConverter:
|
||||||
result = symChoice(c, n, s, scOpen)
|
result = symChoice(c, n, s, scOpen)
|
||||||
of skTemplate:
|
of skTemplate:
|
||||||
if macroToExpand(s):
|
if macroToExpand(s):
|
||||||
|
|
@ -141,7 +141,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||||
# symbol lookup ...
|
# symbol lookup ...
|
||||||
of skUnknown, skParam:
|
of skUnknown, skParam:
|
||||||
# Leave it as an identifier.
|
# Leave it as an identifier.
|
||||||
of skProc, skMethod, skIterator, skConverter:
|
of skProc, skMethod, skIterators, skConverter:
|
||||||
result.sons[0] = symChoice(c, n.sons[0], s, scOption)
|
result.sons[0] = symChoice(c, n.sons[0], s, scOption)
|
||||||
first = 1
|
first = 1
|
||||||
of skGenericParam:
|
of skGenericParam:
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,11 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
|
||||||
if n.kind != nkGenericParams:
|
if n.kind != nkGenericParams:
|
||||||
internalError(n.info, "instantiateGenericParamList; no generic params")
|
internalError(n.info, "instantiateGenericParamList; no generic params")
|
||||||
newSeq(entry.concreteTypes, n.len)
|
newSeq(entry.concreteTypes, n.len)
|
||||||
for i in countup(0, n.len - 1):
|
for i, a in n.pairs:
|
||||||
var a = n.sons[i]
|
|
||||||
if a.kind != nkSym:
|
if a.kind != nkSym:
|
||||||
internalError(a.info, "instantiateGenericParamList; no symbol")
|
internalError(a.info, "instantiateGenericParamList; no symbol")
|
||||||
var q = a.sym
|
var q = a.sym
|
||||||
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses:
|
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
|
||||||
continue
|
continue
|
||||||
var s = newSym(skType, q.name, getCurrOwner(), q.info)
|
var s = newSym(skType, q.name, getCurrOwner(), q.info)
|
||||||
s.flags = s.flags + {sfUsed, sfFromGeneric}
|
s.flags = s.flags + {sfUsed, sfFromGeneric}
|
||||||
|
|
@ -86,19 +85,21 @@ proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
|
||||||
|
|
||||||
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
|
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
|
||||||
|
|
||||||
|
proc addProcDecls(c: PContext, fn: PSym) =
|
||||||
|
# get the proc itself in scope (e.g. for recursion)
|
||||||
|
addDecl(c, fn)
|
||||||
|
|
||||||
|
for i in 1 .. <fn.typ.n.len:
|
||||||
|
var param = fn.typ.n.sons[i].sym
|
||||||
|
param.owner = fn
|
||||||
|
addParamOrResult(c, param, fn.kind)
|
||||||
|
|
||||||
|
maybeAddResult(c, fn, fn.ast)
|
||||||
|
|
||||||
proc instantiateBody(c: PContext, n: PNode, result: PSym) =
|
proc instantiateBody(c: PContext, n: PNode, result: PSym) =
|
||||||
if n.sons[bodyPos].kind != nkEmpty:
|
if n.sons[bodyPos].kind != nkEmpty:
|
||||||
inc c.inGenericInst
|
inc c.inGenericInst
|
||||||
# add it here, so that recursive generic procs are possible:
|
# add it here, so that recursive generic procs are possible:
|
||||||
addDecl(c, result)
|
|
||||||
pushProcCon(c, result)
|
|
||||||
# add params to scope
|
|
||||||
for i in 1 .. <result.typ.n.len:
|
|
||||||
var param = result.typ.n.sons[i].sym
|
|
||||||
param.owner = result
|
|
||||||
addParamOrResult(c, param, result.kind)
|
|
||||||
# debug result.typ.n
|
|
||||||
maybeAddResult(c, result, n)
|
|
||||||
var b = n.sons[bodyPos]
|
var b = n.sons[bodyPos]
|
||||||
var symMap: TIdTable
|
var symMap: TIdTable
|
||||||
initIdTable symMap
|
initIdTable symMap
|
||||||
|
|
@ -108,7 +109,6 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
|
||||||
n.sons[bodyPos] = transformBody(c.module, b, result)
|
n.sons[bodyPos] = transformBody(c.module, b, result)
|
||||||
#echo "code instantiated ", result.name.s
|
#echo "code instantiated ", result.name.s
|
||||||
excl(result.flags, sfForward)
|
excl(result.flags, sfForward)
|
||||||
popProcCon(c)
|
|
||||||
dec c.inGenericInst
|
dec c.inGenericInst
|
||||||
|
|
||||||
proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
|
proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
|
||||||
|
|
@ -145,11 +145,56 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
|
||||||
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
|
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
|
||||||
result = instGenericContainer(c, n.info, header)
|
result = instGenericContainer(c, n.info, header)
|
||||||
|
|
||||||
|
proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||||
|
prc: PSym, info: TLineInfo) =
|
||||||
|
# XXX: Instantiates a generic proc signature, while at the same
|
||||||
|
# time adding the instantiated proc params into the current scope.
|
||||||
|
# This is necessary, because the instantiation process may refer to
|
||||||
|
# these params in situations like this:
|
||||||
|
# proc foo[Container](a: Container, b: a.type.Item): type(b.x)
|
||||||
|
#
|
||||||
|
# Alas, doing this here is probably not enough, because another
|
||||||
|
# proc signature could appear in the params:
|
||||||
|
# proc foo[T](a: proc (x: T, b: type(x.y))
|
||||||
|
#
|
||||||
|
# The solution would be to move this logic into semtypinst, but
|
||||||
|
# at this point semtypinst have to become part of sem, because it
|
||||||
|
# will need to use openScope, addDecl, etc
|
||||||
|
#
|
||||||
|
addDecl(c, prc)
|
||||||
|
|
||||||
|
pushInfoContext(info)
|
||||||
|
var cl = initTypeVars(c, pt, info)
|
||||||
|
var result = instCopyType(cl, prc.typ)
|
||||||
|
let originalParams = result.n
|
||||||
|
result.n = originalParams.shallowCopy
|
||||||
|
|
||||||
|
for i in 1 .. <result.len:
|
||||||
|
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
|
||||||
|
propagateToOwner(result, result.sons[i])
|
||||||
|
let param = replaceTypeVarsN(cl, originalParams[i])
|
||||||
|
result.n.sons[i] = param
|
||||||
|
if param.kind == nkSym:
|
||||||
|
# XXX: this won't be true for void params
|
||||||
|
# implement pass-through of void params and
|
||||||
|
# the "sort by distance to point" container
|
||||||
|
param.sym.owner = prc
|
||||||
|
addDecl(c, param.sym)
|
||||||
|
|
||||||
|
result.sons[0] = replaceTypeVarsT(cl, result.sons[0])
|
||||||
|
result.n.sons[0] = originalParams[0].copyTree
|
||||||
|
|
||||||
|
eraseVoidParams(result)
|
||||||
|
skipIntLiteralParams(result)
|
||||||
|
|
||||||
|
prc.typ = result
|
||||||
|
maybeAddResult(c, prc, prc.ast)
|
||||||
|
popInfoContext()
|
||||||
|
|
||||||
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
info: TLineInfo): PSym =
|
info: TLineInfo): PSym =
|
||||||
# no need to instantiate generic templates/macros:
|
# no need to instantiate generic templates/macros:
|
||||||
if fn.kind in {skTemplate, skMacro}: return fn
|
if fn.kind in {skTemplate, skMacro}: return fn
|
||||||
|
|
||||||
# generates an instantiated proc
|
# generates an instantiated proc
|
||||||
if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
|
if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
|
||||||
inc(c.instCounter)
|
inc(c.instCounter)
|
||||||
|
|
@ -173,7 +218,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
var entry = TInstantiation.new
|
var entry = TInstantiation.new
|
||||||
entry.sym = result
|
entry.sym = result
|
||||||
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
|
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
|
||||||
result.typ = generateTypeInstance(c, pt, info, fn.typ)
|
pushProcCon(c, result)
|
||||||
|
instantiateProcType(c, pt, result, info)
|
||||||
n.sons[genericParamsPos] = ast.emptyNode
|
n.sons[genericParamsPos] = ast.emptyNode
|
||||||
var oldPrc = genericCacheGet(fn, entry[])
|
var oldPrc = genericCacheGet(fn, entry[])
|
||||||
if oldPrc == nil:
|
if oldPrc == nil:
|
||||||
|
|
@ -183,12 +229,12 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
|
pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
|
||||||
if isNil(n.sons[bodyPos]):
|
if isNil(n.sons[bodyPos]):
|
||||||
n.sons[bodyPos] = copyTree(fn.getBody)
|
n.sons[bodyPos] = copyTree(fn.getBody)
|
||||||
if fn.kind != skTemplate:
|
instantiateBody(c, n, result)
|
||||||
instantiateBody(c, n, result)
|
sideEffectsCheck(c, result)
|
||||||
sideEffectsCheck(c, result)
|
|
||||||
paramsTypeCheck(c, result.typ)
|
paramsTypeCheck(c, result.typ)
|
||||||
else:
|
else:
|
||||||
result = oldPrc
|
result = oldPrc
|
||||||
|
popProcCon(c)
|
||||||
popInfoContext()
|
popInfoContext()
|
||||||
closeScope(c) # close scope for parameters
|
closeScope(c) # close scope for parameters
|
||||||
popOwner()
|
popOwner()
|
||||||
|
|
|
||||||
|
|
@ -84,10 +84,10 @@ proc initVar(a: PEffects, n: PNode) =
|
||||||
proc initVarViaNew(a: PEffects, n: PNode) =
|
proc initVarViaNew(a: PEffects, n: PNode) =
|
||||||
if n.kind != nkSym: return
|
if n.kind != nkSym: return
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
if {tfNeedsInit, tfNotNil} * s.typ.flags == {tfNotNil}:
|
if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
|
||||||
# 'x' is not nil, but that doesn't mean it's not nil children
|
# 'x' is not nil, but that doesn't mean it's not nil children
|
||||||
# are initialized:
|
# are initialized:
|
||||||
initVarViaNew(a, n)
|
initVar(a, n)
|
||||||
|
|
||||||
proc useVar(a: PEffects, n: PNode) =
|
proc useVar(a: PEffects, n: PNode) =
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
|
|
@ -466,8 +466,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
||||||
mergeTags(tracked, effectList.sons[tagEffects], n)
|
mergeTags(tracked, effectList.sons[tagEffects], n)
|
||||||
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
|
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
|
||||||
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize,
|
if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
|
||||||
mNewSeq, mShallowCopy}:
|
|
||||||
# may not look like an assignment, but it is:
|
# may not look like an assignment, but it is:
|
||||||
initVarViaNew(tracked, n.sons[1])
|
initVarViaNew(tracked, n.sons[1])
|
||||||
for i in 0 .. <safeLen(n):
|
for i in 0 .. <safeLen(n):
|
||||||
|
|
@ -477,7 +476,6 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
if warnProveField in gNotes: checkFieldAccess(tracked.guards, n)
|
if warnProveField in gNotes: checkFieldAccess(tracked.guards, n)
|
||||||
of nkTryStmt: trackTryStmt(tracked, n)
|
of nkTryStmt: trackTryStmt(tracked, n)
|
||||||
of nkPragma: trackPragmaStmt(tracked, n)
|
of nkPragma: trackPragmaStmt(tracked, n)
|
||||||
of nkMacroDef, nkTemplateDef: discard
|
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn:
|
||||||
track(tracked, n.sons[1])
|
track(tracked, n.sons[1])
|
||||||
initVar(tracked, n.sons[0])
|
initVar(tracked, n.sons[0])
|
||||||
|
|
@ -527,7 +525,9 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
if sfDiscriminant in x.sons[0].sym.flags:
|
if sfDiscriminant in x.sons[0].sym.flags:
|
||||||
addDiscriminantFact(tracked.guards, x)
|
addDiscriminantFact(tracked.guards, x)
|
||||||
setLen(tracked.guards, oldFacts)
|
setLen(tracked.guards, oldFacts)
|
||||||
of nkTypeSection: discard
|
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||||
|
nkMacroDef, nkTemplateDef:
|
||||||
|
discard
|
||||||
else:
|
else:
|
||||||
for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
|
for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
|
||||||
|
|
||||||
|
|
@ -581,22 +581,26 @@ proc setEffectsForProcType*(t: PType, n: PNode) =
|
||||||
if not isNil(tagsSpec):
|
if not isNil(tagsSpec):
|
||||||
effects.sons[tagEffects] = tagsSpec
|
effects.sons[tagEffects] = tagsSpec
|
||||||
|
|
||||||
|
proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
|
||||||
|
newSeq(effects.sons, effectListLen)
|
||||||
|
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
|
||||||
|
effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
|
||||||
|
|
||||||
|
t.exc = effects.sons[exceptionEffects]
|
||||||
|
t.tags = effects.sons[tagEffects]
|
||||||
|
t.owner = s
|
||||||
|
t.init = @[]
|
||||||
|
t.guards = @[]
|
||||||
|
|
||||||
proc trackProc*(s: PSym, body: PNode) =
|
proc trackProc*(s: PSym, body: PNode) =
|
||||||
var effects = s.typ.n.sons[0]
|
var effects = s.typ.n.sons[0]
|
||||||
internalAssert effects.kind == nkEffectList
|
internalAssert effects.kind == nkEffectList
|
||||||
# effects already computed?
|
# effects already computed?
|
||||||
if sfForward in s.flags: return
|
if sfForward in s.flags: return
|
||||||
if effects.len == effectListLen: return
|
if effects.len == effectListLen: return
|
||||||
newSeq(effects.sons, effectListLen)
|
|
||||||
effects.sons[exceptionEffects] = newNodeI(nkArgList, body.info)
|
|
||||||
effects.sons[tagEffects] = newNodeI(nkArgList, body.info)
|
|
||||||
|
|
||||||
var t: TEffects
|
var t: TEffects
|
||||||
t.exc = effects.sons[exceptionEffects]
|
initEffects(effects, s, t)
|
||||||
t.tags = effects.sons[tagEffects]
|
|
||||||
t.owner = s
|
|
||||||
t.init = @[]
|
|
||||||
t.guards = @[]
|
|
||||||
track(t, body)
|
track(t, body)
|
||||||
|
|
||||||
if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and
|
if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and
|
||||||
|
|
@ -619,3 +623,12 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||||
# after the check, use the formal spec:
|
# after the check, use the formal spec:
|
||||||
effects.sons[tagEffects] = tagsSpec
|
effects.sons[tagEffects] = tagsSpec
|
||||||
|
|
||||||
|
proc trackTopLevelStmt*(module: PSym; n: PNode) =
|
||||||
|
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
|
||||||
|
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
|
||||||
|
return
|
||||||
|
var effects = newNode(nkEffectList, n.info)
|
||||||
|
var t: TEffects
|
||||||
|
initEffects(effects, module, t)
|
||||||
|
|
||||||
|
track(t, n)
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,6 @@
|
||||||
|
|
||||||
var enforceVoidContext = PType(kind: tyStmt)
|
var enforceVoidContext = PType(kind: tyStmt)
|
||||||
|
|
||||||
proc semCommand(c: PContext, n: PNode): PNode =
|
|
||||||
result = semExprNoType(c, n)
|
|
||||||
|
|
||||||
proc semDiscard(c: PContext, n: PNode): PNode =
|
proc semDiscard(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
|
|
@ -76,8 +73,8 @@ proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
|
||||||
|
|
||||||
proc semProcvarCheck(c: PContext, n: PNode) =
|
proc semProcvarCheck(c: PContext, n: PNode) =
|
||||||
let n = n.skipConv
|
let n = n.skipConv
|
||||||
if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator,
|
if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter,
|
||||||
skConverter}:
|
skIterator, skClosureIterator}:
|
||||||
performProcvarCheck(c, n, n.sym)
|
performProcvarCheck(c, n, n.sym)
|
||||||
|
|
||||||
proc semProc(c: PContext, n: PNode): PNode
|
proc semProc(c: PContext, n: PNode): PNode
|
||||||
|
|
@ -126,13 +123,14 @@ proc implicitlyDiscardable(n: PNode): bool =
|
||||||
proc fixNilType(n: PNode) =
|
proc fixNilType(n: PNode) =
|
||||||
if isAtom(n):
|
if isAtom(n):
|
||||||
if n.kind != nkNilLit and n.typ != nil:
|
if n.kind != nkNilLit and n.typ != nil:
|
||||||
localError(n.info, errDiscardValue)
|
localError(n.info, errDiscardValueX, n.typ.typeToString)
|
||||||
elif n.kind in {nkStmtList, nkStmtListExpr}:
|
elif n.kind in {nkStmtList, nkStmtListExpr}:
|
||||||
n.kind = nkStmtList
|
n.kind = nkStmtList
|
||||||
for it in n: fixNilType(it)
|
for it in n: fixNilType(it)
|
||||||
n.typ = nil
|
n.typ = nil
|
||||||
|
|
||||||
proc discardCheck(c: PContext, result: PNode) =
|
proc discardCheck(c: PContext, result: PNode) =
|
||||||
|
if c.inTypeClass > 0: return
|
||||||
if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
|
if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
|
||||||
if result.kind == nkNilLit:
|
if result.kind == nkNilLit:
|
||||||
result.typ = nil
|
result.typ = nil
|
||||||
|
|
@ -143,10 +141,6 @@ proc discardCheck(c: PContext, result: PNode) =
|
||||||
while n.kind in skipForDiscardable:
|
while n.kind in skipForDiscardable:
|
||||||
n = n.lastSon
|
n = n.lastSon
|
||||||
n.typ = nil
|
n.typ = nil
|
||||||
elif c.inTypeClass > 0 and result.typ.kind == tyBool:
|
|
||||||
let verdict = semConstExpr(c, result)
|
|
||||||
if verdict.intVal == 0:
|
|
||||||
localError(result.info, "type class predicate failed")
|
|
||||||
elif result.typ.kind != tyError and gCmd != cmdInteractive:
|
elif result.typ.kind != tyError and gCmd != cmdInteractive:
|
||||||
if result.typ.kind == tyNil:
|
if result.typ.kind == tyNil:
|
||||||
fixNilType(result)
|
fixNilType(result)
|
||||||
|
|
@ -154,7 +148,7 @@ proc discardCheck(c: PContext, result: PNode) =
|
||||||
else:
|
else:
|
||||||
var n = result
|
var n = result
|
||||||
while n.kind in skipForDiscardable: n = n.lastSon
|
while n.kind in skipForDiscardable: n = n.lastSon
|
||||||
localError(n.info, errDiscardValue)
|
localError(n.info, errDiscardValueX, result.typ.typeToString)
|
||||||
|
|
||||||
proc semIf(c: PContext, n: PNode): PNode =
|
proc semIf(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
|
|
@ -331,6 +325,7 @@ proc checkNilable(v: PSym) =
|
||||||
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
var b: PNode
|
var b: PNode
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
|
var hasCompileTime = false
|
||||||
for i in countup(0, sonsLen(n)-1):
|
for i in countup(0, sonsLen(n)-1):
|
||||||
var a = n.sons[i]
|
var a = n.sons[i]
|
||||||
if gCmd == cmdIdeTools: suggestStmt(c, a)
|
if gCmd == cmdIdeTools: suggestStmt(c, a)
|
||||||
|
|
@ -349,7 +344,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
# BUGFIX: ``fitNode`` is needed here!
|
# BUGFIX: ``fitNode`` is needed here!
|
||||||
# check type compability between def.typ and typ:
|
# check type compability between def.typ and typ:
|
||||||
if typ != nil: def = fitNode(c, typ, def)
|
if typ != nil: def = fitNode(c, typ, def)
|
||||||
else: typ = skipIntLit(def.typ)
|
else:
|
||||||
|
typ = skipIntLit(def.typ)
|
||||||
|
if typ.kind in {tySequence, tyArray, tySet} and
|
||||||
|
typ.lastSon.kind == tyEmpty:
|
||||||
|
localError(def.info, errCannotInferTypeOfTheLiteral,
|
||||||
|
($typ.kind).substr(2).toLower)
|
||||||
else:
|
else:
|
||||||
def = ast.emptyNode
|
def = ast.emptyNode
|
||||||
if symkind == skLet: localError(a.info, errLetNeedsInit)
|
if symkind == skLet: localError(a.info, errLetNeedsInit)
|
||||||
|
|
@ -405,6 +405,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
v.typ = tup.sons[j]
|
v.typ = tup.sons[j]
|
||||||
b.sons[j] = newSymNode(v)
|
b.sons[j] = newSymNode(v)
|
||||||
checkNilable(v)
|
checkNilable(v)
|
||||||
|
if sfCompileTime in v.flags: hasCompileTime = true
|
||||||
|
if hasCompileTime: vm.setupCompileTimeVar(c.module, result)
|
||||||
|
|
||||||
proc semConst(c: PContext, n: PNode): PNode =
|
proc semConst(c: PContext, n: PNode): PNode =
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
|
|
@ -607,7 +609,8 @@ proc symForVar(c: PContext, n: PNode): PSym =
|
||||||
proc semForVars(c: PContext, n: PNode): PNode =
|
proc semForVars(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
var length = sonsLen(n)
|
var length = sonsLen(n)
|
||||||
var iter = skipTypes(n.sons[length-2].typ, {tyGenericInst})
|
let iterBase = n.sons[length-2].typ.skipTypes({tyIter})
|
||||||
|
var iter = skipTypes(iterBase, {tyGenericInst})
|
||||||
# length == 3 means that there is one for loop variable
|
# length == 3 means that there is one for loop variable
|
||||||
# and thus no tuple unpacking:
|
# and thus no tuple unpacking:
|
||||||
if iter.kind != tyTuple or length == 3:
|
if iter.kind != tyTuple or length == 3:
|
||||||
|
|
@ -617,7 +620,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
|
||||||
# BUGFIX: don't use `iter` here as that would strip away
|
# BUGFIX: don't use `iter` here as that would strip away
|
||||||
# the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
|
# the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
|
||||||
# for an example:
|
# for an example:
|
||||||
v.typ = n.sons[length-2].typ
|
v.typ = iterBase
|
||||||
n.sons[0] = newSymNode(v)
|
n.sons[0] = newSymNode(v)
|
||||||
if sfGenSym notin v.flags: addForVarDecl(c, v)
|
if sfGenSym notin v.flags: addForVarDecl(c, v)
|
||||||
else:
|
else:
|
||||||
|
|
@ -651,11 +654,19 @@ proc semFor(c: PContext, n: PNode): PNode =
|
||||||
openScope(c)
|
openScope(c)
|
||||||
n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
|
n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
|
||||||
var call = n.sons[length-2]
|
var call = n.sons[length-2]
|
||||||
if call.kind in nkCallKinds and call.sons[0].typ.callConv == ccClosure:
|
let isCallExpr = call.kind in nkCallKinds
|
||||||
|
if isCallExpr and call.sons[0].sym.magic != mNone:
|
||||||
|
if call.sons[0].sym.magic == mOmpParFor:
|
||||||
|
result = semForVars(c, n)
|
||||||
|
result.kind = nkParForStmt
|
||||||
|
else:
|
||||||
|
result = semForFields(c, n, call.sons[0].sym.magic)
|
||||||
|
elif (isCallExpr and call.sons[0].typ.callConv == ccClosure) or
|
||||||
|
call.typ.kind == tyIter:
|
||||||
# first class iterator:
|
# first class iterator:
|
||||||
result = semForVars(c, n)
|
result = semForVars(c, n)
|
||||||
elif call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
|
elif not isCallExpr or call.sons[0].kind != nkSym or
|
||||||
call.sons[0].sym.kind != skIterator:
|
call.sons[0].sym.kind notin skIterators:
|
||||||
if length == 3:
|
if length == 3:
|
||||||
n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2])
|
n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2])
|
||||||
elif length == 4:
|
elif length == 4:
|
||||||
|
|
@ -663,12 +674,6 @@ proc semFor(c: PContext, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
localError(n.sons[length-2].info, errIteratorExpected)
|
localError(n.sons[length-2].info, errIteratorExpected)
|
||||||
result = semForVars(c, n)
|
result = semForVars(c, n)
|
||||||
elif call.sons[0].sym.magic != mNone:
|
|
||||||
if call.sons[0].sym.magic == mOmpParFor:
|
|
||||||
result = semForVars(c, n)
|
|
||||||
result.kind = nkParForStmt
|
|
||||||
else:
|
|
||||||
result = semForFields(c, n, call.sons[0].sym.magic)
|
|
||||||
else:
|
else:
|
||||||
result = semForVars(c, n)
|
result = semForVars(c, n)
|
||||||
# propagate any enforced VoidContext:
|
# propagate any enforced VoidContext:
|
||||||
|
|
@ -764,6 +769,29 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||||
s.ast = a
|
s.ast = a
|
||||||
popOwner()
|
popOwner()
|
||||||
|
|
||||||
|
proc checkForMetaFields(n: PNode) =
|
||||||
|
template checkMeta(t) =
|
||||||
|
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
|
||||||
|
localError(n.info, errTIsNotAConcreteType, t.typeToString)
|
||||||
|
|
||||||
|
case n.kind
|
||||||
|
of nkRecList, nkRecCase:
|
||||||
|
for s in n: checkForMetaFields(s)
|
||||||
|
of nkOfBranch, nkElse:
|
||||||
|
checkForMetaFields(n.lastSon)
|
||||||
|
of nkSym:
|
||||||
|
let t = n.sym.typ
|
||||||
|
case t.kind
|
||||||
|
of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef,
|
||||||
|
tyProc, tyGenericInvokation, tyGenericInst:
|
||||||
|
let start = ord(t.kind in {tyGenericInvokation, tyGenericInst})
|
||||||
|
for i in start .. <t.sons.len:
|
||||||
|
checkMeta(t.sons[i])
|
||||||
|
else:
|
||||||
|
checkMeta(t)
|
||||||
|
else:
|
||||||
|
internalAssert false
|
||||||
|
|
||||||
proc typeSectionFinalPass(c: PContext, n: PNode) =
|
proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
var a = n.sons[i]
|
var a = n.sons[i]
|
||||||
|
|
@ -780,6 +808,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||||
assignType(s.typ, t)
|
assignType(s.typ, t)
|
||||||
s.typ.id = t.id # same id
|
s.typ.id = t.id # same id
|
||||||
checkConstructedType(s.info, s.typ)
|
checkConstructedType(s.info, s.typ)
|
||||||
|
if s.typ.kind in {tyObject, tyTuple}:
|
||||||
|
checkForMetaFields(s.typ.n)
|
||||||
let aa = a.sons[2]
|
let aa = a.sons[2]
|
||||||
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
|
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
|
||||||
aa.sons[0].kind == nkObjectTy:
|
aa.sons[0].kind == nkObjectTy:
|
||||||
|
|
@ -883,12 +913,19 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
s = n[namePos].sym
|
s = n[namePos].sym
|
||||||
pushOwner(s)
|
pushOwner(s)
|
||||||
openScope(c)
|
openScope(c)
|
||||||
|
var gp: PNode
|
||||||
if n.sons[genericParamsPos].kind != nkEmpty:
|
if n.sons[genericParamsPos].kind != nkEmpty:
|
||||||
illFormedAst(n) # process parameters:
|
n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
|
||||||
|
gp = n.sons[genericParamsPos]
|
||||||
|
else:
|
||||||
|
gp = newNodeI(nkGenericParams, n.info)
|
||||||
|
|
||||||
if n.sons[paramsPos].kind != nkEmpty:
|
if n.sons[paramsPos].kind != nkEmpty:
|
||||||
var gp = newNodeI(nkGenericParams, n.info)
|
|
||||||
semParamList(c, n.sons[paramsPos], gp, s)
|
semParamList(c, n.sons[paramsPos], gp, s)
|
||||||
paramsTypeCheck(c, s.typ)
|
# paramsTypeCheck(c, s.typ)
|
||||||
|
if sonsLen(gp) > 0 and n.sons[genericParamsPos].kind == nkEmpty:
|
||||||
|
# we have a list of implicit type parameters:
|
||||||
|
n.sons[genericParamsPos] = gp
|
||||||
else:
|
else:
|
||||||
s.typ = newTypeS(tyProc, c)
|
s.typ = newTypeS(tyProc, c)
|
||||||
rawAddSon(s.typ, nil)
|
rawAddSon(s.typ, nil)
|
||||||
|
|
@ -900,12 +937,15 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
|
localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
|
||||||
#if efDetermineType notin flags:
|
#if efDetermineType notin flags:
|
||||||
# XXX not good enough; see tnamedparamanonproc.nim
|
# XXX not good enough; see tnamedparamanonproc.nim
|
||||||
pushProcCon(c, s)
|
if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags):
|
||||||
addResult(c, s.typ.sons[0], n.info, skProc)
|
pushProcCon(c, s)
|
||||||
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
addResult(c, s.typ.sons[0], n.info, skProc)
|
||||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||||
addResultNode(c, n)
|
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
||||||
popProcCon(c)
|
addResultNode(c, n)
|
||||||
|
popProcCon(c)
|
||||||
|
elif efOperand notin flags:
|
||||||
|
localError(n.info, errGenericLambdaNotAllowed)
|
||||||
sideEffectsCheck(c, s)
|
sideEffectsCheck(c, s)
|
||||||
else:
|
else:
|
||||||
localError(n.info, errImplOfXexpected, s.name.s)
|
localError(n.info, errImplOfXexpected, s.name.s)
|
||||||
|
|
@ -913,6 +953,34 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
popOwner()
|
popOwner()
|
||||||
result.typ = s.typ
|
result.typ = s.typ
|
||||||
|
|
||||||
|
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
|
||||||
|
var n = n
|
||||||
|
|
||||||
|
n = replaceTypesInBody(c, pt, n)
|
||||||
|
result = n
|
||||||
|
|
||||||
|
n.sons[genericParamsPos] = emptyNode
|
||||||
|
n.sons[paramsPos] = n.typ.n
|
||||||
|
|
||||||
|
openScope(c)
|
||||||
|
var s = n.sons[namePos].sym
|
||||||
|
addParams(c, n.typ.n, skProc)
|
||||||
|
pushProcCon(c, s)
|
||||||
|
addResult(c, n.typ.sons[0], n.info, skProc)
|
||||||
|
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||||
|
n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym)
|
||||||
|
addResultNode(c, n)
|
||||||
|
popProcCon(c)
|
||||||
|
closeScope(c)
|
||||||
|
|
||||||
|
s.ast = result
|
||||||
|
|
||||||
|
# alternative variant (not quite working):
|
||||||
|
# var prc = arg[0].sym
|
||||||
|
# let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
|
||||||
|
# result = inferred.ast
|
||||||
|
# result.kind = arg.kind
|
||||||
|
|
||||||
proc activate(c: PContext, n: PNode) =
|
proc activate(c: PContext, n: PNode) =
|
||||||
# XXX: This proc is part of my plan for getting rid of
|
# XXX: This proc is part of my plan for getting rid of
|
||||||
# forward declarations. stay tuned.
|
# forward declarations. stay tuned.
|
||||||
|
|
@ -927,8 +995,7 @@ proc activate(c: PContext, n: PNode) =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
|
proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
|
||||||
if s.typ.sons[0] != nil and
|
if s.typ.sons[0] != nil and s.kind != skIterator:
|
||||||
(s.kind != skIterator or s.typ.callConv == ccClosure):
|
|
||||||
addResult(c, s.typ.sons[0], n.info, s.kind)
|
addResult(c, s.typ.sons[0], n.info, s.kind)
|
||||||
addResultNode(c, n)
|
addResultNode(c, n)
|
||||||
|
|
||||||
|
|
@ -971,6 +1038,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
s = n[namePos].sym
|
s = n[namePos].sym
|
||||||
|
s.owner = getCurrOwner()
|
||||||
typeIsDetermined = s.typ == nil
|
typeIsDetermined = s.typ == nil
|
||||||
s.ast = n
|
s.ast = n
|
||||||
s.scope = c.currentScope
|
s.scope = c.currentScope
|
||||||
|
|
@ -1003,12 +1071,12 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
rawAddSon(s.typ, nil)
|
rawAddSon(s.typ, nil)
|
||||||
if n.sons[patternPos].kind != nkEmpty:
|
if n.sons[patternPos].kind != nkEmpty:
|
||||||
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
|
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
|
||||||
if s.kind == skIterator:
|
if s.kind in skIterators:
|
||||||
s.typ.flags.incl(tfIterator)
|
s.typ.flags.incl(tfIterator)
|
||||||
|
|
||||||
var proto = searchForProc(c, s.scope, s)
|
var proto = searchForProc(c, s.scope, s)
|
||||||
if proto == nil:
|
if proto == nil:
|
||||||
if s.kind == skIterator and isAnon: s.typ.callConv = ccClosure
|
if s.kind == skClosureIterator: s.typ.callConv = ccClosure
|
||||||
else: s.typ.callConv = lastOptionEntry(c).defaultCC
|
else: s.typ.callConv = lastOptionEntry(c).defaultCC
|
||||||
# add it here, so that recursive procs are possible:
|
# add it here, so that recursive procs are possible:
|
||||||
if sfGenSym in s.flags: discard
|
if sfGenSym in s.flags: discard
|
||||||
|
|
@ -1021,7 +1089,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
if n.sons[pragmasPos].kind != nkEmpty:
|
if n.sons[pragmasPos].kind != nkEmpty:
|
||||||
pragma(c, s, n.sons[pragmasPos], validPragmas)
|
pragma(c, s, n.sons[pragmasPos], validPragmas)
|
||||||
else:
|
else:
|
||||||
implictPragmas(c, s, n, validPragmas)
|
implicitPragmas(c, s, n, validPragmas)
|
||||||
else:
|
else:
|
||||||
if n.sons[pragmasPos].kind != nkEmpty:
|
if n.sons[pragmasPos].kind != nkEmpty:
|
||||||
localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProc)
|
localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProc)
|
||||||
|
|
@ -1068,7 +1136,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||||
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
n.sons[bodyPos] = transformBody(c.module, semBody, s)
|
||||||
popProcCon(c)
|
popProcCon(c)
|
||||||
else:
|
else:
|
||||||
if s.typ.sons[0] != nil and kind != skIterator:
|
if s.typ.sons[0] != nil and kind notin skIterators:
|
||||||
addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
|
addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
|
||||||
var toBind = initIntSet()
|
var toBind = initIntSet()
|
||||||
n.sons[bodyPos] = semGenericStmtScope(c, n.sons[bodyPos], {}, toBind)
|
n.sons[bodyPos] = semGenericStmtScope(c, n.sons[bodyPos], {}, toBind)
|
||||||
|
|
@ -1095,7 +1163,15 @@ proc determineType(c: PContext, s: PSym) =
|
||||||
discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType)
|
discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType)
|
||||||
|
|
||||||
proc semIterator(c: PContext, n: PNode): PNode =
|
proc semIterator(c: PContext, n: PNode): PNode =
|
||||||
result = semProcAux(c, n, skIterator, iteratorPragmas)
|
let kind = if hasPragma(n[pragmasPos], wClosure) or
|
||||||
|
n[namePos].kind == nkEmpty: skClosureIterator
|
||||||
|
else: skIterator
|
||||||
|
# gensym'ed iterator?
|
||||||
|
if n[namePos].kind == nkSym:
|
||||||
|
# gensym'ed iterators might need to become closure iterators:
|
||||||
|
n[namePos].sym.owner = getCurrOwner()
|
||||||
|
n[namePos].sym.kind = kind
|
||||||
|
result = semProcAux(c, n, kind, iteratorPragmas)
|
||||||
var s = result.sons[namePos].sym
|
var s = result.sons[namePos].sym
|
||||||
var t = s.typ
|
var t = s.typ
|
||||||
if t.sons[0] == nil and s.typ.callConv != ccClosure:
|
if t.sons[0] == nil and s.typ.callConv != ccClosure:
|
||||||
|
|
@ -1247,13 +1323,17 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
n.sons[i] = semExpr(c, n.sons[i])
|
n.sons[i] = semExpr(c, n.sons[i])
|
||||||
|
if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool:
|
||||||
|
let verdict = semConstExpr(c, n[i])
|
||||||
|
if verdict.intVal == 0:
|
||||||
|
localError(result.info, "type class predicate failed")
|
||||||
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
|
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
|
||||||
voidContext = true
|
voidContext = true
|
||||||
n.typ = enforceVoidContext
|
n.typ = enforceVoidContext
|
||||||
if i == last and efWantValue in flags:
|
if i == last and (length == 1 or efWantValue in flags):
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||||
elif i != last or voidContext or c.inTypeClass > 0:
|
elif i != last or voidContext:
|
||||||
discardCheck(c, n.sons[i])
|
discardCheck(c, n.sons[i])
|
||||||
else:
|
else:
|
||||||
n.typ = n.sons[i].typ
|
n.typ = n.sons[i].typ
|
||||||
|
|
@ -1263,8 +1343,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
let (outer, inner) = insertDestructors(c, n.sons[i])
|
let (outer, inner) = insertDestructors(c, n.sons[i])
|
||||||
if outer != nil:
|
if outer != nil:
|
||||||
n.sons[i] = outer
|
n.sons[i] = outer
|
||||||
for j in countup(i+1, length-1):
|
var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1])
|
||||||
inner.addSon(semStmt(c, n.sons[j]))
|
inner.addSon(semStmtList(c, rest, flags))
|
||||||
n.sons.setLen(i+1)
|
n.sons.setLen(i+1)
|
||||||
return
|
return
|
||||||
of LastBlockStmts:
|
of LastBlockStmts:
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,11 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
|
||||||
of nkPragmaExpr: x = x[0]
|
of nkPragmaExpr: x = x[0]
|
||||||
of nkIdent: break
|
of nkIdent: break
|
||||||
else: illFormedAst(x)
|
else: illFormedAst(x)
|
||||||
c.toInject.incl(x.ident.id)
|
let ident = getIdentNode(c, x)
|
||||||
|
if not isTemplParam(c, ident):
|
||||||
|
c.toInject.incl(x.ident.id)
|
||||||
|
else:
|
||||||
|
replaceIdentBySym(n, ident)
|
||||||
else:
|
else:
|
||||||
let ident = getIdentNode(c, n)
|
let ident = getIdentNode(c, n)
|
||||||
if not isTemplParam(c, ident):
|
if not isTemplParam(c, ident):
|
||||||
|
|
@ -171,7 +175,7 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
of skUnknown:
|
of skUnknown:
|
||||||
# Introduced in this pass! Leave it as an identifier.
|
# Introduced in this pass! Leave it as an identifier.
|
||||||
result = n
|
result = n
|
||||||
of skProc, skMethod, skIterator, skConverter, skTemplate, skMacro:
|
of OverloadableSyms:
|
||||||
result = symChoice(c, n, s, scOpen)
|
result = symChoice(c, n, s, scOpen)
|
||||||
of skGenericParam:
|
of skGenericParam:
|
||||||
result = newSymNodeTypeDesc(s, n.info)
|
result = newSymNodeTypeDesc(s, n.info)
|
||||||
|
|
@ -228,6 +232,18 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind) =
|
||||||
for j in countup(0, L-3):
|
for j in countup(0, L-3):
|
||||||
addLocalDecl(c, a.sons[j], symKind)
|
addLocalDecl(c, a.sons[j], symKind)
|
||||||
|
|
||||||
|
proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
|
||||||
|
result = n
|
||||||
|
if n.kind == nkIdent:
|
||||||
|
let s = qualifiedLookUp(c.c, n, {})
|
||||||
|
if s != nil:
|
||||||
|
if s.owner == c.owner and s.kind == skParam:
|
||||||
|
incl(s.flags, sfUsed)
|
||||||
|
result = newSymNode(s, n.info)
|
||||||
|
else:
|
||||||
|
for i in 0 .. <n.safeLen:
|
||||||
|
result.sons[i] = onlyReplaceParams(c, n.sons[i])
|
||||||
|
|
||||||
proc semPattern(c: PContext, n: PNode): PNode
|
proc semPattern(c: PContext, n: PNode): PNode
|
||||||
proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
|
|
@ -348,7 +364,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
of nkMethodDef:
|
of nkMethodDef:
|
||||||
result = semRoutineInTemplBody(c, n, skMethod)
|
result = semRoutineInTemplBody(c, n, skMethod)
|
||||||
of nkIteratorDef:
|
of nkIteratorDef:
|
||||||
result = semRoutineInTemplBody(c, n, skIterator)
|
let kind = if hasPragma(n[pragmasPos], wClosure): skClosureIterator
|
||||||
|
else: skIterator
|
||||||
|
result = semRoutineInTemplBody(c, n, kind)
|
||||||
of nkTemplateDef:
|
of nkTemplateDef:
|
||||||
result = semRoutineInTemplBody(c, n, skTemplate)
|
result = semRoutineInTemplBody(c, n, skTemplate)
|
||||||
of nkMacroDef:
|
of nkMacroDef:
|
||||||
|
|
@ -357,8 +375,10 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||||
result = semRoutineInTemplBody(c, n, skConverter)
|
result = semRoutineInTemplBody(c, n, skConverter)
|
||||||
of nkPragmaExpr:
|
of nkPragmaExpr:
|
||||||
result.sons[0] = semTemplBody(c, n.sons[0])
|
result.sons[0] = semTemplBody(c, n.sons[0])
|
||||||
|
of nkPostfix:
|
||||||
|
result.sons[1] = semTemplBody(c, n.sons[1])
|
||||||
of nkPragma:
|
of nkPragma:
|
||||||
discard
|
result = onlyReplaceParams(c, n)
|
||||||
else:
|
else:
|
||||||
# dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
|
# dotExpr is ambiguous: note that we explicitely allow 'x.TemplateParam',
|
||||||
# so we use the generic code for nkDotExpr too
|
# so we use the generic code for nkDotExpr too
|
||||||
|
|
|
||||||
|
|
@ -141,11 +141,10 @@ proc semVarType(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = newConstraint(c, tyVar)
|
result = newConstraint(c, tyVar)
|
||||||
|
|
||||||
proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
|
proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
|
||||||
if sonsLen(n) == 1:
|
if n.len == 0: return newConstraint(c, tyDistinct)
|
||||||
result = newOrPrevType(tyDistinct, prev, c)
|
result = newOrPrevType(tyDistinct, prev, c)
|
||||||
addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil))
|
addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil))
|
||||||
else:
|
if n.len > 1: result.n = n[1]
|
||||||
result = newConstraint(c, tyDistinct)
|
|
||||||
|
|
||||||
proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||||
assert isRange(n)
|
assert isRange(n)
|
||||||
|
|
@ -154,19 +153,31 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result.n = newNodeI(nkRange, n.info)
|
result.n = newNodeI(nkRange, n.info)
|
||||||
if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty):
|
if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty):
|
||||||
localError(n.info, errRangeIsEmpty)
|
localError(n.info, errRangeIsEmpty)
|
||||||
var a = semConstExpr(c, n[1])
|
|
||||||
var b = semConstExpr(c, n[2])
|
var range: array[2, PNode]
|
||||||
if not sameType(a.typ, b.typ):
|
range[0] = semExprWithType(c, n[1], {efDetermineType})
|
||||||
|
range[1] = semExprWithType(c, n[2], {efDetermineType})
|
||||||
|
|
||||||
|
var rangeT: array[2, PType]
|
||||||
|
for i in 0..1: rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit
|
||||||
|
|
||||||
|
if not sameType(rangeT[0], rangeT[1]):
|
||||||
localError(n.info, errPureTypeMismatch)
|
localError(n.info, errPureTypeMismatch)
|
||||||
elif a.typ.kind notin {tyInt..tyInt64,tyEnum,tyBool,tyChar,
|
elif not rangeT[0].isOrdinalType:
|
||||||
tyFloat..tyFloat128,tyUInt8..tyUInt32}:
|
|
||||||
localError(n.info, errOrdinalTypeExpected)
|
localError(n.info, errOrdinalTypeExpected)
|
||||||
elif enumHasHoles(a.typ):
|
elif enumHasHoles(rangeT[0]):
|
||||||
localError(n.info, errEnumXHasHoles, a.typ.sym.name.s)
|
localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s)
|
||||||
elif not leValue(a, b): localError(n.info, errRangeIsEmpty)
|
|
||||||
addSon(result.n, a)
|
for i in 0..1:
|
||||||
addSon(result.n, b)
|
if hasGenericArguments(range[i]):
|
||||||
addSonSkipIntLit(result, b.typ)
|
result.n.addSon makeStaticExpr(c, range[i])
|
||||||
|
else:
|
||||||
|
result.n.addSon semConstExpr(c, range[i])
|
||||||
|
|
||||||
|
if weakLeValue(result.n[0], result.n[1]) == impNo:
|
||||||
|
localError(n.info, errRangeIsEmpty)
|
||||||
|
|
||||||
|
addSonSkipIntLit(result, rangeT[0])
|
||||||
|
|
||||||
proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = nil
|
result = nil
|
||||||
|
|
@ -186,11 +197,6 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
|
||||||
localError(n.info, errXExpectsOneTypeParam, "range")
|
localError(n.info, errXExpectsOneTypeParam, "range")
|
||||||
result = newOrPrevType(tyError, prev, c)
|
result = newOrPrevType(tyError, prev, c)
|
||||||
|
|
||||||
proc nMinusOne(n: PNode): PNode =
|
|
||||||
result = newNode(nkCall, n.info, @[
|
|
||||||
newSymNode(getSysMagic("<", mUnaryLt)),
|
|
||||||
n])
|
|
||||||
|
|
||||||
proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||||
var indx, base: PType
|
var indx, base: PType
|
||||||
result = newOrPrevType(tyArray, prev, c)
|
result = newOrPrevType(tyArray, prev, c)
|
||||||
|
|
@ -200,7 +206,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||||
else:
|
else:
|
||||||
let e = semExprWithType(c, n.sons[1], {efDetermineType})
|
let e = semExprWithType(c, n.sons[1], {efDetermineType})
|
||||||
if e.typ.kind == tyFromExpr:
|
if e.typ.kind == tyFromExpr:
|
||||||
indx = e.typ
|
indx = makeRangeWithStaticExpr(c, e.typ.n)
|
||||||
elif e.kind in {nkIntLit..nkUInt64Lit}:
|
elif e.kind in {nkIntLit..nkUInt64Lit}:
|
||||||
indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
|
indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
|
||||||
elif e.kind == nkSym and e.typ.kind == tyStatic:
|
elif e.kind == nkSym and e.typ.kind == tyStatic:
|
||||||
|
|
@ -208,7 +214,8 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||||
internalAssert c.inGenericContext > 0
|
internalAssert c.inGenericContext > 0
|
||||||
if not isOrdinalType(e.typ.lastSon):
|
if not isOrdinalType(e.typ.lastSon):
|
||||||
localError(n[1].info, errOrdinalTypeExpected)
|
localError(n[1].info, errOrdinalTypeExpected)
|
||||||
indx = e.typ
|
indx = makeRangeWithStaticExpr(c, e)
|
||||||
|
indx.flags.incl tfUnresolved
|
||||||
elif e.kind in nkCallKinds and hasGenericArguments(e):
|
elif e.kind in nkCallKinds and hasGenericArguments(e):
|
||||||
if not isOrdinalType(e.typ):
|
if not isOrdinalType(e.typ):
|
||||||
localError(n[1].info, errOrdinalTypeExpected)
|
localError(n[1].info, errOrdinalTypeExpected)
|
||||||
|
|
@ -217,12 +224,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||||
# We are going to construct a range type that will be
|
# We are going to construct a range type that will be
|
||||||
# properly filled-out in semtypinst (see how tyStaticExpr
|
# properly filled-out in semtypinst (see how tyStaticExpr
|
||||||
# is handled there).
|
# is handled there).
|
||||||
let intType = getSysType(tyInt)
|
indx = makeRangeWithStaticExpr(c, e)
|
||||||
indx = newTypeS(tyRange, c)
|
|
||||||
indx.sons = @[intType]
|
|
||||||
indx.n = newNode(nkRange, n.info, @[
|
|
||||||
newIntTypeNode(nkIntLit, 0, intType),
|
|
||||||
makeStaticExpr(c, e.nMinusOne)])
|
|
||||||
else:
|
else:
|
||||||
indx = e.typ.skipTypes({tyTypeDesc})
|
indx = e.typ.skipTypes({tyTypeDesc})
|
||||||
addSonSkipIntLit(result, indx)
|
addSonSkipIntLit(result, indx)
|
||||||
|
|
@ -271,6 +273,18 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||||
result = result.typ.sym.copySym
|
result = result.typ.sym.copySym
|
||||||
result.typ = copyType(result.typ, result.typ.owner, true)
|
result.typ = copyType(result.typ, result.typ.owner, true)
|
||||||
result.typ.flags.incl tfUnresolved
|
result.typ.flags.incl tfUnresolved
|
||||||
|
|
||||||
|
if result.kind == skGenericParam:
|
||||||
|
if result.typ.kind == tyGenericParam and result.typ.len == 0 and
|
||||||
|
tfWildcard in result.typ.flags:
|
||||||
|
# collapse the wild-card param to a type
|
||||||
|
result.kind = skType
|
||||||
|
result.typ.flags.excl tfWildcard
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
localError(n.info, errTypeExpected)
|
||||||
|
return errorSym(c, n)
|
||||||
|
|
||||||
if result.kind != skType:
|
if result.kind != skType:
|
||||||
# this implements the wanted ``var v: V, x: V`` feature ...
|
# this implements the wanted ``var v: V, x: V`` feature ...
|
||||||
var ov: TOverloadIter
|
var ov: TOverloadIter
|
||||||
|
|
@ -601,16 +615,36 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
if base == nil and tfInheritable notin result.flags:
|
if base == nil and tfInheritable notin result.flags:
|
||||||
incl(result.flags, tfFinal)
|
incl(result.flags, tfFinal)
|
||||||
|
|
||||||
|
proc findEnforcedStaticType(t: PType): PType =
|
||||||
|
# This handles types such as `static[T] and Foo`,
|
||||||
|
# which are subset of `static[T]`, hence they could
|
||||||
|
# be treated in the same way
|
||||||
|
if t.kind == tyStatic: return t
|
||||||
|
if t.kind == tyAnd:
|
||||||
|
for s in t.sons:
|
||||||
|
let t = findEnforcedStaticType(s)
|
||||||
|
if t != nil: return t
|
||||||
|
|
||||||
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
||||||
if kind == skMacro and param.typ.kind notin {tyTypeDesc, tyStatic}:
|
template addDecl(x) =
|
||||||
# within a macro, every param has the type PNimrodNode!
|
if sfGenSym notin x.flags: addDecl(c, x)
|
||||||
# and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}:
|
|
||||||
let nn = getSysSym"PNimrodNode"
|
if kind == skMacro:
|
||||||
var a = copySym(param)
|
let staticType = findEnforcedStaticType(param.typ)
|
||||||
a.typ = nn.typ
|
if staticType != nil:
|
||||||
if sfGenSym notin a.flags: addDecl(c, a)
|
var a = copySym(param)
|
||||||
|
a.typ = staticType.base
|
||||||
|
addDecl(a)
|
||||||
|
elif param.typ.kind == tyTypeDesc:
|
||||||
|
addDecl(param)
|
||||||
|
else:
|
||||||
|
# within a macro, every param has the type PNimrodNode!
|
||||||
|
let nn = getSysSym"PNimrodNode"
|
||||||
|
var a = copySym(param)
|
||||||
|
a.typ = nn.typ
|
||||||
|
addDecl(a)
|
||||||
else:
|
else:
|
||||||
if sfGenSym notin param.flags: addDecl(c, param)
|
addDecl(param)
|
||||||
|
|
||||||
let typedescId = getIdent"typedesc"
|
let typedescId = getIdent"typedesc"
|
||||||
|
|
||||||
|
|
@ -644,6 +678,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
s.position = genericParams.len
|
s.position = genericParams.len
|
||||||
genericParams.addSon(newSymNode(s))
|
genericParams.addSon(newSymNode(s))
|
||||||
result = typeClass
|
result = typeClass
|
||||||
|
addDecl(c, s)
|
||||||
|
|
||||||
# XXX: There are codegen errors if this is turned into a nested proc
|
# XXX: There are codegen errors if this is turned into a nested proc
|
||||||
template liftingWalk(typ: PType, anonFlag = false): expr =
|
template liftingWalk(typ: PType, anonFlag = false): expr =
|
||||||
|
|
@ -653,6 +688,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name
|
var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name
|
||||||
else: nil
|
else: nil
|
||||||
|
|
||||||
|
template maybeLift(typ: PType): expr =
|
||||||
|
let lifted = liftingWalk(typ)
|
||||||
|
(if lifted != nil: lifted else: typ)
|
||||||
|
|
||||||
template addImplicitGeneric(e: expr): expr =
|
template addImplicitGeneric(e: expr): expr =
|
||||||
addImplicitGenericImpl(e, paramTypId)
|
addImplicitGenericImpl(e, paramTypId)
|
||||||
|
|
||||||
|
|
@ -663,15 +702,21 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
of tyStatic:
|
of tyStatic:
|
||||||
# proc(a: expr{string}, b: expr{nkLambda})
|
# proc(a: expr{string}, b: expr{nkLambda})
|
||||||
# overload on compile time values and AST trees
|
# overload on compile time values and AST trees
|
||||||
result = addImplicitGeneric(c.newTypeWithSons(tyStatic, paramType.sons))
|
if paramType.n != nil: return # this is a concrete type
|
||||||
result.flags.incl tfHasStatic
|
if tfUnresolved in paramType.flags: return # already lifted
|
||||||
|
let base = paramType.base.maybeLift
|
||||||
|
if base.isMetaType and procKind == skMacro:
|
||||||
|
localError(info, errMacroBodyDependsOnGenericTypes, paramName)
|
||||||
|
result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base]))
|
||||||
|
result.flags.incl({tfHasStatic, tfUnresolved})
|
||||||
|
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
if tfUnresolved notin paramType.flags:
|
if tfUnresolved notin paramType.flags:
|
||||||
# naked typedescs are not bindOnce types
|
# naked typedescs are not bindOnce types
|
||||||
if paramType.sonsLen == 0 and paramTypId != nil and
|
if paramType.base.kind == tyNone and paramTypId != nil and
|
||||||
paramTypId.id == typedescId.id: paramTypId = nil
|
paramTypId.id == typedescId.id: paramTypId = nil
|
||||||
result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons))
|
result = addImplicitGeneric(
|
||||||
|
c.newTypeWithSons(tyTypeDesc, @[paramType.base]))
|
||||||
|
|
||||||
of tyDistinct:
|
of tyDistinct:
|
||||||
if paramType.sonsLen == 1:
|
if paramType.sonsLen == 1:
|
||||||
|
|
@ -703,12 +748,26 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
for i in 0 .. paramType.sonsLen - 2:
|
for i in 0 .. paramType.sonsLen - 2:
|
||||||
result.rawAddSon newTypeS(tyAnything, c)
|
result.rawAddSon newTypeS(tyAnything, c)
|
||||||
# result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
|
# result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
|
||||||
|
|
||||||
|
if paramType.lastSon.kind == tyUserTypeClass:
|
||||||
|
result.kind = tyUserTypeClassInst
|
||||||
|
result.rawAddSon paramType.lastSon
|
||||||
|
return addImplicitGeneric(result)
|
||||||
|
|
||||||
result = instGenericContainer(c, paramType.sym.info, result,
|
result = instGenericContainer(c, paramType.sym.info, result,
|
||||||
allowMetaTypes = true)
|
allowMetaTypes = true)
|
||||||
result.lastSon.shouldHaveMeta
|
|
||||||
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
|
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
|
||||||
result = addImplicitGeneric(result)
|
result = addImplicitGeneric(result)
|
||||||
|
|
||||||
|
of tyIter:
|
||||||
|
if paramType.callConv == ccInline:
|
||||||
|
if procKind notin {skTemplate, skMacro, skIterator}:
|
||||||
|
localError(info, errInlineIteratorsAsProcParams)
|
||||||
|
if paramType.len == 1:
|
||||||
|
let lifted = liftingWalk(paramType.base)
|
||||||
|
if lifted != nil: paramType.sons[0] = lifted
|
||||||
|
result = addImplicitGeneric(paramType)
|
||||||
|
|
||||||
of tyGenericInst:
|
of tyGenericInst:
|
||||||
if paramType.lastSon.kind == tyUserTypeClass:
|
if paramType.lastSon.kind == tyUserTypeClass:
|
||||||
var cp = copyType(paramType, getCurrOwner(), false)
|
var cp = copyType(paramType, getCurrOwner(), false)
|
||||||
|
|
@ -722,7 +781,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
result = paramType
|
result = paramType
|
||||||
result.lastSon.shouldHaveMeta
|
result.lastSon.shouldHaveMeta
|
||||||
|
|
||||||
let liftBody = liftingWalk(paramType.lastSon)
|
let liftBody = liftingWalk(paramType.lastSon, true)
|
||||||
if liftBody != nil:
|
if liftBody != nil:
|
||||||
result = liftBody
|
result = liftBody
|
||||||
result.shouldHaveMeta
|
result.shouldHaveMeta
|
||||||
|
|
@ -734,7 +793,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
|
|
||||||
let expanded = instGenericContainer(c, info, paramType,
|
let expanded = instGenericContainer(c, info, paramType,
|
||||||
allowMetaTypes = true)
|
allowMetaTypes = true)
|
||||||
result = liftingWalk(expanded)
|
result = liftingWalk(expanded, true)
|
||||||
|
|
||||||
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
|
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
|
||||||
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
|
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
|
||||||
|
|
@ -744,11 +803,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
result = addImplicitGeneric(newTypeS(tyAnything, c))
|
result = addImplicitGeneric(newTypeS(tyAnything, c))
|
||||||
|
|
||||||
of tyGenericParam:
|
of tyGenericParam:
|
||||||
if tfGenericTypeParam in paramType.flags and false:
|
markUsed(genericParams, paramType.sym)
|
||||||
if paramType.sonsLen > 0:
|
if tfWildcard in paramType.flags:
|
||||||
result = liftingWalk(paramType.lastSon)
|
paramType.flags.excl tfWildcard
|
||||||
else:
|
paramType.sym.kind = skType
|
||||||
result = addImplicitGeneric(newTypeS(tyAnything, c))
|
|
||||||
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
|
|
@ -827,9 +885,13 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
addParamOrResult(c, arg, kind)
|
addParamOrResult(c, arg, kind)
|
||||||
if gCmd == cmdPretty: checkDef(a.sons[j], arg)
|
if gCmd == cmdPretty: checkDef(a.sons[j], arg)
|
||||||
|
|
||||||
|
var r: PType
|
||||||
if n.sons[0].kind != nkEmpty:
|
if n.sons[0].kind != nkEmpty:
|
||||||
var r = semTypeNode(c, n.sons[0], nil)
|
r = semTypeNode(c, n.sons[0], nil)
|
||||||
|
elif kind == skIterator:
|
||||||
|
r = newTypeS(tyAnything, c)
|
||||||
|
|
||||||
|
if r != nil:
|
||||||
# turn explicit 'void' return type into 'nil' because the rest of the
|
# turn explicit 'void' return type into 'nil' because the rest of the
|
||||||
# compiler only checks for 'nil':
|
# compiler only checks for 'nil':
|
||||||
if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
|
if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
|
||||||
|
|
@ -838,8 +900,20 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
n.sons[0].info)
|
n.sons[0].info)
|
||||||
if lifted != nil: r = lifted
|
if lifted != nil: r = lifted
|
||||||
r.flags.incl tfRetType
|
r.flags.incl tfRetType
|
||||||
result.sons[0] = skipIntLit(r)
|
r = skipIntLit(r)
|
||||||
res.typ = result.sons[0]
|
if kind == skIterator:
|
||||||
|
# see tchainediterators
|
||||||
|
# in cases like iterator foo(it: iterator): type(it)
|
||||||
|
# we don't need to change the return type to iter[T]
|
||||||
|
if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r])
|
||||||
|
result.sons[0] = r
|
||||||
|
res.typ = r
|
||||||
|
|
||||||
|
if genericParams != nil:
|
||||||
|
for n in genericParams:
|
||||||
|
if tfWildcard in n.sym.typ.flags:
|
||||||
|
n.sym.kind = skType
|
||||||
|
n.sym.typ.flags.excl tfWildcard
|
||||||
|
|
||||||
proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
|
proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
|
||||||
checkMinSonsLen(n, 1)
|
checkMinSonsLen(n, 1)
|
||||||
|
|
@ -869,8 +943,17 @@ proc semGenericParamInInvokation(c: PContext, n: PNode): PType =
|
||||||
result = semTypeNode(c, n, nil)
|
result = semTypeNode(c, n, nil)
|
||||||
|
|
||||||
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||||
|
if s.typ == nil:
|
||||||
|
localError(n.info, "cannot instantiate the '$1' $2" %
|
||||||
|
[s.name.s, ($s.kind).substr(2).toLower])
|
||||||
|
return newOrPrevType(tyError, prev, c)
|
||||||
|
|
||||||
|
var t = s.typ
|
||||||
|
if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
|
||||||
|
t = t.base
|
||||||
|
|
||||||
result = newOrPrevType(tyGenericInvokation, prev, c)
|
result = newOrPrevType(tyGenericInvokation, prev, c)
|
||||||
addSonSkipIntLit(result, s.typ)
|
addSonSkipIntLit(result, t)
|
||||||
|
|
||||||
template addToResult(typ) =
|
template addToResult(typ) =
|
||||||
if typ.isNil:
|
if typ.isNil:
|
||||||
|
|
@ -878,27 +961,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||||
rawAddSon(result, typ)
|
rawAddSon(result, typ)
|
||||||
else: addSonSkipIntLit(result, typ)
|
else: addSonSkipIntLit(result, typ)
|
||||||
|
|
||||||
if s.typ == nil:
|
if t.kind == tyForward:
|
||||||
localError(n.info, errCannotInstantiateX, s.name.s)
|
|
||||||
return newOrPrevType(tyError, prev, c)
|
|
||||||
elif s.typ.kind == tyForward:
|
|
||||||
for i in countup(1, sonsLen(n)-1):
|
for i in countup(1, sonsLen(n)-1):
|
||||||
var elem = semGenericParamInInvokation(c, n.sons[i])
|
var elem = semGenericParamInInvokation(c, n.sons[i])
|
||||||
addToResult(elem)
|
addToResult(elem)
|
||||||
elif s.typ.kind != tyGenericBody:
|
return
|
||||||
|
elif t.kind != tyGenericBody:
|
||||||
#we likely got code of the form TypeA[TypeB] where TypeA is
|
#we likely got code of the form TypeA[TypeB] where TypeA is
|
||||||
#not generic.
|
#not generic.
|
||||||
localError(n.info, errNoGenericParamsAllowedForX, s.name.s)
|
localError(n.info, errNoGenericParamsAllowedForX, s.name.s)
|
||||||
return newOrPrevType(tyError, prev, c)
|
return newOrPrevType(tyError, prev, c)
|
||||||
else:
|
else:
|
||||||
|
var m = newCandidate(c, t)
|
||||||
var m = newCandidate(c, s, n)
|
|
||||||
matches(c, n, copyTree(n), m)
|
matches(c, n, copyTree(n), m)
|
||||||
|
|
||||||
if m.state != csMatch:
|
if m.state != csMatch:
|
||||||
var err = "cannot instantiate " & typeToString(s.typ) & "\n" &
|
var err = "cannot instantiate " & typeToString(t) & "\n" &
|
||||||
"got: (" & describeArgs(c, n) & ")\n" &
|
"got: (" & describeArgs(c, n) & ")\n" &
|
||||||
"but expected: (" & describeArgs(c, s.typ.n, 0) & ")"
|
"but expected: (" & describeArgs(c, t.n, 0) & ")"
|
||||||
localError(n.info, errGenerated, err)
|
localError(n.info, errGenerated, err)
|
||||||
return newOrPrevType(tyError, prev, c)
|
return newOrPrevType(tyError, prev, c)
|
||||||
|
|
||||||
|
|
@ -910,7 +990,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||||
addToResult(typ)
|
addToResult(typ)
|
||||||
|
|
||||||
if isConcrete:
|
if isConcrete:
|
||||||
if s.ast == nil:
|
if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
|
||||||
|
# XXX: What kind of error is this? is it still relevant?
|
||||||
localError(n.info, errCannotInstantiateX, s.name.s)
|
localError(n.info, errCannotInstantiateX, s.name.s)
|
||||||
result = newOrPrevType(tyError, prev, c)
|
result = newOrPrevType(tyError, prev, c)
|
||||||
else:
|
else:
|
||||||
|
|
@ -944,6 +1025,23 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||||
let typ = semTypeNode(c, n, nil)
|
let typ = semTypeNode(c, n, nil)
|
||||||
result.sons.safeAdd(typ)
|
result.sons.safeAdd(typ)
|
||||||
|
|
||||||
|
proc semProcTypeWithScope(c: PContext, n: PNode,
|
||||||
|
prev: PType, kind: TSymKind): PType =
|
||||||
|
checkSonsLen(n, 2)
|
||||||
|
openScope(c)
|
||||||
|
result = semProcTypeNode(c, n.sons[0], nil, prev, kind)
|
||||||
|
# dummy symbol for `pragma`:
|
||||||
|
var s = newSymS(kind, newIdentNode(getIdent("dummy"), n.info), c)
|
||||||
|
s.typ = result
|
||||||
|
if n.sons[1].kind == nkEmpty or n.sons[1].len == 0:
|
||||||
|
if result.callConv == ccDefault:
|
||||||
|
result.callConv = ccClosure
|
||||||
|
#Message(n.info, warnImplicitClosure, renderTree(n))
|
||||||
|
else:
|
||||||
|
pragma(c, s, n.sons[1], procTypePragmas)
|
||||||
|
when useEffectSystem: setEffectsForProcType(result, n.sons[1])
|
||||||
|
closeScope(c)
|
||||||
|
|
||||||
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
result = nil
|
result = nil
|
||||||
if gCmd == cmdIdeTools: suggestExpr(c, n)
|
if gCmd == cmdIdeTools: suggestExpr(c, n)
|
||||||
|
|
@ -952,7 +1050,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
of nkTypeOfExpr:
|
of nkTypeOfExpr:
|
||||||
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
||||||
checkSonsLen(n, 1)
|
checkSonsLen(n, 1)
|
||||||
result = semExprWithType(c, n.sons[0], {efInTypeof}).typ
|
let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
|
||||||
|
result = typExpr.typ.skipTypes({tyIter})
|
||||||
of nkPar:
|
of nkPar:
|
||||||
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
|
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1011,28 +1110,32 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
of mOrdinal: result = semOrdinal(c, n, prev)
|
of mOrdinal: result = semOrdinal(c, n, prev)
|
||||||
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
|
||||||
of mVarargs: result = semVarargs(c, n, prev)
|
of mVarargs: result = semVarargs(c, n, prev)
|
||||||
of mExpr, mTypeDesc:
|
of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
|
||||||
|
of mExpr:
|
||||||
result = semTypeNode(c, n.sons[0], nil)
|
result = semTypeNode(c, n.sons[0], nil)
|
||||||
if result != nil:
|
if result != nil:
|
||||||
result = copyType(result, getCurrOwner(), false)
|
result = copyType(result, getCurrOwner(), false)
|
||||||
for i in countup(1, n.len - 1):
|
for i in countup(1, n.len - 1):
|
||||||
result.rawAddSon(semTypeNode(c, n.sons[i], nil))
|
result.rawAddSon(semTypeNode(c, n.sons[i], nil))
|
||||||
else: result = semGeneric(c, n, s, prev)
|
else: result = semGeneric(c, n, s, prev)
|
||||||
of nkIdent, nkDotExpr, nkAccQuoted:
|
of nkDotExpr:
|
||||||
if n.kind == nkDotExpr:
|
var typeExpr = semExpr(c, n)
|
||||||
let head = qualifiedLookUp(c, n[0], {checkAmbiguity, checkUndeclared})
|
if typeExpr.typ.kind != tyTypeDesc:
|
||||||
if head.kind in {skType}:
|
localError(n.info, errTypeExpected)
|
||||||
var toBind = initIntSet()
|
return errorType(c)
|
||||||
var preprocessed = semGenericStmt(c, n, {}, toBind)
|
result = typeExpr.typ.base
|
||||||
return makeTypeFromExpr(c, preprocessed)
|
if result.isMetaType:
|
||||||
|
var toBind = initIntSet()
|
||||||
|
var preprocessed = semGenericStmt(c, n, {}, toBind)
|
||||||
|
return makeTypeFromExpr(c, preprocessed)
|
||||||
|
of nkIdent, nkAccQuoted:
|
||||||
var s = semTypeIdent(c, n)
|
var s = semTypeIdent(c, n)
|
||||||
if s.typ == nil:
|
if s.typ == nil:
|
||||||
if s.kind != skError: localError(n.info, errTypeExpected)
|
if s.kind != skError: localError(n.info, errTypeExpected)
|
||||||
result = newOrPrevType(tyError, prev, c)
|
result = newOrPrevType(tyError, prev, c)
|
||||||
elif s.kind == skParam and s.typ.kind == tyTypeDesc:
|
elif s.kind == skParam and s.typ.kind == tyTypeDesc:
|
||||||
assert s.typ.len > 0
|
internalAssert s.typ.base.kind != tyNone and prev == nil
|
||||||
internalAssert prev == nil
|
result = s.typ.base
|
||||||
result = s.typ.sons[0]
|
|
||||||
elif prev == nil:
|
elif prev == nil:
|
||||||
result = s.typ
|
result = s.typ
|
||||||
else:
|
else:
|
||||||
|
|
@ -1066,27 +1169,22 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
var base = semTypeNode(c, n.sons[0], nil)
|
var base = semTypeNode(c, n.sons[0], nil)
|
||||||
result.rawAddSon(base)
|
result.rawAddSon(base)
|
||||||
result.flags.incl tfHasStatic
|
result.flags.incl tfHasStatic
|
||||||
of nkProcTy, nkIteratorTy:
|
of nkIteratorTy:
|
||||||
|
if n.sonsLen == 0:
|
||||||
|
result = newConstraint(c, tyIter)
|
||||||
|
else:
|
||||||
|
result = semProcTypeWithScope(c, n, prev, skClosureIterator)
|
||||||
|
if n.lastSon.kind == nkPragma and hasPragma(n.lastSon, wInline):
|
||||||
|
result.kind = tyIter
|
||||||
|
result.callConv = ccInline
|
||||||
|
else:
|
||||||
|
result.flags.incl(tfIterator)
|
||||||
|
result.callConv = ccClosure
|
||||||
|
of nkProcTy:
|
||||||
if n.sonsLen == 0:
|
if n.sonsLen == 0:
|
||||||
result = newConstraint(c, tyProc)
|
result = newConstraint(c, tyProc)
|
||||||
else:
|
else:
|
||||||
checkSonsLen(n, 2)
|
result = semProcTypeWithScope(c, n, prev, skProc)
|
||||||
openScope(c)
|
|
||||||
result = semProcTypeNode(c, n.sons[0], nil, prev, skProc)
|
|
||||||
# dummy symbol for `pragma`:
|
|
||||||
var s = newSymS(skProc, newIdentNode(getIdent("dummy"), n.info), c)
|
|
||||||
s.typ = result
|
|
||||||
if n.sons[1].kind == nkEmpty or n.sons[1].len == 0:
|
|
||||||
if result.callConv == ccDefault:
|
|
||||||
result.callConv = ccClosure
|
|
||||||
#Message(n.info, warnImplicitClosure, renderTree(n))
|
|
||||||
else:
|
|
||||||
pragma(c, s, n.sons[1], procTypePragmas)
|
|
||||||
when useEffectSystem: setEffectsForProcType(result, n.sons[1])
|
|
||||||
closeScope(c)
|
|
||||||
if n.kind == nkIteratorTy:
|
|
||||||
result.flags.incl(tfIterator)
|
|
||||||
result.callConv = ccClosure
|
|
||||||
of nkEnumTy: result = semEnum(c, n, prev)
|
of nkEnumTy: result = semEnum(c, n, prev)
|
||||||
of nkType: result = n.typ
|
of nkType: result = n.typ
|
||||||
of nkStmtListType: result = semStmtListType(c, n, prev)
|
of nkStmtListType: result = semStmtListType(c, n, prev)
|
||||||
|
|
@ -1196,6 +1294,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||||
|
|
||||||
if typ == nil:
|
if typ == nil:
|
||||||
typ = newTypeS(tyGenericParam, c)
|
typ = newTypeS(tyGenericParam, c)
|
||||||
|
if father == nil: typ.flags.incl tfWildcard
|
||||||
|
|
||||||
typ.flags.incl tfGenericTypeParam
|
typ.flags.incl tfGenericTypeParam
|
||||||
|
|
||||||
|
|
@ -1206,8 +1305,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||||
# type for each generic param. the index
|
# type for each generic param. the index
|
||||||
# of the parameter will be stored in the
|
# of the parameter will be stored in the
|
||||||
# attached symbol.
|
# attached symbol.
|
||||||
var s = case finalType.kind
|
var s = if finalType.kind == tyStatic or tfWildcard in typ.flags:
|
||||||
of tyStatic:
|
|
||||||
newSymG(skGenericParam, a.sons[j], c).linkTo(finalType)
|
newSymG(skGenericParam, a.sons[j], c).linkTo(finalType)
|
||||||
else:
|
else:
|
||||||
newSymG(skType, a.sons[j], c).linkTo(finalType)
|
newSymG(skType, a.sons[j], c).linkTo(finalType)
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
|
||||||
localError(info, errVarVarTypeNotAllowed)
|
localError(info, errVarVarTypeNotAllowed)
|
||||||
elif computeSize(t) == szIllegalRecursion:
|
elif computeSize(t) == szIllegalRecursion:
|
||||||
localError(info, errIllegalRecursionInTypeX, typeToString(t))
|
localError(info, errIllegalRecursionInTypeX, typeToString(t))
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
if t.kind == tyObject and t.sons[0] != nil:
|
if t.kind == tyObject and t.sons[0] != nil:
|
||||||
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
|
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
|
||||||
|
|
@ -79,7 +80,7 @@ type
|
||||||
|
|
||||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
|
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
|
||||||
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
|
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
|
||||||
proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode
|
proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode): PNode
|
||||||
|
|
||||||
template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
|
template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
|
||||||
when false:
|
when false:
|
||||||
|
|
@ -95,8 +96,11 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
|
||||||
checkMetaInvariants(cl, result)
|
checkMetaInvariants(cl, result)
|
||||||
|
|
||||||
proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
|
proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
|
||||||
|
let t = replaceTypeVarsT(cl, n.typ)
|
||||||
|
if t != nil and t.kind == tyStatic and t.n != nil:
|
||||||
|
return t.n
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
result.typ = replaceTypeVarsT(cl, n.typ)
|
result.typ = t
|
||||||
if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym)
|
if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym)
|
||||||
let isCall = result.kind in nkCallKinds
|
let isCall = result.kind in nkCallKinds
|
||||||
for i in 0 .. <n.safeLen:
|
for i in 0 .. <n.safeLen:
|
||||||
|
|
@ -153,6 +157,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
|
||||||
discard
|
discard
|
||||||
of nkSym:
|
of nkSym:
|
||||||
result.sym = replaceTypeVarsS(cl, n.sym)
|
result.sym = replaceTypeVarsS(cl, n.sym)
|
||||||
|
if result.sym.typ.kind == tyEmpty:
|
||||||
|
# don't add the 'void' field
|
||||||
|
result = newNode(nkRecList, n.info)
|
||||||
of nkRecWhen:
|
of nkRecWhen:
|
||||||
var branch: PNode = nil # the branch to take
|
var branch: PNode = nil # the branch to take
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
|
|
@ -177,7 +184,8 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
|
||||||
of nkStaticExpr:
|
of nkStaticExpr:
|
||||||
var n = prepareNode(cl, n)
|
var n = prepareNode(cl, n)
|
||||||
n = reResolveCallsWithTypedescParams(cl, n)
|
n = reResolveCallsWithTypedescParams(cl, n)
|
||||||
result = cl.c.semExpr(cl.c, n)
|
result = if cl.allowMetaTypes: n
|
||||||
|
else: cl.c.semExpr(cl.c, n)
|
||||||
else:
|
else:
|
||||||
var length = sonsLen(n)
|
var length = sonsLen(n)
|
||||||
if length > 0:
|
if length > 0:
|
||||||
|
|
@ -192,8 +200,8 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
|
||||||
result = copySym(s, false)
|
result = copySym(s, false)
|
||||||
incl(result.flags, sfFromGeneric)
|
incl(result.flags, sfFromGeneric)
|
||||||
idTablePut(cl.symMap, s, result)
|
idTablePut(cl.symMap, s, result)
|
||||||
result.typ = replaceTypeVarsT(cl, s.typ)
|
|
||||||
result.owner = s.owner
|
result.owner = s.owner
|
||||||
|
result.typ = replaceTypeVarsT(cl, s.typ)
|
||||||
result.ast = replaceTypeVarsN(cl, s.ast)
|
result.ast = replaceTypeVarsN(cl, s.ast)
|
||||||
|
|
||||||
proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
|
proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
|
||||||
|
|
@ -205,7 +213,7 @@ proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
|
||||||
elif result.kind == tyGenericParam and not cl.allowMetaTypes:
|
elif result.kind == tyGenericParam and not cl.allowMetaTypes:
|
||||||
internalError(cl.info, "substitution with generic parameter")
|
internalError(cl.info, "substitution with generic parameter")
|
||||||
|
|
||||||
proc instCopyType(cl: var TReplTypeVars, t: PType): PType =
|
proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# XXX: relying on allowMetaTypes is a kludge
|
# XXX: relying on allowMetaTypes is a kludge
|
||||||
result = copyType(t, t.owner, cl.allowMetaTypes)
|
result = copyType(t, t.owner, cl.allowMetaTypes)
|
||||||
result.flags.incl tfFromGeneric
|
result.flags.incl tfFromGeneric
|
||||||
|
|
@ -216,7 +224,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# is difficult to handle:
|
# is difficult to handle:
|
||||||
var body = t.sons[0]
|
var body = t.sons[0]
|
||||||
if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
|
if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
|
||||||
var header: PType = nil
|
var header: PType = t
|
||||||
# search for some instantiation here:
|
# search for some instantiation here:
|
||||||
if cl.allowMetaTypes:
|
if cl.allowMetaTypes:
|
||||||
result = PType(idTableGet(cl.localCache, t))
|
result = PType(idTableGet(cl.localCache, t))
|
||||||
|
|
@ -228,11 +236,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
if x.kind == tyGenericParam:
|
if x.kind == tyGenericParam:
|
||||||
x = lookupTypeVar(cl, x)
|
x = lookupTypeVar(cl, x)
|
||||||
if x != nil:
|
if x != nil:
|
||||||
if header == nil: header = instCopyType(cl, t)
|
if header == t: header = instCopyType(cl, t)
|
||||||
header.sons[i] = x
|
header.sons[i] = x
|
||||||
propagateToOwner(header, x)
|
propagateToOwner(header, x)
|
||||||
|
else:
|
||||||
|
propagateToOwner(header, x)
|
||||||
|
|
||||||
if header != nil:
|
if header != t:
|
||||||
# search again after first pass:
|
# search again after first pass:
|
||||||
result = searchInstTypes(header)
|
result = searchInstTypes(header)
|
||||||
if result != nil: return
|
if result != nil: return
|
||||||
|
|
@ -240,6 +250,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
header = instCopyType(cl, t)
|
header = instCopyType(cl, t)
|
||||||
|
|
||||||
result = newType(tyGenericInst, t.sons[0].owner)
|
result = newType(tyGenericInst, t.sons[0].owner)
|
||||||
|
result.flags = header.flags
|
||||||
# be careful not to propagate unnecessary flags here (don't use rawAddSon)
|
# be careful not to propagate unnecessary flags here (don't use rawAddSon)
|
||||||
result.sons = @[header.sons[0]]
|
result.sons = @[header.sons[0]]
|
||||||
# ugh need another pass for deeply recursive generic types (e.g. PActor)
|
# ugh need another pass for deeply recursive generic types (e.g. PActor)
|
||||||
|
|
@ -273,7 +284,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
rawAddSon(result, newbody)
|
rawAddSon(result, newbody)
|
||||||
checkPartialConstructedType(cl.info, newbody)
|
checkPartialConstructedType(cl.info, newbody)
|
||||||
|
|
||||||
proc eraseVoidParams(t: PType) =
|
proc eraseVoidParams*(t: PType) =
|
||||||
if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
|
if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
|
||||||
t.sons[0] = nil
|
t.sons[0] = nil
|
||||||
|
|
||||||
|
|
@ -290,7 +301,7 @@ proc eraseVoidParams(t: PType) =
|
||||||
setLen t.n.sons, pos
|
setLen t.n.sons, pos
|
||||||
return
|
return
|
||||||
|
|
||||||
proc skipIntLiteralParams(t: PType) =
|
proc skipIntLiteralParams*(t: PType) =
|
||||||
for i in 0 .. <t.sonsLen:
|
for i in 0 .. <t.sonsLen:
|
||||||
let p = t.sons[i]
|
let p = t.sons[i]
|
||||||
if p == nil: continue
|
if p == nil: continue
|
||||||
|
|
@ -299,6 +310,11 @@ proc skipIntLiteralParams(t: PType) =
|
||||||
t.sons[i] = skipped
|
t.sons[i] = skipped
|
||||||
if i > 0: t.n.sons[i].sym.typ = skipped
|
if i > 0: t.n.sons[i].sym.typ = skipped
|
||||||
|
|
||||||
|
# when the typeof operator is used on a static input
|
||||||
|
# param, the results gets infected with static as well:
|
||||||
|
if t.sons[0] != nil and t.sons[0].kind == tyStatic:
|
||||||
|
t.sons[0] = t.sons[0].base
|
||||||
|
|
||||||
proc propagateFieldFlags(t: PType, n: PNode) =
|
proc propagateFieldFlags(t: PType, n: PNode) =
|
||||||
# This is meant for objects and tuples
|
# This is meant for objects and tuples
|
||||||
# The type must be fully instantiated!
|
# The type must be fully instantiated!
|
||||||
|
|
@ -307,16 +323,15 @@ proc propagateFieldFlags(t: PType, n: PNode) =
|
||||||
of nkSym:
|
of nkSym:
|
||||||
propagateToOwner(t, n.sym.typ)
|
propagateToOwner(t, n.sym.typ)
|
||||||
of nkRecList, nkRecCase, nkOfBranch, nkElse:
|
of nkRecList, nkRecCase, nkOfBranch, nkElse:
|
||||||
if n.sons != nil:
|
for son in n:
|
||||||
for son in n.sons:
|
propagateFieldFlags(t, son)
|
||||||
propagateFieldFlags(t, son)
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
result = t
|
result = t
|
||||||
if t == nil: return
|
if t == nil: return
|
||||||
|
|
||||||
if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses:
|
if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses:
|
||||||
let lookup = PType(idTableGet(cl.typeMap, t))
|
let lookup = PType(idTableGet(cl.typeMap, t))
|
||||||
if lookup != nil: return lookup
|
if lookup != nil: return lookup
|
||||||
|
|
||||||
|
|
@ -329,6 +344,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
result = replaceTypeVarsT(cl, lastSon(t))
|
result = replaceTypeVarsT(cl, lastSon(t))
|
||||||
|
|
||||||
of tyFromExpr:
|
of tyFromExpr:
|
||||||
|
if cl.allowMetaTypes: return
|
||||||
var n = prepareNode(cl, t.n)
|
var n = prepareNode(cl, t.n)
|
||||||
n = cl.c.semConstExpr(cl.c, n)
|
n = cl.c.semConstExpr(cl.c, n)
|
||||||
if n.typ.kind == tyTypeDesc:
|
if n.typ.kind == tyTypeDesc:
|
||||||
|
|
@ -382,20 +398,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
|
|
||||||
result.n = replaceTypeVarsN(cl, result.n)
|
result.n = replaceTypeVarsN(cl, result.n)
|
||||||
|
|
||||||
# XXX: This is not really needed?
|
|
||||||
# if result.kind in GenericTypes:
|
|
||||||
# localError(cl.info, errCannotInstantiateX, typeToString(t, preferName))
|
|
||||||
|
|
||||||
case result.kind
|
case result.kind
|
||||||
of tyArray:
|
of tyArray:
|
||||||
let idx = result.sons[0]
|
let idx = result.sons[0]
|
||||||
if idx.kind == tyStatic:
|
internalAssert idx.kind != tyStatic
|
||||||
if idx.n == nil:
|
|
||||||
let lookup = lookupTypeVar(cl, idx)
|
|
||||||
internalAssert lookup != nil
|
|
||||||
idx.n = lookup.n
|
|
||||||
|
|
||||||
result.sons[0] = makeRangeType(cl.c, 0, idx.n.intVal - 1, idx.n.info)
|
|
||||||
|
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
propagateFieldFlags(result, result.n)
|
propagateFieldFlags(result, result.n)
|
||||||
|
|
@ -406,14 +412,22 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
|
proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
|
||||||
|
initIdTable(result.symMap)
|
||||||
|
copyIdTable(result.typeMap, pt)
|
||||||
|
initIdTable(result.localCache)
|
||||||
|
result.info = info
|
||||||
|
result.c = p
|
||||||
|
|
||||||
|
proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode): PNode =
|
||||||
|
var cl = initTypeVars(p, pt, n.info)
|
||||||
|
pushInfoContext(n.info)
|
||||||
|
result = replaceTypeVarsN(cl, n)
|
||||||
|
popInfoContext()
|
||||||
|
|
||||||
proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
|
proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
|
||||||
t: PType): PType =
|
t: PType): PType =
|
||||||
var cl: TReplTypeVars
|
var cl = initTypeVars(p, pt, info)
|
||||||
initIdTable(cl.symMap)
|
|
||||||
copyIdTable(cl.typeMap, pt)
|
|
||||||
initIdTable(cl.localCache)
|
|
||||||
cl.info = info
|
|
||||||
cl.c = p
|
|
||||||
pushInfoContext(info)
|
pushInfoContext(info)
|
||||||
result = replaceTypeVarsT(cl, t)
|
result = replaceTypeVarsT(cl, t)
|
||||||
popInfoContext()
|
popInfoContext()
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
|
intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
|
||||||
magicsys, condsyms, idents, lexer, options, parampatterns, strutils
|
magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees
|
||||||
|
|
||||||
when not defined(noDocgen):
|
when not defined(noDocgen):
|
||||||
import docgen
|
import docgen
|
||||||
|
|
@ -40,6 +40,8 @@ type
|
||||||
proxyMatch*: bool # to prevent instantiations
|
proxyMatch*: bool # to prevent instantiations
|
||||||
genericConverter*: bool # true if a generic converter needs to
|
genericConverter*: bool # true if a generic converter needs to
|
||||||
# be instantiated
|
# be instantiated
|
||||||
|
coerceDistincts*: bool # this is an explicit coercion that can strip away
|
||||||
|
# a distrinct type
|
||||||
typedescMatched: bool
|
typedescMatched: bool
|
||||||
inheritancePenalty: int # to prefer closest father object type
|
inheritancePenalty: int # to prefer closest father object type
|
||||||
errors*: seq[string] # additional clarifications to be displayed to the
|
errors*: seq[string] # additional clarifications to be displayed to the
|
||||||
|
|
@ -51,6 +53,8 @@ type
|
||||||
isSubtype,
|
isSubtype,
|
||||||
isSubrange, # subrange of the wanted type; no type conversion
|
isSubrange, # subrange of the wanted type; no type conversion
|
||||||
# but apart from that counts as ``isSubtype``
|
# but apart from that counts as ``isSubtype``
|
||||||
|
isInferred, # generic proc was matched against a concrete type
|
||||||
|
isInferredConvertible, # same as above, but requiring proc CC conversion
|
||||||
isGeneric,
|
isGeneric,
|
||||||
isFromIntLit, # conversion *from* int literal; proven safe
|
isFromIntLit, # conversion *from* int literal; proven safe
|
||||||
isEqual
|
isEqual
|
||||||
|
|
@ -112,6 +116,9 @@ proc newCandidate*(ctx: PContext, callee: PSym,
|
||||||
binding: PNode, calleeScope = -1): TCandidate =
|
binding: PNode, calleeScope = -1): TCandidate =
|
||||||
initCandidate(ctx, result, callee, binding, calleeScope)
|
initCandidate(ctx, result, callee, binding, calleeScope)
|
||||||
|
|
||||||
|
proc newCandidate*(ctx: PContext, callee: PType): TCandidate =
|
||||||
|
initCandidate(ctx, result, callee)
|
||||||
|
|
||||||
proc copyCandidate(a: var TCandidate, b: TCandidate) =
|
proc copyCandidate(a: var TCandidate, b: TCandidate) =
|
||||||
a.c = b.c
|
a.c = b.c
|
||||||
a.exactMatches = b.exactMatches
|
a.exactMatches = b.exactMatches
|
||||||
|
|
@ -338,34 +345,60 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
proc allowsNil(f: PType): TTypeRelation {.inline.} =
|
proc allowsNil(f: PType): TTypeRelation {.inline.} =
|
||||||
result = if tfNotNil notin f.flags: isSubtype else: isNone
|
result = if tfNotNil notin f.flags: isSubtype else: isNone
|
||||||
|
|
||||||
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
|
||||||
proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
|
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
|
||||||
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
|
|
||||||
|
|
||||||
|
proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
|
var f = f
|
||||||
|
|
||||||
|
if a.isMetaType:
|
||||||
|
if f.isMetaType:
|
||||||
|
# We are matching a generic proc (as proc param)
|
||||||
|
# to another generic type appearing in the proc
|
||||||
|
# signature. There is a change that the target
|
||||||
|
# type is already fully-determined, so we are
|
||||||
|
# going to try resolve it
|
||||||
|
f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
|
||||||
|
if f == nil or f.isMetaType:
|
||||||
|
# no luck resolving the type, so the inference fails
|
||||||
|
return isNone
|
||||||
|
let reverseRel = typeRel(c, a, f)
|
||||||
|
if reverseRel == isGeneric:
|
||||||
|
result = isInferred
|
||||||
|
inc c.genericMatches
|
||||||
|
else:
|
||||||
|
result = typeRel(c, f, a)
|
||||||
|
|
||||||
|
if result <= isSubtype or inconsistentVarTypes(f, a):
|
||||||
|
result = isNone
|
||||||
|
|
||||||
|
if result == isEqual:
|
||||||
|
inc c.exactMatches
|
||||||
|
|
||||||
|
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
case a.kind
|
case a.kind
|
||||||
of tyProc:
|
of tyProc:
|
||||||
if sonsLen(f) != sonsLen(a): return
|
if sonsLen(f) != sonsLen(a): return
|
||||||
# Note: We have to do unification for the parameters before the
|
|
||||||
# return type!
|
|
||||||
result = isEqual # start with maximum; also correct for no
|
result = isEqual # start with maximum; also correct for no
|
||||||
# params at all
|
# params at all
|
||||||
for i in countup(1, sonsLen(f)-1):
|
|
||||||
var m = typeRel(c, f.sons[i], a.sons[i])
|
template checkParam(f, a) =
|
||||||
if m <= isSubtype or inconsistentVarTypes(f.sons[i], a.sons[i]):
|
result = minRel(result, procParamTypeRel(c, f, a))
|
||||||
return isNone
|
if result == isNone: return
|
||||||
else: result = minRel(m, result)
|
|
||||||
|
# Note: We have to do unification for the parameters before the
|
||||||
|
# return type!
|
||||||
|
for i in 1 .. <f.sonsLen:
|
||||||
|
checkParam(f.sons[i], a.sons[i])
|
||||||
|
|
||||||
if f.sons[0] != nil:
|
if f.sons[0] != nil:
|
||||||
if a.sons[0] != nil:
|
if a.sons[0] != nil:
|
||||||
var m = typeRel(c, f.sons[0], a.sons[0])
|
checkParam(f.sons[0], a.sons[0])
|
||||||
# Subtype is sufficient for return types!
|
|
||||||
if m < isSubtype or inconsistentVarTypes(f.sons[0], a.sons[0]):
|
|
||||||
return isNone
|
|
||||||
elif m == isSubtype: result = isConvertible
|
|
||||||
else: result = minRel(m, result)
|
|
||||||
else:
|
else:
|
||||||
return isNone
|
return isNone
|
||||||
elif a.sons[0] != nil:
|
elif a.sons[0] != nil:
|
||||||
return isNone
|
return isNone
|
||||||
|
|
||||||
if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
|
if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
|
||||||
return isNone
|
return isNone
|
||||||
elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}:
|
elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}:
|
||||||
|
|
@ -376,12 +409,16 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
elif f.callConv != a.callConv:
|
elif f.callConv != a.callConv:
|
||||||
# valid to pass a 'nimcall' thingie to 'closure':
|
# valid to pass a 'nimcall' thingie to 'closure':
|
||||||
if f.callConv == ccClosure and a.callConv == ccDefault:
|
if f.callConv == ccClosure and a.callConv == ccDefault:
|
||||||
result = isConvertible
|
result = if result != isInferred: isConvertible
|
||||||
|
else: isInferredConvertible
|
||||||
else:
|
else:
|
||||||
return isNone
|
return isNone
|
||||||
when useEffectSystem:
|
when useEffectSystem:
|
||||||
if not compatibleEffects(f, a): return isNone
|
if not compatibleEffects(f, a): return isNone
|
||||||
of tyNil: result = f.allowsNil
|
of tyNil:
|
||||||
|
result = f.allowsNil
|
||||||
|
of tyIter:
|
||||||
|
if tfIterator in f.flags: result = typeRel(c, f.base, a.base)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
|
proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
|
||||||
|
|
@ -402,18 +439,8 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
|
||||||
|
|
||||||
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
ff, a: PType): TTypeRelation =
|
ff, a: PType): TTypeRelation =
|
||||||
#if f.n == nil:
|
|
||||||
# let r = typeRel(m, f, a)
|
|
||||||
# return if r == isGeneric: arg else: nil
|
|
||||||
|
|
||||||
var body = ff.skipTypes({tyUserTypeClassInst})
|
var body = ff.skipTypes({tyUserTypeClassInst})
|
||||||
|
|
||||||
# var prev = PType(idTableGet(m.bindings, f))
|
|
||||||
# if prev != nil:
|
|
||||||
# if sameType(prev, a): return arg
|
|
||||||
# else: return nil
|
|
||||||
|
|
||||||
# pushInfoContext(arg.info)
|
|
||||||
openScope(c)
|
openScope(c)
|
||||||
inc c.inTypeClass
|
inc c.inTypeClass
|
||||||
|
|
||||||
|
|
@ -438,7 +465,8 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
|
|
||||||
if param.kind == nkVarTy:
|
if param.kind == nkVarTy:
|
||||||
dummyName = param[0]
|
dummyName = param[0]
|
||||||
dummyType = makeVarType(c, a)
|
dummyType = if a.kind != tyVar: makeVarType(c, a)
|
||||||
|
else: a
|
||||||
else:
|
else:
|
||||||
dummyName = param
|
dummyName = param
|
||||||
dummyType = a
|
dummyType = a
|
||||||
|
|
@ -448,7 +476,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
dummyParam.typ = dummyType
|
dummyParam.typ = dummyType
|
||||||
addDecl(c, dummyParam)
|
addDecl(c, dummyParam)
|
||||||
|
|
||||||
var checkedBody = c.semTryExpr(c, copyTree(body.n[3]), bufferErrors = false)
|
var checkedBody = c.semTryExpr(c, body.n[3].copyTree, bufferErrors = false)
|
||||||
m.errors = bufferedMsgs
|
m.errors = bufferedMsgs
|
||||||
clearBufferedMsgs()
|
clearBufferedMsgs()
|
||||||
if checkedBody == nil: return isNone
|
if checkedBody == nil: return isNone
|
||||||
|
|
@ -462,7 +490,23 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
return isGeneric
|
return isGeneric
|
||||||
# put(m.bindings, f, a)
|
|
||||||
|
proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool =
|
||||||
|
if rules.kind == nkWith:
|
||||||
|
for r in rules:
|
||||||
|
if r.considerAcc == callIdent: return true
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
for r in rules:
|
||||||
|
if r.considerAcc == callIdent: return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc maybeSkipDistinct(t: PType, callee: PSym): PType =
|
||||||
|
if t != nil and t.kind == tyDistinct and t.n != nil and
|
||||||
|
shouldSkipDistinct(t.n, callee.name):
|
||||||
|
result = t.base
|
||||||
|
else:
|
||||||
|
result = t
|
||||||
|
|
||||||
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
# typeRel can be used to establish various relationships between types:
|
# typeRel can be used to establish various relationships between types:
|
||||||
|
|
@ -491,7 +535,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
assert(aOrig != nil)
|
assert(aOrig != nil)
|
||||||
|
|
||||||
# var and static arguments match regular modifier-free types
|
# var and static arguments match regular modifier-free types
|
||||||
let a = aOrig.skipTypes({tyStatic, tyVar})
|
let a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym)
|
||||||
|
# XXX: Theoretically, maybeSkipDistinct could be called before we even
|
||||||
|
# start the param matching process. This could be done in `prepareOperand`
|
||||||
|
# for example, but unfortunately `prepareOperand` is not called in certain
|
||||||
|
# situation when nkDotExpr are rotated to nkDotCalls
|
||||||
|
|
||||||
if a.kind == tyGenericInst and
|
if a.kind == tyGenericInst and
|
||||||
skipTypes(f, {tyVar}).kind notin {
|
skipTypes(f, {tyVar}).kind notin {
|
||||||
|
|
@ -501,8 +549,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
|
|
||||||
template bindingRet(res) =
|
template bindingRet(res) =
|
||||||
when res == isGeneric:
|
when res == isGeneric:
|
||||||
let bound = aOrig.skipTypes({tyRange}).skipIntLit
|
if doBind:
|
||||||
put(c.bindings, f, bound)
|
let bound = aOrig.skipTypes({tyRange}).skipIntLit
|
||||||
|
if doBind: put(c.bindings, f, bound)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
template considerPreviousT(body: stmt) {.immediate.} =
|
template considerPreviousT(body: stmt) {.immediate.} =
|
||||||
|
|
@ -599,8 +648,24 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
else:
|
else:
|
||||||
fRange = prev
|
fRange = prev
|
||||||
result = typeRel(c, f.sons[1], a.sons[1])
|
result = typeRel(c, f.sons[1], a.sons[1])
|
||||||
if result < isGeneric: result = isNone
|
if result < isGeneric:
|
||||||
elif lengthOrd(fRange) != lengthOrd(a): result = isNone
|
result = isNone
|
||||||
|
elif tfUnresolved in fRange.flags and
|
||||||
|
rangeHasStaticIf(fRange):
|
||||||
|
# This is a range from an array instantiated with a generic
|
||||||
|
# static param. We must extract the static param here and bind
|
||||||
|
# it to the size of the currently supplied array.
|
||||||
|
var
|
||||||
|
rangeStaticT = fRange.getStaticTypeFromRange
|
||||||
|
replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
|
||||||
|
inputUpperBound = a.sons[0].n[1].intVal
|
||||||
|
# we must correct for the off-by-one discrepancy between
|
||||||
|
# ranges and static params:
|
||||||
|
replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
|
||||||
|
put(c.bindings, rangeStaticT, replacementT)
|
||||||
|
result = isGeneric
|
||||||
|
elif lengthOrd(fRange) != lengthOrd(a):
|
||||||
|
result = isNone
|
||||||
else: discard
|
else: discard
|
||||||
of tyOpenArray, tyVarargs:
|
of tyOpenArray, tyVarargs:
|
||||||
case a.kind
|
case a.kind
|
||||||
|
|
@ -662,6 +727,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
result = isSubtype
|
result = isSubtype
|
||||||
of tyDistinct:
|
of tyDistinct:
|
||||||
if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual
|
if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual
|
||||||
|
elif c.coerceDistincts: result = typeRel(c, f.base, a)
|
||||||
of tySet:
|
of tySet:
|
||||||
if a.kind == tySet:
|
if a.kind == tySet:
|
||||||
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
|
||||||
|
|
@ -824,7 +890,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
|
|
||||||
of tyUserTypeClass, tyUserTypeClassInst:
|
of tyUserTypeClass, tyUserTypeClassInst:
|
||||||
considerPreviousT:
|
considerPreviousT:
|
||||||
result = matchUserTypeClass(c.c, c, f, a)
|
result = matchUserTypeClass(c.c, c, f, aOrig)
|
||||||
if result == isGeneric:
|
if result == isGeneric:
|
||||||
put(c.bindings, f, a)
|
put(c.bindings, f, a)
|
||||||
|
|
||||||
|
|
@ -839,20 +905,22 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
of tyGenericParam:
|
of tyGenericParam:
|
||||||
var x = PType(idTableGet(c.bindings, f))
|
var x = PType(idTableGet(c.bindings, f))
|
||||||
if x == nil:
|
if x == nil:
|
||||||
if c.calleeSym != nil and c.calleeSym.kind == skType and
|
if c.callee.kind == tyGenericBody and
|
||||||
f.kind == tyGenericParam and not c.typedescMatched:
|
f.kind == tyGenericParam and not c.typedescMatched:
|
||||||
# XXX: The fact that generic types currently use tyGenericParam for
|
# XXX: The fact that generic types currently use tyGenericParam for
|
||||||
# their parameters is really a misnomer. tyGenericParam means "match
|
# their parameters is really a misnomer. tyGenericParam means "match
|
||||||
# any value" and what we need is "match any type", which can be encoded
|
# any value" and what we need is "match any type", which can be encoded
|
||||||
# by a tyTypeDesc params. Unfortunately, this requires more substantial
|
# by a tyTypeDesc params. Unfortunately, this requires more substantial
|
||||||
# changes in semtypinst and elsewhere.
|
# changes in semtypinst and elsewhere.
|
||||||
if a.kind == tyTypeDesc:
|
if tfWildcard in a.flags:
|
||||||
|
result = isGeneric
|
||||||
|
elif a.kind == tyTypeDesc:
|
||||||
if f.sonsLen == 0:
|
if f.sonsLen == 0:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
else:
|
else:
|
||||||
internalAssert a.sons != nil and a.sons.len > 0
|
internalAssert a.sons != nil and a.sons.len > 0
|
||||||
c.typedescMatched = true
|
c.typedescMatched = true
|
||||||
result = typeRel(c, f.sons[0], a.sons[0])
|
result = typeRel(c, f.base, a.base)
|
||||||
else:
|
else:
|
||||||
result = isNone
|
result = isNone
|
||||||
else:
|
else:
|
||||||
|
|
@ -862,11 +930,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
|
|
||||||
if result == isGeneric:
|
if result == isGeneric:
|
||||||
var concrete = concreteType(c, a)
|
var concrete = a
|
||||||
if concrete == nil:
|
if tfWildcard in a.flags:
|
||||||
result = isNone
|
a.sym.kind = skType
|
||||||
|
a.flags.excl tfWildcard
|
||||||
else:
|
else:
|
||||||
if doBind: put(c.bindings, f, concrete)
|
concrete = concreteType(c, a)
|
||||||
|
if concrete == nil:
|
||||||
|
return isNone
|
||||||
|
if doBind:
|
||||||
|
put(c.bindings, f, concrete)
|
||||||
elif a.kind == tyEmpty:
|
elif a.kind == tyEmpty:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
elif x.kind == tyGenericParam:
|
elif x.kind == tyGenericParam:
|
||||||
|
|
@ -884,20 +957,32 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
var prev = PType(idTableGet(c.bindings, f))
|
var prev = PType(idTableGet(c.bindings, f))
|
||||||
if prev == nil:
|
if prev == nil:
|
||||||
if a.kind == tyTypeDesc:
|
# proc foo(T: typedesc, x: T)
|
||||||
if f.sons[0].kind == tyNone:
|
# when `f` is an unresolved typedesc, `a` could be any
|
||||||
result = isGeneric
|
# type, so we should not perform this check earlier
|
||||||
else:
|
if a.kind != tyTypeDesc: return isNone
|
||||||
result = typeRel(c, f.sons[0], a.sons[0])
|
|
||||||
if result != isNone:
|
if f.base.kind == tyNone:
|
||||||
put(c.bindings, f, a)
|
result = isGeneric
|
||||||
|
else:
|
||||||
|
result = typeRel(c, f.base, a.base)
|
||||||
|
|
||||||
|
if result != isNone:
|
||||||
|
put(c.bindings, f, a)
|
||||||
|
else:
|
||||||
|
if tfUnresolved in f.flags:
|
||||||
|
result = typeRel(c, prev.base, a)
|
||||||
|
elif a.kind == tyTypeDesc:
|
||||||
|
result = typeRel(c, prev.base, a.base)
|
||||||
else:
|
else:
|
||||||
result = isNone
|
result = isNone
|
||||||
|
|
||||||
|
of tyIter:
|
||||||
|
if a.kind == tyIter or
|
||||||
|
(a.kind == tyProc and tfIterator in a.flags):
|
||||||
|
result = typeRel(c, f.base, a.base)
|
||||||
else:
|
else:
|
||||||
internalAssert prev.sonsLen == 1
|
result = isNone
|
||||||
let toMatch = if tfUnresolved in f.flags: a
|
|
||||||
else: a.sons[0]
|
|
||||||
result = typeRel(c, prev.sons[0], toMatch)
|
|
||||||
|
|
||||||
of tyStmt:
|
of tyStmt:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
|
|
@ -905,7 +990,24 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
of tyProxy:
|
of tyProxy:
|
||||||
result = isEqual
|
result = isEqual
|
||||||
|
|
||||||
else: internalError("typeRel: " & $f.kind)
|
of tyFromExpr:
|
||||||
|
# fix the expression, so it contains the already instantiated types
|
||||||
|
let instantiated = replaceTypesInBody(c.c, c.bindings, f.n)
|
||||||
|
let reevaluted = c.c.semExpr(c.c, instantiated)
|
||||||
|
case reevaluted.typ.kind
|
||||||
|
of tyTypeDesc:
|
||||||
|
result = typeRel(c, a, reevaluted.typ.base)
|
||||||
|
of tyStatic:
|
||||||
|
result = typeRel(c, a, reevaluted.typ.base)
|
||||||
|
if result != isNone and reevaluted.typ.n != nil:
|
||||||
|
if not exprStructuralEquivalent(aOrig.n, reevaluted.typ.n):
|
||||||
|
result = isNone
|
||||||
|
else:
|
||||||
|
localError(f.n.info, errTypeExpected)
|
||||||
|
result = isNone
|
||||||
|
|
||||||
|
else:
|
||||||
|
internalAssert false
|
||||||
|
|
||||||
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
|
|
@ -981,6 +1083,10 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
|
||||||
result.typ = getInstantiatedType(c, arg, m, base(f))
|
result.typ = getInstantiatedType(c, arg, m, base(f))
|
||||||
m.baseTypeMatch = true
|
m.baseTypeMatch = true
|
||||||
|
|
||||||
|
proc isInlineIterator*(t: PType): bool =
|
||||||
|
result = t.kind == tyIter or
|
||||||
|
(t.kind == tyBuiltInTypeClass and t.base.kind == tyIter)
|
||||||
|
|
||||||
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
argSemantized, argOrig: PNode): PNode =
|
argSemantized, argOrig: PNode): PNode =
|
||||||
var
|
var
|
||||||
|
|
@ -993,19 +1099,62 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
# XXX: When implicit statics are the default
|
# XXX: When implicit statics are the default
|
||||||
# this will be done earlier - we just have to
|
# this will be done earlier - we just have to
|
||||||
# make sure that static types enter here
|
# make sure that static types enter here
|
||||||
var evaluated = c.semTryConstExpr(c, arg)
|
|
||||||
if evaluated != nil:
|
# XXX: weaken tyGenericParam and call it tyGenericPlaceholder
|
||||||
arg.typ = newTypeS(tyStatic, c)
|
# and finally start using tyTypedesc for generic types properly.
|
||||||
arg.typ.sons = @[evaluated.typ]
|
if argType.kind == tyGenericParam and tfWildcard in argType.flags:
|
||||||
arg.typ.n = evaluated
|
argType.assignType(f)
|
||||||
argType = arg.typ
|
# put(m.bindings, f, argType)
|
||||||
|
return argSemantized
|
||||||
|
|
||||||
|
if argType.kind == tyStatic:
|
||||||
|
if m.callee.kind == tyGenericBody:
|
||||||
|
result = newNodeI(nkType, argOrig.info)
|
||||||
|
result.typ = makeTypeFromExpr(c, arg)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
var evaluated = c.semTryConstExpr(c, arg)
|
||||||
|
if evaluated != nil:
|
||||||
|
arg.typ = newTypeS(tyStatic, c)
|
||||||
|
arg.typ.sons = @[evaluated.typ]
|
||||||
|
arg.typ.n = evaluated
|
||||||
|
argType = arg.typ
|
||||||
|
|
||||||
var
|
var
|
||||||
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc})
|
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor})
|
||||||
else: argType
|
else: argType
|
||||||
|
|
||||||
r = typeRel(m, f, a)
|
r = typeRel(m, f, a)
|
||||||
|
|
||||||
|
if r != isNone and m.calleeSym != nil and
|
||||||
|
m.calleeSym.kind in {skMacro, skTemplate}:
|
||||||
|
# XXX: duplicating this is ugly, maybe we should move this
|
||||||
|
# directly into typeRel using return-like templates
|
||||||
|
case r
|
||||||
|
of isConvertible, isIntConv: inc(m.convMatches)
|
||||||
|
of isSubtype, isSubrange: inc(m.subtypeMatches)
|
||||||
|
of isGeneric, isInferred: inc(m.genericMatches)
|
||||||
|
of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches)
|
||||||
|
of isFromIntLit: inc(m.intConvMatches, 256)
|
||||||
|
of isEqual: inc(m.exactMatches)
|
||||||
|
of isNone: discard
|
||||||
|
|
||||||
|
if f.kind == tyStmt and argOrig.kind == nkDo:
|
||||||
|
return argOrig[bodyPos]
|
||||||
|
elif f.kind == tyTypeDesc:
|
||||||
|
return arg
|
||||||
|
elif f.kind == tyStatic:
|
||||||
|
return arg.typ.n
|
||||||
|
else:
|
||||||
|
return argOrig
|
||||||
|
|
||||||
|
if r != isNone and f.isInlineIterator:
|
||||||
|
var inlined = newTypeS(tyStatic, c)
|
||||||
|
inlined.sons = @[argType]
|
||||||
|
inlined.n = argSemantized
|
||||||
|
put(m.bindings, f, inlined)
|
||||||
|
return argSemantized
|
||||||
|
|
||||||
case r
|
case r
|
||||||
of isConvertible:
|
of isConvertible:
|
||||||
inc(m.convMatches)
|
inc(m.convMatches)
|
||||||
|
|
@ -1022,24 +1171,24 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
inc(m.subtypeMatches)
|
inc(m.subtypeMatches)
|
||||||
#result = copyTree(arg)
|
#result = copyTree(arg)
|
||||||
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
||||||
|
of isInferred, isInferredConvertible:
|
||||||
|
inc(m.genericMatches)
|
||||||
|
if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
|
||||||
|
result = c.semInferredLambda(c, m.bindings, arg)
|
||||||
|
else:
|
||||||
|
let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
|
||||||
|
result = newSymNode(inferred, arg.info)
|
||||||
|
if r == isInferredConvertible:
|
||||||
|
inc(m.convMatches)
|
||||||
|
result = implicitConv(nkHiddenStdConv, f, result, m, c)
|
||||||
of isGeneric:
|
of isGeneric:
|
||||||
inc(m.genericMatches)
|
inc(m.genericMatches)
|
||||||
if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}:
|
result = copyTree(arg)
|
||||||
if f.kind == tyStmt and argOrig.kind == nkDo:
|
result.typ = getInstantiatedType(c, arg, m, f)
|
||||||
result = argOrig[bodyPos]
|
# BUG: f may not be the right key!
|
||||||
elif f.kind == tyTypeDesc:
|
if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
|
||||||
result = arg
|
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
||||||
elif f.kind == tyStatic:
|
# BUGFIX: use ``result.typ`` and not `f` here
|
||||||
result = arg.typ.n
|
|
||||||
else:
|
|
||||||
result = argOrig
|
|
||||||
else:
|
|
||||||
result = copyTree(arg)
|
|
||||||
result.typ = getInstantiatedType(c, arg, m, f)
|
|
||||||
# BUG: f may not be the right key!
|
|
||||||
if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
|
|
||||||
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
|
||||||
# BUGFIX: use ``result.typ`` and not `f` here
|
|
||||||
of isFromIntLit:
|
of isFromIntLit:
|
||||||
# too lazy to introduce another ``*matches`` field, so we conflate
|
# too lazy to introduce another ``*matches`` field, so we conflate
|
||||||
# ``isIntConv`` and ``isIntLit`` here:
|
# ``isIntConv`` and ``isIntLit`` here:
|
||||||
|
|
@ -1082,6 +1231,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||||
# incorrect to simply use the first fitting match. However, to implement
|
# incorrect to simply use the first fitting match. However, to implement
|
||||||
# this correctly is inefficient. We have to copy `m` here to be able to
|
# this correctly is inefficient. We have to copy `m` here to be able to
|
||||||
# roll back the side effects of the unification algorithm.
|
# roll back the side effects of the unification algorithm.
|
||||||
|
|
||||||
let c = m.c
|
let c = m.c
|
||||||
var x, y, z: TCandidate
|
var x, y, z: TCandidate
|
||||||
initCandidate(c, x, m.callee)
|
initCandidate(c, x, m.callee)
|
||||||
|
|
@ -1092,7 +1242,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||||
z.calleeSym = m.calleeSym
|
z.calleeSym = m.calleeSym
|
||||||
var best = -1
|
var best = -1
|
||||||
for i in countup(0, sonsLen(arg) - 1):
|
for i in countup(0, sonsLen(arg) - 1):
|
||||||
if arg.sons[i].sym.kind in {skProc, skIterator, skMethod, skConverter}:
|
if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators:
|
||||||
copyCandidate(z, m)
|
copyCandidate(z, m)
|
||||||
var r = typeRel(z, f, arg.sons[i].typ)
|
var r = typeRel(z, f, arg.sons[i].typ)
|
||||||
if r != isNone:
|
if r != isNone:
|
||||||
|
|
@ -1132,7 +1282,9 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
|
||||||
# a.typ == nil is valid
|
# a.typ == nil is valid
|
||||||
result = a
|
result = a
|
||||||
elif a.typ.isNil:
|
elif a.typ.isNil:
|
||||||
result = c.semOperand(c, a, {efDetermineType})
|
let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator}
|
||||||
|
else: {efDetermineType}
|
||||||
|
result = c.semOperand(c, a, flags)
|
||||||
else:
|
else:
|
||||||
result = a
|
result = a
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,13 @@ type
|
||||||
TFilterKind* = enum
|
TFilterKind* = enum
|
||||||
filtNone, filtTemplate, filtReplace, filtStrip
|
filtNone, filtTemplate, filtReplace, filtStrip
|
||||||
TParserKind* = enum
|
TParserKind* = enum
|
||||||
skinStandard, skinBraces, skinEndX
|
skinStandard, skinStrongSpaces, skinBraces, skinEndX
|
||||||
|
|
||||||
const
|
const
|
||||||
parserNames*: array[TParserKind, string] = ["standard", "braces", "endx"]
|
parserNames*: array[TParserKind, string] = ["standard", "strongspaces",
|
||||||
|
"braces", "endx"]
|
||||||
filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
|
filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
|
||||||
"strip"]
|
"strip"]
|
||||||
|
|
||||||
type
|
type
|
||||||
TParsers*{.final.} = object
|
TParsers*{.final.} = object
|
||||||
|
|
@ -54,7 +55,7 @@ proc parseFile(fileIdx: int32): PNode =
|
||||||
|
|
||||||
proc parseAll(p: var TParsers): PNode =
|
proc parseAll(p: var TParsers): PNode =
|
||||||
case p.skin
|
case p.skin
|
||||||
of skinStandard:
|
of skinStandard, skinStrongSpaces:
|
||||||
result = parser.parseAll(p.parser)
|
result = parser.parseAll(p.parser)
|
||||||
of skinBraces:
|
of skinBraces:
|
||||||
result = pbraces.parseAll(p.parser)
|
result = pbraces.parseAll(p.parser)
|
||||||
|
|
@ -65,7 +66,7 @@ proc parseAll(p: var TParsers): PNode =
|
||||||
|
|
||||||
proc parseTopLevelStmt(p: var TParsers): PNode =
|
proc parseTopLevelStmt(p: var TParsers): PNode =
|
||||||
case p.skin
|
case p.skin
|
||||||
of skinStandard:
|
of skinStandard, skinStrongSpaces:
|
||||||
result = parser.parseTopLevelStmt(p.parser)
|
result = parser.parseTopLevelStmt(p.parser)
|
||||||
of skinBraces:
|
of skinBraces:
|
||||||
result = pbraces.parseTopLevelStmt(p.parser)
|
result = pbraces.parseTopLevelStmt(p.parser)
|
||||||
|
|
@ -170,7 +171,9 @@ proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
|
||||||
else: s = inputstream
|
else: s = inputstream
|
||||||
case p.skin
|
case p.skin
|
||||||
of skinStandard, skinBraces, skinEndX:
|
of skinStandard, skinBraces, skinEndX:
|
||||||
parser.openParser(p.parser, fileIdx, s)
|
parser.openParser(p.parser, fileIdx, s, false)
|
||||||
|
of skinStrongSpaces:
|
||||||
|
parser.openParser(p.parser, fileIdx, s, true)
|
||||||
|
|
||||||
proc closeParsers(p: var TParsers) =
|
proc closeParsers(p: var TParsers) =
|
||||||
parser.closeParser(p.parser)
|
parser.closeParser(p.parser)
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,7 @@
|
||||||
import
|
import
|
||||||
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
|
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
|
||||||
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
|
idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
|
||||||
lambdalifting, sempass2
|
lambdalifting, sempass2, lowerings
|
||||||
|
|
||||||
const
|
|
||||||
genPrefix* = ":tmp" # prefix for generated names
|
|
||||||
|
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
|
|
@ -113,7 +110,7 @@ proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
|
||||||
result[1] = ri
|
result[1] = ri
|
||||||
|
|
||||||
proc transformSymAux(c: PTransf, n: PNode): PNode =
|
proc transformSymAux(c: PTransf, n: PNode): PNode =
|
||||||
#if n.sym.kind == skIterator and n.sym.typ.callConv == ccClosure:
|
#if n.sym.kind == skClosureIterator:
|
||||||
# return liftIterSym(n)
|
# return liftIterSym(n)
|
||||||
var b: PNode
|
var b: PNode
|
||||||
var tc = c.transCon
|
var tc = c.transCon
|
||||||
|
|
@ -241,13 +238,6 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
|
||||||
else:
|
else:
|
||||||
result = transform(c, n)
|
result = transform(c, n)
|
||||||
|
|
||||||
proc newTupleAccess(tup: PNode, i: int): PNode =
|
|
||||||
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.sons[i])
|
|
||||||
addSon(result, copyTree(tup))
|
|
||||||
var lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt))
|
|
||||||
lit.intVal = i
|
|
||||||
addSon(result, lit)
|
|
||||||
|
|
||||||
proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
|
proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
|
||||||
# XXX: BUG: what if `n` is an expression with side-effects?
|
# XXX: BUG: what if `n` is an expression with side-effects?
|
||||||
for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
|
for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
|
||||||
|
|
@ -433,9 +423,9 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||||
|
|
||||||
var length = sonsLen(n)
|
var length = sonsLen(n)
|
||||||
var call = n.sons[length - 2]
|
var call = n.sons[length - 2]
|
||||||
if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
|
if call.typ.kind != tyIter and
|
||||||
call.sons[0].typ.callConv == ccClosure or
|
(call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
|
||||||
call.sons[0].sym.kind != skIterator:
|
call.sons[0].sym.kind != skIterator):
|
||||||
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
|
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
|
||||||
return lambdalifting.liftForLoop(n).PTransNode
|
return lambdalifting.liftForLoop(n).PTransNode
|
||||||
#InternalError(call.info, "transformFor")
|
#InternalError(call.info, "transformFor")
|
||||||
|
|
@ -454,12 +444,13 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||||
var newC = newTransCon(getCurrOwner(c))
|
var newC = newTransCon(getCurrOwner(c))
|
||||||
newC.forStmt = n
|
newC.forStmt = n
|
||||||
newC.forLoopBody = loopBody
|
newC.forLoopBody = loopBody
|
||||||
if iter.kind != skIterator: internalError(call.info, "transformFor")
|
internalAssert iter.kind == skIterator
|
||||||
# generate access statements for the parameters (unless they are constant)
|
# generate access statements for the parameters (unless they are constant)
|
||||||
pushTransCon(c, newC)
|
pushTransCon(c, newC)
|
||||||
for i in countup(1, sonsLen(call) - 1):
|
for i in countup(1, sonsLen(call) - 1):
|
||||||
var arg = transform(c, call.sons[i]).PNode
|
var arg = transform(c, call.sons[i]).PNode
|
||||||
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
|
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
|
||||||
|
if arg.typ.kind == tyIter: continue
|
||||||
case putArgInto(arg, formal.typ)
|
case putArgInto(arg, formal.typ)
|
||||||
of paDirectMapping:
|
of paDirectMapping:
|
||||||
idNodeTablePut(newC.mapping, formal, arg)
|
idNodeTablePut(newC.mapping, formal, arg)
|
||||||
|
|
@ -481,7 +472,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||||
dec(c.inlining)
|
dec(c.inlining)
|
||||||
popInfoContext()
|
popInfoContext()
|
||||||
popTransCon(c)
|
popTransCon(c)
|
||||||
#echo "transformed: ", renderTree(n)
|
# echo "transformed: ", result.PNode.renderTree
|
||||||
|
|
||||||
proc getMagicOp(call: PNode): TMagic =
|
proc getMagicOp(call: PNode): TMagic =
|
||||||
if call.sons[0].kind == nkSym and
|
if call.sons[0].kind == nkSym and
|
||||||
|
|
@ -741,7 +732,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
|
||||||
var c = openTransf(module, "")
|
var c = openTransf(module, "")
|
||||||
result = processTransf(c, n, prc)
|
result = processTransf(c, n, prc)
|
||||||
result = liftLambdas(prc, result)
|
result = liftLambdas(prc, result)
|
||||||
#if prc.kind == skIterator and prc.typ.callConv == ccClosure:
|
#if prc.kind == skClosureIterator:
|
||||||
# result = lambdalifting.liftIterator(prc, result)
|
# result = lambdalifting.liftIterator(prc, result)
|
||||||
incl(result.flags, nfTransf)
|
incl(result.flags, nfTransf)
|
||||||
when useEffectSystem: trackProc(prc, result)
|
when useEffectSystem: trackProc(prc, result)
|
||||||
|
|
@ -754,6 +745,7 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
|
||||||
result = processTransf(c, n, module)
|
result = processTransf(c, n, module)
|
||||||
result = liftLambdasForTopLevel(module, result)
|
result = liftLambdasForTopLevel(module, result)
|
||||||
incl(result.flags, nfTransf)
|
incl(result.flags, nfTransf)
|
||||||
|
when useEffectSystem: trackTopLevelStmt(module, result)
|
||||||
|
|
||||||
proc transformExpr*(module: PSym, n: PNode): PNode =
|
proc transformExpr*(module: PSym, n: PNode): PNode =
|
||||||
if nfTransf in n.flags:
|
if nfTransf in n.flags:
|
||||||
|
|
|
||||||
|
|
@ -431,11 +431,12 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
add(result, typeToString(t.sons[i]))
|
add(result, typeToString(t.sons[i]))
|
||||||
add(result, ']')
|
add(result, ']')
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
if t.len == 0: result = "typedesc"
|
if t.base.kind == tyNone: result = "typedesc"
|
||||||
else: result = "typedesc[" & typeToString(t.sons[0]) & "]"
|
else: result = "typedesc[" & typeToString(t.base) & "]"
|
||||||
of tyStatic:
|
of tyStatic:
|
||||||
internalAssert t.len > 0
|
internalAssert t.len > 0
|
||||||
result = "static[" & typeToString(t.sons[0]) & "]"
|
result = "static[" & typeToString(t.sons[0]) & "]"
|
||||||
|
if t.n != nil: result.add "(" & renderTree(t.n) & ")"
|
||||||
of tyUserTypeClass:
|
of tyUserTypeClass:
|
||||||
internalAssert t.sym != nil and t.sym.owner != nil
|
internalAssert t.sym != nil and t.sym.owner != nil
|
||||||
return t.sym.owner.name.s
|
return t.sym.owner.name.s
|
||||||
|
|
@ -452,7 +453,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
of tyProc: "proc"
|
of tyProc: "proc"
|
||||||
of tyObject: "object"
|
of tyObject: "object"
|
||||||
of tyTuple: "tuple"
|
of tyTuple: "tuple"
|
||||||
else: (internalAssert(false); "")
|
of tyOpenArray: "openarray"
|
||||||
|
else: typeToStr[t.base.kind]
|
||||||
of tyUserTypeClassInst:
|
of tyUserTypeClassInst:
|
||||||
let body = t.base
|
let body = t.base
|
||||||
result = body.sym.name.s & "["
|
result = body.sym.name.s & "["
|
||||||
|
|
@ -463,7 +465,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
of tyAnd:
|
of tyAnd:
|
||||||
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
|
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
|
||||||
of tyOr:
|
of tyOr:
|
||||||
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
|
result = typeToString(t.sons[0]) & " or " & typeToString(t.sons[1])
|
||||||
of tyNot:
|
of tyNot:
|
||||||
result = "not " & typeToString(t.sons[0])
|
result = "not " & typeToString(t.sons[0])
|
||||||
of tyExpr:
|
of tyExpr:
|
||||||
|
|
@ -828,6 +830,12 @@ proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
|
||||||
result = sameTypeOrNilAux(a.sons[i], b.sons[i], c)
|
result = sameTypeOrNilAux(a.sons[i], b.sons[i], c)
|
||||||
if not result: return
|
if not result: return
|
||||||
|
|
||||||
|
proc isGenericAlias*(t: PType): bool =
|
||||||
|
return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst
|
||||||
|
|
||||||
|
proc skipGenericAlias*(t: PType): PType =
|
||||||
|
return if t.isGenericAlias: t.lastSon else: t
|
||||||
|
|
||||||
proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||||
template cycleCheck() =
|
template cycleCheck() =
|
||||||
# believe it or not, the direct check for ``containsOrIncl(c, a, b)``
|
# believe it or not, the direct check for ``containsOrIncl(c, a, b)``
|
||||||
|
|
@ -858,6 +866,19 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||||
of dcEqOrDistinctOf:
|
of dcEqOrDistinctOf:
|
||||||
while a.kind == tyDistinct: a = a.sons[0]
|
while a.kind == tyDistinct: a = a.sons[0]
|
||||||
if a.kind != b.kind: return false
|
if a.kind != b.kind: return false
|
||||||
|
|
||||||
|
if x.kind == tyGenericInst:
|
||||||
|
let
|
||||||
|
lhs = x.skipGenericAlias
|
||||||
|
rhs = y.skipGenericAlias
|
||||||
|
if rhs.kind != tyGenericInst or lhs.base != rhs.base:
|
||||||
|
return false
|
||||||
|
for i in 1 .. lhs.len - 2:
|
||||||
|
let ff = rhs.sons[i]
|
||||||
|
let aa = lhs.sons[i]
|
||||||
|
if not sameTypeAux(ff, aa, c): return false
|
||||||
|
return true
|
||||||
|
|
||||||
case a.kind
|
case a.kind
|
||||||
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
|
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
|
||||||
tyInt..tyBigNum, tyStmt, tyExpr:
|
tyInt..tyBigNum, tyStmt, tyExpr:
|
||||||
|
|
@ -882,8 +903,6 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
cycleCheck()
|
cycleCheck()
|
||||||
result = sameTuple(a, b, c) and sameFlags(a, b)
|
result = sameTuple(a, b, c) and sameFlags(a, b)
|
||||||
of tyGenericInst:
|
|
||||||
result = sameTypeAux(lastSon(a), lastSon(b), c)
|
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
if c.cmp == dcEqIgnoreDistinct: result = false
|
if c.cmp == dcEqIgnoreDistinct: result = false
|
||||||
elif ExactTypeDescValues in c.flags:
|
elif ExactTypeDescValues in c.flags:
|
||||||
|
|
@ -910,6 +929,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||||
result = sameTypeOrNilAux(a.sons[0], b.sons[0], c) and
|
result = sameTypeOrNilAux(a.sons[0], b.sons[0], c) and
|
||||||
sameValue(a.n.sons[0], b.n.sons[0]) and
|
sameValue(a.n.sons[0], b.n.sons[0]) and
|
||||||
sameValue(a.n.sons[1], b.n.sons[1])
|
sameValue(a.n.sons[1], b.n.sons[1])
|
||||||
|
of tyGenericInst: discard
|
||||||
of tyNone: result = false
|
of tyNone: result = false
|
||||||
|
|
||||||
proc sameBackendType*(x, y: PType): bool =
|
proc sameBackendType*(x, y: PType): bool =
|
||||||
|
|
@ -1003,12 +1023,6 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
|
||||||
a = a.sons[i]
|
a = a.sons[i]
|
||||||
result = a.kind == last
|
result = a.kind == last
|
||||||
|
|
||||||
proc isGenericAlias*(t: PType): bool =
|
|
||||||
return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst
|
|
||||||
|
|
||||||
proc skipGenericAlias*(t: PType): PType =
|
|
||||||
return if t.isGenericAlias: t.lastSon else: t
|
|
||||||
|
|
||||||
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
|
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
|
||||||
flags: TTypeAllowedFlags = {}): bool =
|
flags: TTypeAllowedFlags = {}): bool =
|
||||||
assert(kind in {skVar, skLet, skConst, skParam, skResult})
|
assert(kind in {skVar, skLet, skConst, skParam, skResult})
|
||||||
|
|
@ -1042,7 +1056,8 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
|
||||||
of tyEmpty:
|
of tyEmpty:
|
||||||
result = taField in flags
|
result = taField in flags
|
||||||
of tyTypeClasses:
|
of tyTypeClasses:
|
||||||
result = true
|
result = tfGenericTypeParam in t.flags or
|
||||||
|
taField notin flags
|
||||||
of tyGenericBody, tyGenericParam, tyGenericInvokation,
|
of tyGenericBody, tyGenericParam, tyGenericInvokation,
|
||||||
tyNone, tyForward, tyFromExpr, tyFieldAccessor:
|
tyNone, tyForward, tyFromExpr, tyFieldAccessor:
|
||||||
result = false
|
result = false
|
||||||
|
|
@ -1231,8 +1246,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
|
||||||
of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter:
|
of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter:
|
||||||
result = computeSizeAux(lastSon(typ), a)
|
result = computeSizeAux(lastSon(typ), a)
|
||||||
of tyTypeDesc:
|
of tyTypeDesc:
|
||||||
result = if typ.len == 1: computeSizeAux(typ.sons[0], a)
|
result = computeSizeAux(typ.base, a)
|
||||||
else: szUnknownSize
|
|
||||||
of tyForward: return szIllegalRecursion
|
of tyForward: return szIllegalRecursion
|
||||||
else:
|
else:
|
||||||
#internalError("computeSizeAux()")
|
#internalError("computeSizeAux()")
|
||||||
|
|
@ -1246,7 +1260,7 @@ proc computeSize(typ: PType): BiggestInt =
|
||||||
|
|
||||||
proc getReturnType*(s: PSym): PType =
|
proc getReturnType*(s: PSym): PType =
|
||||||
# Obtains the return type of a iterator/proc/macro/template
|
# Obtains the return type of a iterator/proc/macro/template
|
||||||
assert s.kind in {skProc, skTemplate, skMacro, skIterator}
|
assert s.kind in skProcKinds
|
||||||
result = s.typ.sons[0]
|
result = s.typ.sons[0]
|
||||||
|
|
||||||
proc getSize(typ: PType): BiggestInt =
|
proc getSize(typ: PType): BiggestInt =
|
||||||
|
|
@ -1254,15 +1268,18 @@ proc getSize(typ: PType): BiggestInt =
|
||||||
if result < 0: internalError("getSize: " & $typ.kind)
|
if result < 0: internalError("getSize: " & $typ.kind)
|
||||||
|
|
||||||
proc containsGenericTypeIter(t: PType, closure: PObject): bool =
|
proc containsGenericTypeIter(t: PType, closure: PObject): bool =
|
||||||
if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}:
|
if t.kind == tyStatic:
|
||||||
return true
|
return t.n == nil
|
||||||
|
|
||||||
if t.kind == tyTypeDesc:
|
if t.kind == tyTypeDesc:
|
||||||
if t.sons[0].kind == tyNone: return true
|
if t.base.kind == tyNone: return true
|
||||||
if containsGenericTypeIter(t.base, closure): return true
|
if containsGenericTypeIter(t.base, closure): return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
return t.kind == tyStatic and t.n == nil
|
if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}:
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
proc containsGenericType*(t: PType): bool =
|
proc containsGenericType*(t: PType): bool =
|
||||||
result = iterOverType(t, containsGenericTypeIter, nil)
|
result = iterOverType(t, containsGenericTypeIter, nil)
|
||||||
|
|
|
||||||
1062
compiler/vm.nim
1062
compiler/vm.nim
File diff suppressed because it is too large
Load diff
|
|
@ -8,7 +8,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module contains the type definitions for the new evaluation engine.
|
## This module contains the type definitions for the new evaluation engine.
|
||||||
## An instruction is 1-2 int32s in memory, it is a register based VM.
|
## An instruction is 1-3 int32s in memory, it is a register based VM.
|
||||||
|
|
||||||
import ast, passes, msgs, intsets
|
import ast, passes, msgs, intsets
|
||||||
|
|
||||||
|
|
@ -16,6 +16,9 @@ const
|
||||||
byteExcess* = 128 # we use excess-K for immediates
|
byteExcess* = 128 # we use excess-K for immediates
|
||||||
wordExcess* = 32768
|
wordExcess* = 32768
|
||||||
|
|
||||||
|
MaxLoopIterations* = 500_000 # max iterations of all loops
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TRegister* = range[0..255]
|
TRegister* = range[0..255]
|
||||||
TDest* = range[-1 .. 255]
|
TDest* = range[-1 .. 255]
|
||||||
|
|
@ -32,17 +35,17 @@ type
|
||||||
opcAsgnFloat,
|
opcAsgnFloat,
|
||||||
opcAsgnRef,
|
opcAsgnRef,
|
||||||
opcAsgnComplex,
|
opcAsgnComplex,
|
||||||
|
opcRegToNode,
|
||||||
|
opcNodeToReg,
|
||||||
|
|
||||||
opcLdArr, # a = b[c]
|
opcLdArr, # a = b[c]
|
||||||
opcLdArrRef,
|
|
||||||
opcWrArr, # a[b] = c
|
opcWrArr, # a[b] = c
|
||||||
opcWrArrRef,
|
|
||||||
opcLdObj, # a = b.c
|
opcLdObj, # a = b.c
|
||||||
opcLdObjRef,
|
|
||||||
opcWrObj, # a.b = c
|
opcWrObj, # a.b = c
|
||||||
opcWrObjRef,
|
opcAddrReg,
|
||||||
opcAddr,
|
opcAddrNode,
|
||||||
opcDeref,
|
opcLdDeref,
|
||||||
|
opcWrDeref,
|
||||||
opcWrStrIdx,
|
opcWrStrIdx,
|
||||||
opcLdStrIdx, # a = b[c]
|
opcLdStrIdx, # a = b[c]
|
||||||
|
|
||||||
|
|
@ -64,6 +67,7 @@ type
|
||||||
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
|
||||||
opcSwap, opcIsNil, opcOf, opcIs,
|
opcSwap, opcIsNil, opcOf, opcIs,
|
||||||
opcSubStr, opcConv, opcCast, opcQuit, opcReset,
|
opcSubStr, opcConv, opcCast, opcQuit, opcReset,
|
||||||
|
opcNarrowS, opcNarrowU,
|
||||||
|
|
||||||
opcAddStrCh,
|
opcAddStrCh,
|
||||||
opcAddStrStr,
|
opcAddStrStr,
|
||||||
|
|
@ -109,6 +113,7 @@ type
|
||||||
opcTJmp, # jump Bx if A != 0
|
opcTJmp, # jump Bx if A != 0
|
||||||
opcFJmp, # jump Bx if A == 0
|
opcFJmp, # jump Bx if A == 0
|
||||||
opcJmp, # jump Bx
|
opcJmp, # jump Bx
|
||||||
|
opcJmpBack, # jump Bx; resulting from a while loop
|
||||||
opcBranch, # branch for 'case'
|
opcBranch, # branch for 'case'
|
||||||
opcTry,
|
opcTry,
|
||||||
opcExcept,
|
opcExcept,
|
||||||
|
|
@ -117,15 +122,14 @@ type
|
||||||
opcNew,
|
opcNew,
|
||||||
opcNewSeq,
|
opcNewSeq,
|
||||||
opcLdNull, # dest = nullvalue(types[Bx])
|
opcLdNull, # dest = nullvalue(types[Bx])
|
||||||
|
opcLdNullReg,
|
||||||
opcLdConst, # dest = constants[Bx]
|
opcLdConst, # dest = constants[Bx]
|
||||||
opcAsgnConst, # dest = copy(constants[Bx])
|
opcAsgnConst, # dest = copy(constants[Bx])
|
||||||
opcLdGlobal, # dest = globals[Bx]
|
opcLdGlobal, # dest = globals[Bx]
|
||||||
|
opcLdGlobalAddr, # dest = addr(globals[Bx])
|
||||||
|
|
||||||
opcLdImmInt, # dest = immediate value
|
opcLdImmInt, # dest = immediate value
|
||||||
opcNBindSym,
|
opcNBindSym,
|
||||||
opcWrGlobal,
|
|
||||||
opcWrGlobalRef,
|
|
||||||
opcGlobalAlias, # load an alias to a global into a register
|
|
||||||
opcGlobalOnce, # used to introduce an assignment to a global once
|
|
||||||
opcSetType, # dest.typ = types[Bx]
|
opcSetType, # dest.typ = types[Bx]
|
||||||
opcTypeTrait
|
opcTypeTrait
|
||||||
|
|
||||||
|
|
@ -159,14 +163,13 @@ type
|
||||||
slotTempInt, # some temporary int
|
slotTempInt, # some temporary int
|
||||||
slotTempFloat, # some temporary float
|
slotTempFloat, # some temporary float
|
||||||
slotTempStr, # some temporary string
|
slotTempStr, # some temporary string
|
||||||
slotTempComplex # some complex temporary (n.sons field is used)
|
slotTempComplex # some complex temporary (s.node field is used)
|
||||||
|
|
||||||
PProc* = ref object
|
PProc* = ref object
|
||||||
blocks*: seq[TBlock] # blocks; temp data structure
|
blocks*: seq[TBlock] # blocks; temp data structure
|
||||||
|
sym*: PSym
|
||||||
slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]]
|
slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]]
|
||||||
maxSlots*: int
|
maxSlots*: int
|
||||||
globals*: array[TRegister, int] # hack: to support passing globals byref
|
|
||||||
# we map a slot persistently to a global
|
|
||||||
|
|
||||||
PCtx* = ref TCtx
|
PCtx* = ref TCtx
|
||||||
TCtx* = object of passes.TPassContext # code gen context
|
TCtx* = object of passes.TPassContext # code gen context
|
||||||
|
|
@ -183,6 +186,8 @@ type
|
||||||
callsite*: PNode
|
callsite*: PNode
|
||||||
mode*: TEvalMode
|
mode*: TEvalMode
|
||||||
features*: TSandboxFlags
|
features*: TSandboxFlags
|
||||||
|
traceActive*: bool
|
||||||
|
loopIterations*: int
|
||||||
|
|
||||||
TPosition* = distinct int
|
TPosition* = distinct int
|
||||||
|
|
||||||
|
|
@ -191,7 +196,7 @@ type
|
||||||
proc newCtx*(module: PSym): PCtx =
|
proc newCtx*(module: PSym): PCtx =
|
||||||
PCtx(code: @[], debug: @[],
|
PCtx(code: @[], debug: @[],
|
||||||
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
|
||||||
prc: PProc(blocks: @[]), module: module)
|
prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations)
|
||||||
|
|
||||||
proc refresh*(c: PCtx, module: PSym) =
|
proc refresh*(c: PCtx, module: PSym) =
|
||||||
c.module = module
|
c.module = module
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef,
|
unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef,
|
||||||
trees, intsets, rodread, magicsys, options
|
trees, intsets, rodread, magicsys, options, lowerings
|
||||||
|
|
||||||
from os import splitFile
|
from os import splitFile
|
||||||
|
|
||||||
|
|
@ -41,7 +41,14 @@ proc codeListing(c: PCtx, result: var string, start=0) =
|
||||||
let x = c.code[i]
|
let x = c.code[i]
|
||||||
|
|
||||||
let opc = opcode(x)
|
let opc = opcode(x)
|
||||||
if opc < firstABxInstr:
|
if opc in {opcConv, opcCast}:
|
||||||
|
let y = c.code[i+1]
|
||||||
|
let z = c.code[i+2]
|
||||||
|
result.addf("\t$#\tr$#, r$#, $#, $#", ($opc).substr(3), x.regA, x.regB,
|
||||||
|
c.types[y.regBx-wordExcess].typeToString,
|
||||||
|
c.types[z.regBx-wordExcess].typeToString)
|
||||||
|
inc i, 2
|
||||||
|
elif opc < firstABxInstr:
|
||||||
result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA,
|
result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA,
|
||||||
x.regB, x.regC)
|
x.regB, x.regC)
|
||||||
elif opc in relativeJumps:
|
elif opc in relativeJumps:
|
||||||
|
|
@ -92,10 +99,10 @@ proc genLabel(c: PCtx): TPosition =
|
||||||
result = TPosition(c.code.len)
|
result = TPosition(c.code.len)
|
||||||
#c.jumpTargets.incl(c.code.len)
|
#c.jumpTargets.incl(c.code.len)
|
||||||
|
|
||||||
proc jmpBack(c: PCtx, n: PNode, opc: TOpcode, p = TPosition(0)) =
|
proc jmpBack(c: PCtx, n: PNode, p = TPosition(0)) =
|
||||||
let dist = p.int - c.code.len
|
let dist = p.int - c.code.len
|
||||||
internalAssert(-0x7fff < dist and dist < 0x7fff)
|
internalAssert(-0x7fff < dist and dist < 0x7fff)
|
||||||
gABx(c, n, opc, 0, dist)
|
gABx(c, n, opcJmpBack, 0, dist)
|
||||||
|
|
||||||
proc patch(c: PCtx, p: TPosition) =
|
proc patch(c: PCtx, p: TPosition) =
|
||||||
# patch with current index
|
# patch with current index
|
||||||
|
|
@ -139,25 +146,12 @@ proc getTemp(c: PCtx; typ: PType): TRegister =
|
||||||
if not c.slots[i].inUse:
|
if not c.slots[i].inUse:
|
||||||
c.slots[i] = (inUse: true, kind: k)
|
c.slots[i] = (inUse: true, kind: k)
|
||||||
return TRegister(i)
|
return TRegister(i)
|
||||||
|
if c.maxSlots >= high(TRegister):
|
||||||
|
internalError("cannot generate code; too many registers required")
|
||||||
result = TRegister(c.maxSlots)
|
result = TRegister(c.maxSlots)
|
||||||
c.slots[c.maxSlots] = (inUse: true, kind: k)
|
c.slots[c.maxSlots] = (inUse: true, kind: k)
|
||||||
inc c.maxSlots
|
inc c.maxSlots
|
||||||
|
|
||||||
proc getGlobalSlot(c: PCtx; n: PNode; s: PSym): TRegister =
|
|
||||||
let p = c.prc
|
|
||||||
for i in 0 .. p.maxSlots-1:
|
|
||||||
if p.globals[i] == s.id: return TRegister(i)
|
|
||||||
|
|
||||||
result = TRegister(p.maxSlots)
|
|
||||||
p.slots[p.maxSlots] = (inUse: true, kind: slotFixedVar)
|
|
||||||
p.globals[p.maxSlots] = s.id
|
|
||||||
inc p.maxSlots
|
|
||||||
# XXX this is still not correct! We need to load the global in a proc init
|
|
||||||
# section, otherwise control flow could lead to a usage before it's been
|
|
||||||
# loaded.
|
|
||||||
c.gABx(n, opcGlobalAlias, result, s.position)
|
|
||||||
# XXX add some internal asserts here
|
|
||||||
|
|
||||||
proc freeTemp(c: PCtx; r: TRegister) =
|
proc freeTemp(c: PCtx; r: TRegister) =
|
||||||
let c = c.prc
|
let c = c.prc
|
||||||
if c.slots[r].kind >= slotSomeTemp: c.slots[r].inUse = false
|
if c.slots[r].kind >= slotSomeTemp: c.slots[r].inUse = false
|
||||||
|
|
@ -242,20 +236,20 @@ proc genWhile(c: PCtx; n: PNode) =
|
||||||
withBlock(nil):
|
withBlock(nil):
|
||||||
if isTrue(n.sons[0]):
|
if isTrue(n.sons[0]):
|
||||||
c.gen(n.sons[1])
|
c.gen(n.sons[1])
|
||||||
c.jmpBack(n, opcJmp, L1)
|
c.jmpBack(n, L1)
|
||||||
elif isNotOpr(n.sons[0]):
|
elif isNotOpr(n.sons[0]):
|
||||||
var tmp = c.genx(n.sons[0].sons[1])
|
var tmp = c.genx(n.sons[0].sons[1])
|
||||||
let L2 = c.xjmp(n, opcTJmp, tmp)
|
let L2 = c.xjmp(n, opcTJmp, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
c.gen(n.sons[1])
|
c.gen(n.sons[1])
|
||||||
c.jmpBack(n, opcJmp, L1)
|
c.jmpBack(n, L1)
|
||||||
c.patch(L2)
|
c.patch(L2)
|
||||||
else:
|
else:
|
||||||
var tmp = c.genx(n.sons[0])
|
var tmp = c.genx(n.sons[0])
|
||||||
let L2 = c.xjmp(n, opcFJmp, tmp)
|
let L2 = c.xjmp(n, opcFJmp, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
c.gen(n.sons[1])
|
c.gen(n.sons[1])
|
||||||
c.jmpBack(n, opcJmp, L1)
|
c.jmpBack(n, L1)
|
||||||
c.patch(L2)
|
c.patch(L2)
|
||||||
|
|
||||||
proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
|
proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
|
|
@ -321,9 +315,20 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) =
|
||||||
c.gen(n.sons[2], dest)
|
c.gen(n.sons[2], dest)
|
||||||
c.patch(L1)
|
c.patch(L1)
|
||||||
|
|
||||||
|
proc canonValue*(n: PNode): PNode =
|
||||||
|
if n.kind == nkExprColonExpr:
|
||||||
|
result = n.sons[1]
|
||||||
|
elif n.hasSubnodeWith(nkExprColonExpr):
|
||||||
|
result = n.copyNode
|
||||||
|
newSeq(result.sons, n.len)
|
||||||
|
for i in 0.. <n.len:
|
||||||
|
result.sons[i] = canonValue(n.sons[i])
|
||||||
|
else:
|
||||||
|
result = n
|
||||||
|
|
||||||
proc rawGenLiteral(c: PCtx; n: PNode): int =
|
proc rawGenLiteral(c: PCtx; n: PNode): int =
|
||||||
result = c.constants.len
|
result = c.constants.len
|
||||||
c.constants.add n
|
c.constants.add n.canonValue
|
||||||
internalAssert result < 0x7fff
|
internalAssert result < 0x7fff
|
||||||
|
|
||||||
proc sameConstant*(a, b: PNode): bool =
|
proc sameConstant*(a, b: PNode): bool =
|
||||||
|
|
@ -461,21 +466,45 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
c.gABC(n, opcIndCallAsgn, dest, x, n.len)
|
c.gABC(n, opcIndCallAsgn, dest, x, n.len)
|
||||||
c.freeTempRange(x, n.len)
|
c.freeTempRange(x, n.len)
|
||||||
|
|
||||||
|
template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
|
||||||
|
|
||||||
proc needsAsgnPatch(n: PNode): bool =
|
proc needsAsgnPatch(n: PNode): bool =
|
||||||
n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}
|
n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr,
|
||||||
|
nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal)
|
||||||
|
|
||||||
|
proc genField(n: PNode): TRegister =
|
||||||
|
if n.kind != nkSym or n.sym.kind != skField:
|
||||||
|
internalError(n.info, "no field symbol")
|
||||||
|
let s = n.sym
|
||||||
|
if s.position > high(result):
|
||||||
|
internalError(n.info,
|
||||||
|
"too large offset! cannot generate code for: " & s.name.s)
|
||||||
|
result = s.position
|
||||||
|
|
||||||
proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
|
proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
|
||||||
case le.kind
|
case le.kind
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
let dest = c.genx(le.sons[0])
|
let dest = c.genx(le.sons[0], {gfAddrOf})
|
||||||
let idx = c.genx(le.sons[1])
|
let idx = c.genx(le.sons[1])
|
||||||
c.gABC(le, opcWrArrRef, dest, idx, value)
|
c.gABC(le, opcWrArr, dest, idx, value)
|
||||||
|
c.freeTemp(dest)
|
||||||
|
c.freeTemp(idx)
|
||||||
of nkDotExpr, nkCheckedFieldExpr:
|
of nkDotExpr, nkCheckedFieldExpr:
|
||||||
# XXX field checks here
|
# XXX field checks here
|
||||||
let left = if le.kind == nkDotExpr: le else: le.sons[0]
|
let left = if le.kind == nkDotExpr: le else: le.sons[0]
|
||||||
let dest = c.genx(left.sons[0])
|
let dest = c.genx(left.sons[0], {gfAddrOf})
|
||||||
let idx = c.genx(left.sons[1])
|
let idx = genField(left.sons[1])
|
||||||
c.gABC(left, opcWrObjRef, dest, idx, value)
|
c.gABC(left, opcWrObj, dest, idx, value)
|
||||||
|
c.freeTemp(dest)
|
||||||
|
of nkDerefExpr, nkHiddenDeref:
|
||||||
|
let dest = c.genx(le.sons[0], {gfAddrOf})
|
||||||
|
c.gABC(le, opcWrDeref, dest, value)
|
||||||
|
c.freeTemp(dest)
|
||||||
|
of nkSym:
|
||||||
|
if le.sym.isGlobal:
|
||||||
|
let dest = c.genx(le, {gfAddrOf})
|
||||||
|
c.gABC(le, opcWrDeref, dest, value)
|
||||||
|
c.freeTemp(dest)
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
@ -521,6 +550,30 @@ proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
c.freeTemp(tmp2)
|
c.freeTemp(tmp2)
|
||||||
|
|
||||||
|
proc genNarrow(c: PCtx; n: PNode; dest: TDest) =
|
||||||
|
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||||
|
# uint is uint64 in the VM, we we only need to mask the result for
|
||||||
|
# other unsigned types:
|
||||||
|
if t.kind in {tyUInt8..tyUInt32}:
|
||||||
|
c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
|
||||||
|
elif t.kind in {tyInt8..tyInt32}:
|
||||||
|
c.gABC(n, opcNarrowS, dest, TRegister(t.size*8))
|
||||||
|
|
||||||
|
proc genNarrowU(c: PCtx; n: PNode; dest: TDest) =
|
||||||
|
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||||
|
# uint is uint64 in the VM, we we only need to mask the result for
|
||||||
|
# other unsigned types:
|
||||||
|
if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}:
|
||||||
|
c.gABC(n, opcNarrowU, dest, TRegister(t.size*8))
|
||||||
|
|
||||||
|
proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||||
|
genBinaryABC(c, n, dest, opc)
|
||||||
|
genNarrow(c, n, dest)
|
||||||
|
|
||||||
|
proc genBinaryABCnarrowU(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||||
|
genBinaryABC(c, n, dest, opc)
|
||||||
|
genNarrowU(c, n, dest)
|
||||||
|
|
||||||
proc genSetType(c: PCtx; n: PNode; dest: TRegister) =
|
proc genSetType(c: PCtx; n: PNode; dest: TRegister) =
|
||||||
let t = skipTypes(n.typ, abstractInst-{tyTypeDesc})
|
let t = skipTypes(n.typ, abstractInst-{tyTypeDesc})
|
||||||
if t.kind == tySet:
|
if t.kind == tySet:
|
||||||
|
|
@ -582,13 +635,14 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
else:
|
else:
|
||||||
genBinaryABC(c, n, dest, opc)
|
genBinaryABC(c, n, dest, opc)
|
||||||
|
c.genNarrow(n, dest)
|
||||||
|
|
||||||
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
|
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
|
||||||
let tmp = c.genx(arg)
|
let tmp = c.genx(arg)
|
||||||
c.gABx(n, opcSetType, tmp, genType(c, arg.typ))
|
|
||||||
if dest < 0: dest = c.getTemp(n.typ)
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
c.gABC(n, opc, dest, tmp)
|
c.gABC(n, opc, dest, tmp)
|
||||||
c.gABx(n, opc, 0, genType(c, n.typ))
|
c.gABx(n, opc, 0, genType(c, n.typ))
|
||||||
|
c.gABx(n, opc, 0, genType(c, arg.typ))
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
proc genCard(c: PCtx; n: PNode; dest: var TDest) =
|
proc genCard(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
|
|
@ -614,10 +668,17 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
c.genAddSubInt(n, dest, opcAddInt)
|
c.genAddSubInt(n, dest, opcAddInt)
|
||||||
of mInc, mDec:
|
of mInc, mDec:
|
||||||
unused(n, dest)
|
unused(n, dest)
|
||||||
var d = c.genx(n.sons[1]).TDest
|
let opc = if m == mInc: opcAddInt else: opcSubInt
|
||||||
c.genAddSubInt(n, d, if m == mInc: opcAddInt else: opcSubInt)
|
let d = c.genx(n.sons[1])
|
||||||
|
if n.sons[2].isInt8Lit:
|
||||||
|
c.gABI(n, succ(opc), d, d, n.sons[2].intVal)
|
||||||
|
else:
|
||||||
|
let tmp = c.genx(n.sons[2])
|
||||||
|
c.gABC(n, opc, d, d, tmp)
|
||||||
|
c.freeTemp(tmp)
|
||||||
|
c.genNarrow(n.sons[1], d)
|
||||||
c.genAsgnPatch(n.sons[1], d)
|
c.genAsgnPatch(n.sons[1], d)
|
||||||
c.freeTemp(d.TRegister)
|
c.freeTemp(d)
|
||||||
of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest)
|
of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest)
|
||||||
of mNew, mNewFinalize:
|
of mNew, mNewFinalize:
|
||||||
unused(n, dest)
|
unused(n, dest)
|
||||||
|
|
@ -627,6 +688,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
c.genNewSeq(n)
|
c.genNewSeq(n)
|
||||||
of mNewString:
|
of mNewString:
|
||||||
genUnaryABC(c, n, dest, opcNewStr)
|
genUnaryABC(c, n, dest, opcNewStr)
|
||||||
|
# XXX buggy
|
||||||
of mNewStringOfCap:
|
of mNewStringOfCap:
|
||||||
# we ignore the 'cap' argument and translate it as 'newString(0)'.
|
# we ignore the 'cap' argument and translate it as 'newString(0)'.
|
||||||
# eval n.sons[1] for possible side effects:
|
# eval n.sons[1] for possible side effects:
|
||||||
|
|
@ -635,6 +697,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
if dest < 0: dest = c.getTemp(n.typ)
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
c.gABC(n, opcNewStr, dest, tmp)
|
c.gABC(n, opcNewStr, dest, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
|
# XXX buggy
|
||||||
of mLengthOpenArray, mLengthArray, mLengthSeq:
|
of mLengthOpenArray, mLengthArray, mLengthSeq:
|
||||||
genUnaryABI(c, n, dest, opcLenSeq)
|
genUnaryABI(c, n, dest, opcLenSeq)
|
||||||
of mLengthStr:
|
of mLengthStr:
|
||||||
|
|
@ -648,23 +711,23 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
c.freeTemp(d)
|
c.freeTemp(d)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
of mCard: genCard(c, n, dest)
|
of mCard: genCard(c, n, dest)
|
||||||
of mMulI, mMulI64: genBinaryABC(c, n, dest, opcMulInt)
|
of mMulI, mMulI64: genBinaryABCnarrow(c, n, dest, opcMulInt)
|
||||||
of mDivI, mDivI64: genBinaryABC(c, n, dest, opcDivInt)
|
of mDivI, mDivI64: genBinaryABCnarrow(c, n, dest, opcDivInt)
|
||||||
of mModI, mModI64: genBinaryABC(c, n, dest, opcModInt)
|
of mModI, mModI64: genBinaryABCnarrow(c, n, dest, opcModInt)
|
||||||
of mAddF64: genBinaryABC(c, n, dest, opcAddFloat)
|
of mAddF64: genBinaryABC(c, n, dest, opcAddFloat)
|
||||||
of mSubF64: genBinaryABC(c, n, dest, opcSubFloat)
|
of mSubF64: genBinaryABC(c, n, dest, opcSubFloat)
|
||||||
of mMulF64: genBinaryABC(c, n, dest, opcMulFloat)
|
of mMulF64: genBinaryABC(c, n, dest, opcMulFloat)
|
||||||
of mDivF64: genBinaryABC(c, n, dest, opcDivFloat)
|
of mDivF64: genBinaryABC(c, n, dest, opcDivFloat)
|
||||||
of mShrI, mShrI64: genBinaryABC(c, n, dest, opcShrInt)
|
of mShrI, mShrI64: genBinaryABCnarrowU(c, n, dest, opcShrInt)
|
||||||
of mShlI, mShlI64: genBinaryABC(c, n, dest, opcShlInt)
|
of mShlI, mShlI64: genBinaryABCnarrowU(c, n, dest, opcShlInt)
|
||||||
of mBitandI, mBitandI64: genBinaryABC(c, n, dest, opcBitandInt)
|
of mBitandI, mBitandI64: genBinaryABCnarrowU(c, n, dest, opcBitandInt)
|
||||||
of mBitorI, mBitorI64: genBinaryABC(c, n, dest, opcBitorInt)
|
of mBitorI, mBitorI64: genBinaryABCnarrowU(c, n, dest, opcBitorInt)
|
||||||
of mBitxorI, mBitxorI64: genBinaryABC(c, n, dest, opcBitxorInt)
|
of mBitxorI, mBitxorI64: genBinaryABCnarrowU(c, n, dest, opcBitxorInt)
|
||||||
of mAddU: genBinaryABC(c, n, dest, opcAddu)
|
of mAddU: genBinaryABCnarrowU(c, n, dest, opcAddu)
|
||||||
of mSubU: genBinaryABC(c, n, dest, opcSubu)
|
of mSubU: genBinaryABCnarrowU(c, n, dest, opcSubu)
|
||||||
of mMulU: genBinaryABC(c, n, dest, opcMulu)
|
of mMulU: genBinaryABCnarrowU(c, n, dest, opcMulu)
|
||||||
of mDivU: genBinaryABC(c, n, dest, opcDivu)
|
of mDivU: genBinaryABCnarrowU(c, n, dest, opcDivu)
|
||||||
of mModU: genBinaryABC(c, n, dest, opcModu)
|
of mModU: genBinaryABCnarrowU(c, n, dest, opcModu)
|
||||||
of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
|
of mEqI, mEqI64, mEqB, mEqEnum, mEqCh:
|
||||||
genBinaryABC(c, n, dest, opcEqInt)
|
genBinaryABC(c, n, dest, opcEqInt)
|
||||||
of mLeI, mLeI64, mLeEnum, mLeCh, mLeB:
|
of mLeI, mLeI64, mLeEnum, mLeCh, mLeB:
|
||||||
|
|
@ -678,12 +741,16 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
of mLtPtr, mLtU, mLtU64: genBinaryABC(c, n, dest, opcLtu)
|
of mLtPtr, mLtU, mLtU64: genBinaryABC(c, n, dest, opcLtu)
|
||||||
of mEqProc, mEqRef, mEqUntracedRef, mEqCString:
|
of mEqProc, mEqRef, mEqUntracedRef, mEqCString:
|
||||||
genBinaryABC(c, n, dest, opcEqRef)
|
genBinaryABC(c, n, dest, opcEqRef)
|
||||||
of mXor: genBinaryABC(c, n, dest, opcXor)
|
of mXor: genBinaryABCnarrowU(c, n, dest, opcXor)
|
||||||
of mNot: genUnaryABC(c, n, dest, opcNot)
|
of mNot: genUnaryABC(c, n, dest, opcNot)
|
||||||
of mUnaryMinusI, mUnaryMinusI64: genUnaryABC(c, n, dest, opcUnaryMinusInt)
|
of mUnaryMinusI, mUnaryMinusI64:
|
||||||
|
genUnaryABC(c, n, dest, opcUnaryMinusInt)
|
||||||
|
genNarrow(c, n, dest)
|
||||||
of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat)
|
of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat)
|
||||||
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: gen(c, n.sons[1], dest)
|
of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: gen(c, n.sons[1], dest)
|
||||||
of mBitnotI, mBitnotI64: genUnaryABC(c, n, dest, opcBitnotInt)
|
of mBitnotI, mBitnotI64:
|
||||||
|
genUnaryABC(c, n, dest, opcBitnotInt)
|
||||||
|
genNarrowU(c, n, dest)
|
||||||
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64,
|
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64,
|
||||||
mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt,
|
mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt,
|
||||||
mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr,
|
mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr,
|
||||||
|
|
@ -896,6 +963,10 @@ const
|
||||||
tyFloat, tyFloat32, tyFloat64, tyFloat128,
|
tyFloat, tyFloat32, tyFloat64, tyFloat128,
|
||||||
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64}
|
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64}
|
||||||
|
|
||||||
|
proc fitsRegister*(t: PType): bool =
|
||||||
|
t.skipTypes(abstractInst-{tyTypeDesc}).kind in {
|
||||||
|
tyRange, tyEnum, tyBool, tyInt..tyUInt64}
|
||||||
|
|
||||||
proc requiresCopy(n: PNode): bool =
|
proc requiresCopy(n: PNode): bool =
|
||||||
if n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind in atomicTypes:
|
if n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind in atomicTypes:
|
||||||
result = false
|
result = false
|
||||||
|
|
@ -907,22 +978,33 @@ proc requiresCopy(n: PNode): bool =
|
||||||
proc unneededIndirection(n: PNode): bool =
|
proc unneededIndirection(n: PNode): bool =
|
||||||
n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef
|
n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef
|
||||||
|
|
||||||
proc skipDeref(n: PNode): PNode =
|
|
||||||
if n.kind in {nkDerefExpr, nkHiddenDeref} and unneededIndirection(n.sons[0]):
|
|
||||||
result = n.sons[0]
|
|
||||||
else:
|
|
||||||
result = n
|
|
||||||
|
|
||||||
proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
|
proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
|
||||||
flags: TGenFlags) =
|
flags: TGenFlags) =
|
||||||
# a nop for certain types
|
# a nop for certain types
|
||||||
let flags = if opc == opcAddr: flags+{gfAddrOf} else: flags
|
let isAddr = opc in {opcAddrNode, opcAddrReg}
|
||||||
if unneededIndirection(n.sons[0]):
|
let newflags = if isAddr: flags+{gfAddrOf} else: flags
|
||||||
gen(c, n.sons[0], dest, flags)
|
# consider:
|
||||||
|
# proc foo(f: var ref int) =
|
||||||
|
# f = new(int)
|
||||||
|
# proc blah() =
|
||||||
|
# var x: ref int
|
||||||
|
# foo x
|
||||||
|
#
|
||||||
|
# The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for
|
||||||
|
# nkAddr we must not use 'unneededIndirection', but for deref we use it.
|
||||||
|
if not isAddr and unneededIndirection(n.sons[0]):
|
||||||
|
gen(c, n.sons[0], dest, newflags)
|
||||||
else:
|
else:
|
||||||
let tmp = c.genx(n.sons[0], flags)
|
let tmp = c.genx(n.sons[0], newflags)
|
||||||
if dest < 0: dest = c.getTemp(n.typ)
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
gABC(c, n, opc, dest, tmp)
|
if not isAddr:
|
||||||
|
gABC(c, n, opc, dest, tmp)
|
||||||
|
if gfAddrOf notin flags and fitsRegister(n.typ):
|
||||||
|
c.gABC(n, opcNodeToReg, dest, dest)
|
||||||
|
elif c.prc.slots[tmp].kind >= slotTempUnknown:
|
||||||
|
gABC(c, n, opcAddrNode, dest, tmp)
|
||||||
|
else:
|
||||||
|
gABC(c, n, opcAddrReg, dest, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
proc whichAsgnOpc(n: PNode): TOpcode =
|
proc whichAsgnOpc(n: PNode): TOpcode =
|
||||||
|
|
@ -940,8 +1022,7 @@ proc whichAsgnOpc(n: PNode): TOpcode =
|
||||||
|
|
||||||
proc isRef(t: PType): bool = t.skipTypes(abstractRange-{tyTypeDesc}).kind == tyRef
|
proc isRef(t: PType): bool = t.skipTypes(abstractRange-{tyTypeDesc}).kind == tyRef
|
||||||
|
|
||||||
proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode =
|
proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
|
||||||
if isRef(n.typ): succ(opc) else: opc
|
|
||||||
|
|
||||||
proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
|
proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
|
||||||
let tmp = c.genx(ri)
|
let tmp = c.genx(ri)
|
||||||
|
|
@ -949,8 +1030,6 @@ proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
|
||||||
gABC(c, ri, whichAsgnOpc(ri), dest, tmp)
|
gABC(c, ri, whichAsgnOpc(ri), dest, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
|
|
||||||
|
|
||||||
proc setSlot(c: PCtx; v: PSym) =
|
proc setSlot(c: PCtx; v: PSym) =
|
||||||
# XXX generate type initialization here?
|
# XXX generate type initialization here?
|
||||||
if v.position == 0:
|
if v.position == 0:
|
||||||
|
|
@ -959,40 +1038,71 @@ proc setSlot(c: PCtx; v: PSym) =
|
||||||
kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
|
kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
|
||||||
inc c.prc.maxSlots
|
inc c.prc.maxSlots
|
||||||
|
|
||||||
|
proc cannotEval(n: PNode) {.noinline.} =
|
||||||
|
globalError(n.info, errGenerated, "cannot evaluate at compile time: " &
|
||||||
|
n.renderTree)
|
||||||
|
|
||||||
|
proc isOwnedBy(a, b: PSym): bool =
|
||||||
|
var a = a.owner
|
||||||
|
while a != nil and a.kind != skModule:
|
||||||
|
if a == b: return true
|
||||||
|
a = a.owner
|
||||||
|
|
||||||
|
proc getOwner(c: PCtx): PSym =
|
||||||
|
result = c.prc.sym
|
||||||
|
if result.isNil: result = c.module
|
||||||
|
|
||||||
|
proc checkCanEval(c: PCtx; n: PNode) =
|
||||||
|
# we need to ensure that we don't evaluate 'x' here:
|
||||||
|
# proc foo() = var x ...
|
||||||
|
let s = n.sym
|
||||||
|
if s.position == 0:
|
||||||
|
if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
|
||||||
|
not s.isOwnedBy(c.prc.sym) and s.owner != c.module:
|
||||||
|
cannotEval(n)
|
||||||
|
|
||||||
proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
||||||
case le.kind
|
case le.kind
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
let dest = c.genx(le.sons[0])
|
let dest = c.genx(le.sons[0], {gfAddrOf})
|
||||||
let idx = c.genx(le.sons[1])
|
let idx = c.genx(le.sons[1])
|
||||||
let tmp = c.genx(ri)
|
let tmp = c.genx(ri)
|
||||||
if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
|
if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
|
||||||
tyString, tyCString}:
|
tyString, tyCString}:
|
||||||
c.gABC(le, opcWrStrIdx, dest, idx, tmp)
|
c.gABC(le, opcWrStrIdx, dest, idx, tmp)
|
||||||
else:
|
else:
|
||||||
c.gABC(le, whichAsgnOpc(le, opcWrArr), dest, idx, tmp)
|
c.gABC(le, opcWrArr, dest, idx, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
of nkDotExpr, nkCheckedFieldExpr:
|
of nkDotExpr, nkCheckedFieldExpr:
|
||||||
# XXX field checks here
|
# XXX field checks here
|
||||||
let left = if le.kind == nkDotExpr: le else: le.sons[0]
|
let left = if le.kind == nkDotExpr: le else: le.sons[0]
|
||||||
let dest = c.genx(left.sons[0])
|
let dest = c.genx(left.sons[0], {gfAddrOf})
|
||||||
let idx = c.genx(left.sons[1])
|
let idx = genField(left.sons[1])
|
||||||
let tmp = c.genx(ri)
|
let tmp = c.genx(ri)
|
||||||
c.gABC(left, whichAsgnOpc(left, opcWrObj), dest, idx, tmp)
|
c.gABC(left, opcWrObj, dest, idx, tmp)
|
||||||
|
c.freeTemp(tmp)
|
||||||
|
of nkDerefExpr, nkHiddenDeref:
|
||||||
|
let dest = c.genx(le.sons[0], {gfAddrOf})
|
||||||
|
let tmp = c.genx(ri)
|
||||||
|
c.gABC(le, opcWrDeref, dest, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let s = le.sym
|
let s = le.sym
|
||||||
|
checkCanEval(c, le)
|
||||||
if s.isGlobal:
|
if s.isGlobal:
|
||||||
withTemp(tmp, le.typ):
|
withTemp(tmp, le.typ):
|
||||||
gen(c, ri, tmp)
|
c.gen(le, tmp, {gfAddrOf})
|
||||||
c.gABx(le, whichAsgnOpc(le, opcWrGlobal), tmp, s.position)
|
let val = c.genx(ri)
|
||||||
|
c.gABC(le, opcWrDeref, tmp, val)
|
||||||
|
c.freeTemp(val)
|
||||||
else:
|
else:
|
||||||
if s.kind == skForVar and c.mode == emRepl: c.setSlot s
|
if s.kind == skForVar: c.setSlot s
|
||||||
internalAssert s.position > 0 or (s.position == 0 and
|
internalAssert s.position > 0 or (s.position == 0 and
|
||||||
s.kind in {skParam,skResult})
|
s.kind in {skParam,skResult})
|
||||||
var dest: TRegister = s.position + ord(s.kind == skParam)
|
var dest: TRegister = s.position + ord(s.kind == skParam)
|
||||||
gen(c, ri, dest)
|
gen(c, ri, dest)
|
||||||
else:
|
else:
|
||||||
let dest = c.genx(le)
|
let dest = c.genx(le, {gfAddrOf})
|
||||||
genAsgn(c, dest, ri, requiresCopy)
|
genAsgn(c, dest, ri, requiresCopy)
|
||||||
|
|
||||||
proc genLit(c: PCtx; n: PNode; dest: var TDest) =
|
proc genLit(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
|
|
@ -1018,22 +1128,22 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
|
||||||
localError(info, errGenerated,
|
localError(info, errGenerated,
|
||||||
"cannot 'importc' variable at compile time")
|
"cannot 'importc' variable at compile time")
|
||||||
|
|
||||||
proc cannotEval(n: PNode) {.noinline.} =
|
proc getNullValue*(typ: PType, info: TLineInfo): PNode
|
||||||
globalError(n.info, errGenerated, "cannot evaluate at compile time: " &
|
|
||||||
n.renderTree)
|
|
||||||
|
|
||||||
proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
|
proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
|
||||||
c.globals.add(emptyNode.copyNode)
|
c.globals.add(getNullValue(s.typ, n.info))
|
||||||
s.position = c.globals.len
|
s.position = c.globals.len
|
||||||
# This is rather hard to support, due to the laziness of the VM code
|
# This is rather hard to support, due to the laziness of the VM code
|
||||||
# generator. See tests/compile/tmacro2 for why this is necesary:
|
# generator. See tests/compile/tmacro2 for why this is necesary:
|
||||||
# var decls{.compileTime.}: seq[PNimrodNode] = @[]
|
# var decls{.compileTime.}: seq[PNimrodNode] = @[]
|
||||||
c.gABx(n, opcGlobalOnce, 0, s.position)
|
let dest = c.getTemp(s.typ)
|
||||||
|
c.gABx(n, opcLdGlobal, dest, s.position)
|
||||||
let tmp = c.genx(s.ast)
|
let tmp = c.genx(s.ast)
|
||||||
c.gABx(n, whichAsgnOpc(n, opcWrGlobal), tmp, s.position)
|
c.gABC(n, opcWrDeref, dest, tmp)
|
||||||
|
c.freeTemp(dest)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
|
proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
if s.isGlobal:
|
if s.isGlobal:
|
||||||
if sfCompileTime in s.flags or c.mode == emRepl:
|
if sfCompileTime in s.flags or c.mode == emRepl:
|
||||||
|
|
@ -1043,17 +1153,21 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
if s.position == 0:
|
if s.position == 0:
|
||||||
if sfImportc in s.flags: c.importcSym(n.info, s)
|
if sfImportc in s.flags: c.importcSym(n.info, s)
|
||||||
else: genGlobalInit(c, n, s)
|
else: genGlobalInit(c, n, s)
|
||||||
if dest < 0:
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
dest = c.getGlobalSlot(n, s)
|
if gfAddrOf notin flags and fitsRegister(s.typ):
|
||||||
#c.gABx(n, opcAliasGlobal, dest, s.position)
|
var cc = c.getTemp(n.typ)
|
||||||
|
c.gABx(n, opcLdGlobal, cc, s.position)
|
||||||
|
c.gABC(n, opcNodeToReg, dest, cc)
|
||||||
|
c.freeTemp(cc)
|
||||||
else:
|
else:
|
||||||
c.gABx(n, opcLdGlobal, dest, s.position)
|
c.gABx(n, opcLdGlobal, dest, s.position)
|
||||||
else:
|
else:
|
||||||
if s.kind == skForVar and c.mode == emRepl: c.setSlot s
|
if s.kind == skForVar and c.mode == emRepl: c.setSlot(s)
|
||||||
if s.position > 0 or (s.position == 0 and
|
if s.position > 0 or (s.position == 0 and
|
||||||
s.kind in {skParam,skResult}):
|
s.kind in {skParam,skResult}):
|
||||||
if dest < 0:
|
if dest < 0:
|
||||||
dest = s.position + ord(s.kind == skParam)
|
dest = s.position + ord(s.kind == skParam)
|
||||||
|
internalAssert(c.prc.slots[dest].kind < slotSomeTemp)
|
||||||
else:
|
else:
|
||||||
# we need to generate an assignment:
|
# we need to generate an assignment:
|
||||||
genAsgn(c, dest, n, c.prc.slots[dest].kind >= slotSomeTemp)
|
genAsgn(c, dest, n, c.prc.slots[dest].kind >= slotSomeTemp)
|
||||||
|
|
@ -1061,30 +1175,45 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
# see tests/t99bott for an example that triggers it:
|
# see tests/t99bott for an example that triggers it:
|
||||||
cannotEval(n)
|
cannotEval(n)
|
||||||
|
|
||||||
proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
|
proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
|
||||||
flags: TGenFlags) =
|
flags: TGenFlags) =
|
||||||
let a = c.genx(n.sons[0], flags)
|
let a = c.genx(n.sons[0], flags)
|
||||||
let b = c.genx(n.sons[1], {})
|
let b = c.genx(n.sons[1], {})
|
||||||
if dest < 0: dest = c.getTemp(n.typ)
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
c.gABC(n, (if gfAddrOf in flags: succ(opc) else: opc), dest, a, b)
|
if gfAddrOf notin flags and fitsRegister(n.typ):
|
||||||
|
var cc = c.getTemp(n.typ)
|
||||||
|
c.gABC(n, opc, cc, a, b)
|
||||||
|
c.gABC(n, opcNodeToReg, dest, cc)
|
||||||
|
c.freeTemp(cc)
|
||||||
|
else:
|
||||||
|
c.gABC(n, opc, dest, a, b)
|
||||||
c.freeTemp(a)
|
c.freeTemp(a)
|
||||||
c.freeTemp(b)
|
c.freeTemp(b)
|
||||||
|
|
||||||
proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
genAccess(c, n, dest, opcLdObj, flags)
|
let a = c.genx(n.sons[0], flags)
|
||||||
|
let b = genField(n.sons[1])
|
||||||
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
|
if gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar})):
|
||||||
|
var cc = c.getTemp(n.typ)
|
||||||
|
c.gABC(n, opcLdObj, cc, a, b)
|
||||||
|
c.gABC(n, opcNodeToReg, dest, cc)
|
||||||
|
c.freeTemp(cc)
|
||||||
|
else:
|
||||||
|
c.gABC(n, opcLdObj, dest, a, b)
|
||||||
|
c.freeTemp(a)
|
||||||
|
|
||||||
proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
# XXX implement field checks!
|
# XXX implement field checks!
|
||||||
genAccess(c, n.sons[0], dest, opcLdObj, flags)
|
genObjAccess(c, n.sons[0], dest, flags)
|
||||||
|
|
||||||
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
if n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
|
if n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
|
||||||
tyString, tyCString}:
|
tyString, tyCString}:
|
||||||
genAccess(c, n, dest, opcLdStrIdx, {})
|
genArrAccess2(c, n, dest, opcLdStrIdx, {})
|
||||||
else:
|
else:
|
||||||
genAccess(c, n, dest, opcLdArr, flags)
|
genArrAccess2(c, n, dest, opcLdArr, flags)
|
||||||
|
|
||||||
proc getNullValue*(typ: PType, info: TLineInfo): PNode
|
|
||||||
proc getNullValueAux(obj: PNode, result: PNode) =
|
proc getNullValueAux(obj: PNode, result: PNode) =
|
||||||
case obj.kind
|
case obj.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
|
|
@ -1137,50 +1266,51 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
|
||||||
result = newNodeIT(nkCurly, info, t)
|
result = newNodeIT(nkCurly, info, t)
|
||||||
else: internalError("getNullValue: " & $t.kind)
|
else: internalError("getNullValue: " & $t.kind)
|
||||||
|
|
||||||
|
proc ldNullOpcode(t: PType): TOpcode =
|
||||||
|
if fitsRegister(t): opcLdNullReg else: opcLdNull
|
||||||
|
|
||||||
proc genVarSection(c: PCtx; n: PNode) =
|
proc genVarSection(c: PCtx; n: PNode) =
|
||||||
for a in n:
|
for a in n:
|
||||||
if a.kind == nkCommentStmt: continue
|
if a.kind == nkCommentStmt: continue
|
||||||
#assert(a.sons[0].kind == nkSym) can happen for transformed vars
|
#assert(a.sons[0].kind == nkSym) can happen for transformed vars
|
||||||
if a.kind == nkVarTuple:
|
if a.kind == nkVarTuple:
|
||||||
let tmp = c.genx(a.lastSon)
|
|
||||||
for i in 0 .. a.len-3:
|
for i in 0 .. a.len-3:
|
||||||
setSlot(c, a[i].sym)
|
setSlot(c, a[i].sym)
|
||||||
# v = t[i]
|
checkCanEval(c, a[i])
|
||||||
var v: TDest = -1
|
c.gen(lowerTupleUnpacking(a, c.getOwner))
|
||||||
genRdVar(c, a[i], v)
|
|
||||||
c.gABC(n, opcLdObj, v, tmp, i)
|
|
||||||
# XXX globals?
|
|
||||||
c.freeTemp(tmp)
|
|
||||||
elif a.sons[0].kind == nkSym:
|
elif a.sons[0].kind == nkSym:
|
||||||
let s = a.sons[0].sym
|
let s = a.sons[0].sym
|
||||||
|
checkCanEval(c, a.sons[0])
|
||||||
if s.isGlobal:
|
if s.isGlobal:
|
||||||
if s.position == 0:
|
if s.position == 0:
|
||||||
if sfImportc in s.flags: c.importcSym(a.info, s)
|
if sfImportc in s.flags: c.importcSym(a.info, s)
|
||||||
else:
|
else:
|
||||||
let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast
|
let sa = if s.ast.isNil: getNullValue(s.typ, a.info)
|
||||||
|
else: canonValue(s.ast)
|
||||||
c.globals.add(sa)
|
c.globals.add(sa)
|
||||||
s.position = c.globals.len
|
s.position = c.globals.len
|
||||||
# "Once support" is unnecessary here
|
|
||||||
if a.sons[2].kind == nkEmpty:
|
if a.sons[2].kind == nkEmpty:
|
||||||
when false:
|
when false:
|
||||||
withTemp(tmp, s.typ):
|
withTemp(tmp, s.typ):
|
||||||
c.gABx(a, opcLdNull, tmp, c.genType(s.typ))
|
c.gABx(a, opcLdNull, tmp, c.genType(s.typ))
|
||||||
c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position)
|
c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position)
|
||||||
else:
|
else:
|
||||||
let tmp = genx(c, a.sons[2])
|
let tmp = c.genx(a.sons[0], {gfAddrOf})
|
||||||
c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position)
|
let val = c.genx(a.sons[2])
|
||||||
|
c.gABC(a, opcWrDeref, tmp, val)
|
||||||
|
c.freeTemp(val)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
else:
|
else:
|
||||||
setSlot(c, s)
|
setSlot(c, s)
|
||||||
if a.sons[2].kind == nkEmpty:
|
if a.sons[2].kind == nkEmpty:
|
||||||
c.gABx(a, opcLdNull, s.position, c.genType(s.typ))
|
c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
|
||||||
else:
|
else:
|
||||||
gen(c, a.sons[2], s.position.TRegister)
|
gen(c, a.sons[2], s.position.TRegister)
|
||||||
else:
|
else:
|
||||||
# assign to a.sons[0]; happens for closures
|
# assign to a.sons[0]; happens for closures
|
||||||
if a.sons[2].kind == nkEmpty:
|
if a.sons[2].kind == nkEmpty:
|
||||||
let tmp = genx(c, a.sons[0])
|
let tmp = genx(c, a.sons[0])
|
||||||
c.gABx(a, opcLdNull, tmp, c.genType(a.sons[0].typ))
|
c.gABx(a, ldNullOpcode(a[0].typ), tmp, c.genType(a.sons[0].typ))
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
else:
|
else:
|
||||||
genAsgn(c, a.sons[0], a.sons[2], true)
|
genAsgn(c, a.sons[0], a.sons[2], true)
|
||||||
|
|
@ -1188,10 +1318,19 @@ proc genVarSection(c: PCtx; n: PNode) =
|
||||||
proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
|
proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||||
if dest < 0: dest = c.getTemp(n.typ)
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
c.gABx(n, opcLdNull, dest, c.genType(n.typ))
|
c.gABx(n, opcLdNull, dest, c.genType(n.typ))
|
||||||
|
|
||||||
|
let intType = getSysType(tyInt)
|
||||||
|
let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
|
||||||
|
if seqType.kind == tySequence:
|
||||||
|
var tmp = c.getTemp(intType)
|
||||||
|
c.gABx(n, opcLdImmInt, tmp, n.len)
|
||||||
|
c.gABx(n, opcNewSeq, dest, c.genType(seqType))
|
||||||
|
c.gABx(n, opcNewSeq, tmp, 0)
|
||||||
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
if n.len > 0:
|
if n.len > 0:
|
||||||
let intType = getSysType(tyInt)
|
|
||||||
var tmp = getTemp(c, intType)
|
var tmp = getTemp(c, intType)
|
||||||
c.gABx(n, opcLdNull, tmp, c.genType(intType))
|
c.gABx(n, opcLdNullReg, tmp, c.genType(intType))
|
||||||
for x in n:
|
for x in n:
|
||||||
let a = c.genx(x)
|
let a = c.genx(x)
|
||||||
c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a)
|
c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a)
|
||||||
|
|
@ -1224,11 +1363,10 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||||
for i in 1.. <n.len:
|
for i in 1.. <n.len:
|
||||||
let it = n.sons[i]
|
let it = n.sons[i]
|
||||||
if it.kind == nkExprColonExpr and it.sons[0].kind == nkSym:
|
if it.kind == nkExprColonExpr and it.sons[0].kind == nkSym:
|
||||||
let idx = c.genx(it.sons[0])
|
let idx = genField(it.sons[0])
|
||||||
let tmp = c.genx(it.sons[1])
|
let tmp = c.genx(it.sons[1])
|
||||||
c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp)
|
c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
c.freeTemp(idx)
|
|
||||||
else:
|
else:
|
||||||
internalError(n.info, "invalid object constructor")
|
internalError(n.info, "invalid object constructor")
|
||||||
|
|
||||||
|
|
@ -1239,11 +1377,10 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||||
for i in 0.. <n.len:
|
for i in 0.. <n.len:
|
||||||
let it = n.sons[i]
|
let it = n.sons[i]
|
||||||
if it.kind == nkExprColonExpr:
|
if it.kind == nkExprColonExpr:
|
||||||
let idx = c.genx(it.sons[0])
|
let idx = genField(it.sons[0])
|
||||||
let tmp = c.genx(it.sons[1])
|
let tmp = c.genx(it.sons[1])
|
||||||
c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp)
|
c.gABC(it, whichAsgnOpc(it.sons[1], opcWrObj), dest, idx, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
c.freeTemp(idx)
|
|
||||||
else:
|
else:
|
||||||
let tmp = c.genx(it)
|
let tmp = c.genx(it)
|
||||||
c.gABC(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp)
|
c.gABC(it, whichAsgnOpc(it, opcWrObj), dest, i.TRegister, tmp)
|
||||||
|
|
@ -1255,10 +1392,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let s = n.sym
|
let s = n.sym
|
||||||
|
checkCanEval(c, n)
|
||||||
case s.kind
|
case s.kind
|
||||||
of skVar, skForVar, skTemp, skLet, skParam, skResult:
|
of skVar, skForVar, skTemp, skLet, skParam, skResult:
|
||||||
genRdVar(c, n, dest)
|
genRdVar(c, n, dest, flags)
|
||||||
of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator:
|
of skProc, skConverter, skMacro, skTemplate, skMethod, skIterators:
|
||||||
# 'skTemplate' is only allowed for 'getAst' support:
|
# 'skTemplate' is only allowed for 'getAst' support:
|
||||||
if sfImportc in s.flags: c.importcSym(n.info, s)
|
if sfImportc in s.flags: c.importcSym(n.info, s)
|
||||||
genLit(c, n, dest)
|
genLit(c, n, dest)
|
||||||
|
|
@ -1271,12 +1409,6 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||||
else:
|
else:
|
||||||
var lit = genLiteral(c, newIntNode(nkIntLit, s.position))
|
var lit = genLiteral(c, newIntNode(nkIntLit, s.position))
|
||||||
c.gABx(n, opcLdConst, dest, lit)
|
c.gABx(n, opcLdConst, dest, lit)
|
||||||
of skField:
|
|
||||||
internalAssert dest < 0
|
|
||||||
if s.position > high(dest):
|
|
||||||
internalError(n.info,
|
|
||||||
"too large offset! cannot generate code for: " & s.name.s)
|
|
||||||
dest = s.position
|
|
||||||
of skType:
|
of skType:
|
||||||
genTypeLit(c, s.typ, dest)
|
genTypeLit(c, s.typ, dest)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1303,8 +1435,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||||
of nkDotExpr: genObjAccess(c, n, dest, flags)
|
of nkDotExpr: genObjAccess(c, n, dest, flags)
|
||||||
of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags)
|
of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags)
|
||||||
of nkBracketExpr: genArrAccess(c, n, dest, flags)
|
of nkBracketExpr: genArrAccess(c, n, dest, flags)
|
||||||
of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcDeref, flags)
|
of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags)
|
||||||
of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddr, flags)
|
of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags)
|
||||||
of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest)
|
of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest)
|
||||||
of nkCaseStmt: genCase(c, n, dest)
|
of nkCaseStmt: genCase(c, n, dest)
|
||||||
of nkWhileStmt:
|
of nkWhileStmt:
|
||||||
|
|
@ -1423,7 +1555,7 @@ proc optimizeJumps(c: PCtx; start: int) =
|
||||||
var d = i + c.code[i].jmpDiff
|
var d = i + c.code[i].jmpDiff
|
||||||
for iters in countdown(maxIterations, 0):
|
for iters in countdown(maxIterations, 0):
|
||||||
case c.code[d].opcode
|
case c.code[d].opcode
|
||||||
of opcJmp:
|
of opcJmp, opcJmpBack:
|
||||||
d = d + c.code[d].jmpDiff
|
d = d + c.code[d].jmpDiff
|
||||||
of opcTJmp, opcFJmp:
|
of opcTJmp, opcFJmp:
|
||||||
if c.code[d].regA != reg: break
|
if c.code[d].regA != reg: break
|
||||||
|
|
@ -1441,7 +1573,7 @@ proc optimizeJumps(c: PCtx; start: int) =
|
||||||
else: break
|
else: break
|
||||||
if d != i + c.code[i].jmpDiff:
|
if d != i + c.code[i].jmpDiff:
|
||||||
c.finalJumpTarget(i, d - i)
|
c.finalJumpTarget(i, d - i)
|
||||||
of opcJmp:
|
of opcJmp, opcJmpBack:
|
||||||
var d = i + c.code[i].jmpDiff
|
var d = i + c.code[i].jmpDiff
|
||||||
var iters = maxIterations
|
var iters = maxIterations
|
||||||
while c.code[d].opcode == opcJmp and iters > 0:
|
while c.code[d].opcode == opcJmp and iters > 0:
|
||||||
|
|
@ -1472,7 +1604,7 @@ proc genProc(c: PCtx; s: PSym): int =
|
||||||
# procs easily:
|
# procs easily:
|
||||||
let body = s.getBody
|
let body = s.getBody
|
||||||
let procStart = c.xjmp(body, opcJmp, 0)
|
let procStart = c.xjmp(body, opcJmp, 0)
|
||||||
var p = PProc(blocks: @[])
|
var p = PProc(blocks: @[], sym: s)
|
||||||
let oldPrc = c.prc
|
let oldPrc = c.prc
|
||||||
c.prc = p
|
c.prc = p
|
||||||
# iterate over the parameters and allocate space for them:
|
# iterate over the parameters and allocate space for them:
|
||||||
|
|
@ -1489,9 +1621,9 @@ proc genProc(c: PCtx; s: PSym): int =
|
||||||
c.gABC(body, opcEof, eofInstr.regA)
|
c.gABC(body, opcEof, eofInstr.regA)
|
||||||
c.optimizeJumps(result)
|
c.optimizeJumps(result)
|
||||||
s.offset = c.prc.maxSlots
|
s.offset = c.prc.maxSlots
|
||||||
#if s.name.s == "concatStyleInterpolation":
|
#if s.name.s == "addStuff":
|
||||||
|
# echo renderTree(body)
|
||||||
# c.echoCode(result)
|
# c.echoCode(result)
|
||||||
# echo renderTree(body)
|
|
||||||
c.prc = oldPrc
|
c.prc = oldPrc
|
||||||
else:
|
else:
|
||||||
c.prc.maxSlots = s.offset
|
c.prc.maxSlots = s.offset
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,8 @@ type
|
||||||
wWatchPoint, wSubsChar,
|
wWatchPoint, wSubsChar,
|
||||||
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
|
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
|
||||||
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
|
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
|
||||||
wNoStackFrame,
|
wAsmNoStackFrame,
|
||||||
wImplicitStatic, wGlobal, wCodegenDecl,
|
wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked,
|
||||||
|
|
||||||
wAuto, wBool, wCatch, wChar, wClass,
|
wAuto, wBool, wCatch, wChar, wClass,
|
||||||
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
|
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
|
||||||
|
|
@ -72,7 +72,7 @@ type
|
||||||
wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast,
|
wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast,
|
||||||
wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch,
|
wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch,
|
||||||
wThis, wThrow, wTrue, wTypedef, wTypeid, wTypename,
|
wThis, wThrow, wTrue, wTypedef, wTypeid, wTypename,
|
||||||
wUnion, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t,
|
wUnion, wPacked, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t,
|
||||||
|
|
||||||
wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept,
|
wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept,
|
||||||
wThread_local, wStatic_assert, wChar16_t, wChar32_t,
|
wThread_local, wStatic_assert, wChar16_t, wChar32_t,
|
||||||
|
|
@ -145,7 +145,7 @@ const
|
||||||
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
|
||||||
"computedgoto", "injectstmt",
|
"computedgoto", "injectstmt",
|
||||||
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
|
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
|
||||||
"nostackframe", "implicitstatic", "global", "codegendecl",
|
"asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
|
||||||
|
|
||||||
"auto", "bool", "catch", "char", "class",
|
"auto", "bool", "catch", "char", "class",
|
||||||
"const_cast", "default", "delete", "double",
|
"const_cast", "default", "delete", "double",
|
||||||
|
|
@ -155,7 +155,7 @@ const
|
||||||
"private", "protected", "public", "register", "reinterpret_cast",
|
"private", "protected", "public", "register", "reinterpret_cast",
|
||||||
"short", "signed", "sizeof", "static_cast", "struct", "switch",
|
"short", "signed", "sizeof", "static_cast", "struct", "switch",
|
||||||
"this", "throw", "true", "typedef", "typeid",
|
"this", "throw", "true", "typedef", "typeid",
|
||||||
"typename", "union", "unsigned", "virtual", "void", "volatile",
|
"typename", "union", "packed", "unsigned", "virtual", "void", "volatile",
|
||||||
"wchar_t",
|
"wchar_t",
|
||||||
|
|
||||||
"alignas", "alignof", "constexpr", "decltype", "nullptr", "noexcept",
|
"alignas", "alignof", "constexpr", "decltype", "nullptr", "noexcept",
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ Is translated into:
|
||||||
printf("%s\x0A", x)
|
printf("%s\x0A", x)
|
||||||
else:
|
else:
|
||||||
template OUT*(x: expr): stmt =
|
template OUT*(x: expr): stmt =
|
||||||
nil
|
discard
|
||||||
|
|
||||||
As can been seen from the example, C's macros with parameters are mapped
|
As can been seen from the example, C's macros with parameters are mapped
|
||||||
to Nimrod's templates. This mapping is the best one can do, but it is of course
|
to Nimrod's templates. This mapping is the best one can do, but it is of course
|
||||||
|
|
|
||||||
|
|
@ -279,8 +279,8 @@ skForVar
|
||||||
col 7: ""
|
col 7: ""
|
||||||
|
|
||||||
|
|
||||||
skIterator
|
skIterator, skClosureIterator
|
||||||
----------
|
-----------------------------
|
||||||
|
|
||||||
The fourth column will be the empty string if the iterator is being
|
The fourth column will be the empty string if the iterator is being
|
||||||
defined, since at that point in the file the parser hasn't processed
|
defined, since at that point in the file the parser hasn't processed
|
||||||
|
|
|
||||||
|
|
@ -386,9 +386,6 @@ Database support
|
||||||
A higher level SQLite database wrapper. The same interface is implemented
|
A higher level SQLite database wrapper. The same interface is implemented
|
||||||
for other databases too.
|
for other databases too.
|
||||||
|
|
||||||
* `db_mongo <db_mongo.html>`_
|
|
||||||
A higher level **mongodb** wrapper.
|
|
||||||
|
|
||||||
|
|
||||||
Other
|
Other
|
||||||
-----
|
-----
|
||||||
|
|
@ -503,8 +500,6 @@ Database support
|
||||||
Contains a wrapper for the mySQL API.
|
Contains a wrapper for the mySQL API.
|
||||||
* `sqlite3 <sqlite3.html>`_
|
* `sqlite3 <sqlite3.html>`_
|
||||||
Contains a wrapper for SQLite 3 API.
|
Contains a wrapper for SQLite 3 API.
|
||||||
* `mongodb <mongo.html>`_
|
|
||||||
Lower level wrapper for the **mongodb** client C library.
|
|
||||||
* `odbcsql <odbcsql.html>`_
|
* `odbcsql <odbcsql.html>`_
|
||||||
interface to the ODBC driver.
|
interface to the ODBC driver.
|
||||||
* `sphinx <sphinx.html>`_
|
* `sphinx <sphinx.html>`_
|
||||||
|
|
|
||||||
285
doc/manual.txt
285
doc/manual.txt
|
|
@ -79,8 +79,21 @@ program execution. Unless explicitly classified, an error is a static error.
|
||||||
|
|
||||||
A `checked runtime error`:idx: is an error that the implementation detects
|
A `checked runtime error`:idx: is an error that the implementation detects
|
||||||
and reports at runtime. The method for reporting such errors is via *raising
|
and reports at runtime. The method for reporting such errors is via *raising
|
||||||
exceptions*. However, the implementation provides a means to disable these
|
exceptions* or *dying with a fatal error*. However, the implementation
|
||||||
runtime checks. See the section pragmas_ for details.
|
provides a means to disable these runtime checks. See the section pragmas_
|
||||||
|
for details.
|
||||||
|
|
||||||
|
Wether a checked runtime error results in an exception or in a fatal error at
|
||||||
|
runtime is implementation specific. Thus the following program is always
|
||||||
|
invalid:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
var a: array[0..1, char]
|
||||||
|
let i = 5
|
||||||
|
try:
|
||||||
|
a[i] = 'N'
|
||||||
|
except EInvalidIndex:
|
||||||
|
echo "invalid index"
|
||||||
|
|
||||||
An `unchecked runtime error`:idx: is an error that is not guaranteed to be
|
An `unchecked runtime error`:idx: is an error that is not guaranteed to be
|
||||||
detected, and can cause the subsequent behavior of the computation to
|
detected, and can cause the subsequent behavior of the computation to
|
||||||
|
|
@ -480,24 +493,27 @@ precedence and associativity; this is useful for meta programming.
|
||||||
Associativity
|
Associativity
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
All binary operators are left-associative, except binary operators whose
|
Binary operators whose relevant character is ``^`` are right-associative, all
|
||||||
relevant char is ``^``.
|
other binary operators are left-associative.
|
||||||
|
|
||||||
Precedence
|
Precedence
|
||||||
----------
|
----------
|
||||||
|
|
||||||
For operators that are not keywords the precedence is determined by the
|
Unary operators always bind stronger than any binary
|
||||||
|
operator: ``$a + b`` is ``($a) + b`` and not ``$(a + b)``.
|
||||||
|
|
||||||
|
If an unary operator's relevant character is ``@`` it is a `sigil-like`:idx:
|
||||||
|
operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed
|
||||||
|
as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``.
|
||||||
|
|
||||||
|
|
||||||
|
For binary operators that are not keywords the precedence is determined by the
|
||||||
following rules:
|
following rules:
|
||||||
|
|
||||||
If the operator ends with ``=`` and its relevant character is none of
|
If the operator ends with ``=`` and its relevant character is none of
|
||||||
``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which
|
``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which
|
||||||
has the lowest precedence.
|
has the lowest precedence.
|
||||||
|
|
||||||
If the operator's relevant character is ``@`` it is a `sigil-like`:idx:
|
|
||||||
operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed
|
|
||||||
as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``.
|
|
||||||
|
|
||||||
|
|
||||||
Otherwise precedence is determined by the relevant character.
|
Otherwise precedence is determined by the relevant character.
|
||||||
|
|
||||||
================ =============================================== ================== ===============
|
================ =============================================== ================== ===============
|
||||||
|
|
@ -508,7 +524,7 @@ Precedence level Operators Relevant char
|
||||||
7 ``+ -`` ``+ ~ |`` OP7
|
7 ``+ -`` ``+ ~ |`` OP7
|
||||||
6 ``&`` ``&`` OP6
|
6 ``&`` ``&`` OP6
|
||||||
5 ``..`` ``.`` OP5
|
5 ``..`` ``.`` OP5
|
||||||
4 ``== <= < >= > != in not_in is isnot not of`` ``= < > !`` OP4
|
4 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP4
|
||||||
3 ``and`` OP3
|
3 ``and`` OP3
|
||||||
2 ``or xor`` OP2
|
2 ``or xor`` OP2
|
||||||
1 ``@ : ?`` OP1
|
1 ``@ : ?`` OP1
|
||||||
|
|
@ -516,6 +532,51 @@ Precedence level Operators Relevant char
|
||||||
================ =============================================== ================== ===============
|
================ =============================================== ================== ===============
|
||||||
|
|
||||||
|
|
||||||
|
Strong spaces
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The number of spaces preceeding a non-keyword operator affects precedence
|
||||||
|
if the experimental parser directive ``#!strongSpaces`` is used. Indentation
|
||||||
|
is not used to determine the number of spaces. If 2 or more operators have the
|
||||||
|
same number of preceding spaces the precedence table applies, so ``1 + 3 * 4``
|
||||||
|
is still parsed as ``1 + (3 * 4)``, but ``1+3 * 4`` is parsed as ``(1+3) * 4``:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
#! strongSpaces
|
||||||
|
if foo+4 * 4 == 8 and b&c | 9 ++
|
||||||
|
bar:
|
||||||
|
echo ""
|
||||||
|
# is parsed as
|
||||||
|
if ((foo+4)*4 == 8) and (((b&c) | 9) ++ bar): echo ""
|
||||||
|
|
||||||
|
|
||||||
|
Furthermore whether an operator is used a prefix operator is affected by the
|
||||||
|
number of spaces:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
#! strongSpaces
|
||||||
|
echo $foo
|
||||||
|
# is parsed as
|
||||||
|
echo($foo)
|
||||||
|
|
||||||
|
This also affects whether ``[]``, ``{}``, ``()`` are parsed as constructors
|
||||||
|
or as accessors:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
#! strongSpaces
|
||||||
|
echo (1,2)
|
||||||
|
# is parsed as
|
||||||
|
echo((1,2))
|
||||||
|
|
||||||
|
Only 0, 1, 2, 4 or 8 spaces are allowed to specify precedence and it is
|
||||||
|
enforced that infix operators have the same amount of spaces before and after
|
||||||
|
them. This rules does not apply when a newline follows after the operator,
|
||||||
|
then only the preceding spaces are considered.
|
||||||
|
|
||||||
|
|
||||||
|
Grammar
|
||||||
|
-------
|
||||||
|
|
||||||
The grammar's start symbol is ``module``.
|
The grammar's start symbol is ``module``.
|
||||||
|
|
||||||
.. include:: grammar.txt
|
.. include:: grammar.txt
|
||||||
|
|
@ -1508,6 +1569,28 @@ currency. This can be solved with templates_.
|
||||||
defineCurrency(TEuro, int)
|
defineCurrency(TEuro, int)
|
||||||
|
|
||||||
|
|
||||||
|
The borrow pragma can also be used to annotate the distinct type to allow
|
||||||
|
certain builtin operations to be lifted:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
type
|
||||||
|
Foo = object
|
||||||
|
a, b: int
|
||||||
|
s: string
|
||||||
|
|
||||||
|
Bar {.borrow: `.`.} = distinct Foo
|
||||||
|
|
||||||
|
var bb: ref Bar
|
||||||
|
new bb
|
||||||
|
# field access now valid
|
||||||
|
bb.a = 90
|
||||||
|
bb.s = "abc"
|
||||||
|
|
||||||
|
Currently only the dot accessor can be borrowed in this way.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Void type
|
Void type
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
|
@ -2822,7 +2905,6 @@ as there are components in the tuple. The i'th iteration variable's type is
|
||||||
the type of the i'th component. In other words, implicit tuple unpacking in a
|
the type of the i'th component. In other words, implicit tuple unpacking in a
|
||||||
for loop context is supported.
|
for loop context is supported.
|
||||||
|
|
||||||
|
|
||||||
Implict items/pairs invocations
|
Implict items/pairs invocations
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
|
|
@ -2847,10 +2929,11 @@ First class iterators
|
||||||
There are 2 kinds of iterators in Nimrod: *inline* and *closure* iterators.
|
There are 2 kinds of iterators in Nimrod: *inline* and *closure* iterators.
|
||||||
An `inline iterator`:idx: is an iterator that's always inlined by the compiler
|
An `inline iterator`:idx: is an iterator that's always inlined by the compiler
|
||||||
leading to zero overhead for the abstraction, but may result in a heavy
|
leading to zero overhead for the abstraction, but may result in a heavy
|
||||||
increase in code size. Inline iterators are second class
|
increase in code size. Inline iterators are second class citizens;
|
||||||
citizens; one cannot pass them around like first class procs.
|
They can be passed as parameters only to other inlining code facilities like
|
||||||
|
templates, macros and other inline iterators.
|
||||||
|
|
||||||
In contrast to that, a `closure iterator`:idx: can be passed around:
|
In contrast to that, a `closure iterator`:idx: can be passed around more freely:
|
||||||
|
|
||||||
.. code-block:: nimrod
|
.. code-block:: nimrod
|
||||||
iterator count0(): int {.closure.} =
|
iterator count0(): int {.closure.} =
|
||||||
|
|
@ -2873,9 +2956,7 @@ Closure iterators have other restrictions than inline iterators:
|
||||||
1. ``yield`` in a closure iterator can not occur in a ``try`` statement.
|
1. ``yield`` in a closure iterator can not occur in a ``try`` statement.
|
||||||
2. For now, a closure iterator cannot be evaluated at compile time.
|
2. For now, a closure iterator cannot be evaluated at compile time.
|
||||||
3. ``return`` is allowed in a closure iterator (but rarely useful).
|
3. ``return`` is allowed in a closure iterator (but rarely useful).
|
||||||
4. Since closure iterators can be used as a collaborative tasking
|
4. Both inline and closure iterators cannot be recursive.
|
||||||
system, ``void`` is a valid return type for them.
|
|
||||||
5. Both inline and closure iterators cannot be recursive.
|
|
||||||
|
|
||||||
Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly
|
Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly
|
||||||
default to being inline, but that this may change in future versions of the
|
default to being inline, but that this may change in future versions of the
|
||||||
|
|
@ -2937,6 +3018,14 @@ parameters of an outer factory proc:
|
||||||
for f in foo():
|
for f in foo():
|
||||||
echo f
|
echo f
|
||||||
|
|
||||||
|
Implicit return type
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Since inline interators must always produce values that will be consumed in
|
||||||
|
a for loop, the compiler will implicity use the ``auto`` return type if no
|
||||||
|
type is given by the user. In contrast, since closure iterators can be used
|
||||||
|
as a collaborative tasking system, ``void`` is a valid return type for them.
|
||||||
|
|
||||||
|
|
||||||
Type sections
|
Type sections
|
||||||
=============
|
=============
|
||||||
|
|
@ -4016,8 +4105,8 @@ Static params can also appear in the signatures of generic types:
|
||||||
AffineTransform2D[T] = Matrix[3, 3, T]
|
AffineTransform2D[T] = Matrix[3, 3, T]
|
||||||
AffineTransform3D[T] = Matrix[4, 4, T]
|
AffineTransform3D[T] = Matrix[4, 4, T]
|
||||||
|
|
||||||
AffineTransform3D[float] # OK
|
var m1: AffineTransform3D[float] # OK
|
||||||
AffineTransform2D[string] # Error, `string` is not a `Number`
|
var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
|
||||||
|
|
||||||
|
|
||||||
typedesc
|
typedesc
|
||||||
|
|
@ -4106,6 +4195,59 @@ types that will match the typedesc param:
|
||||||
|
|
||||||
The constraint can be a concrete type or a type class.
|
The constraint can be a concrete type or a type class.
|
||||||
|
|
||||||
|
Special Operators
|
||||||
|
=================
|
||||||
|
|
||||||
|
dot operators
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Nimrod offers a special family of dot operators that can be used to
|
||||||
|
intercept and rewrite proc call and field access attempts, referring
|
||||||
|
to previously undeclared symbol names. They can be used to provide a
|
||||||
|
fluent interface to objects lying outside the static confines of the
|
||||||
|
Nimrod's type system such as values from dynamic scripting languages
|
||||||
|
or dynamic file formats such as JSON or XML.
|
||||||
|
|
||||||
|
When Nimrod encounters an expression that cannot be resolved by the
|
||||||
|
standard overload resolution rules, the current scope will be searched
|
||||||
|
for a dot operator that can be matched against a re-written form of
|
||||||
|
the expression, where the unknown field or proc name is converted to
|
||||||
|
an additional static string parameter:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
a.b # becomes `.`(a, "b")
|
||||||
|
a.b(c, d) # becomes `.`(a, "b", c, d)
|
||||||
|
|
||||||
|
The matched dot operators can be symbols of any callable kind (procs,
|
||||||
|
templates and macros), depending on the desired effect:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
proc `.` (js: PJsonNode, field: string): JSON = js[field]
|
||||||
|
|
||||||
|
var js = parseJson("{ x: 1, y: 2}")
|
||||||
|
echo js.x # outputs 1
|
||||||
|
echo js.y # outputs 2
|
||||||
|
|
||||||
|
The following dot operators are available:
|
||||||
|
|
||||||
|
operator `.`
|
||||||
|
------------
|
||||||
|
This operator will be matched against both field accesses and method calls.
|
||||||
|
|
||||||
|
operator `.()`
|
||||||
|
---------------
|
||||||
|
This operator will be matched exclusively against method calls. It has higher
|
||||||
|
precedence than the `.` operator and this allows you to handle expressions like
|
||||||
|
`x.y` and `x.y()` differently if you are interfacing with a scripting language
|
||||||
|
for example.
|
||||||
|
|
||||||
|
operator `.=`
|
||||||
|
-------------
|
||||||
|
This operator will be matched against assignments to missing fields.
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
a.b = c # becomes `.=`(a, "b", c)
|
||||||
|
|
||||||
|
|
||||||
Term rewriting macros
|
Term rewriting macros
|
||||||
=====================
|
=====================
|
||||||
|
|
@ -4758,42 +4900,6 @@ This may change in future versions of language, but for now use
|
||||||
the ``finalizer`` parameter to ``new``.
|
the ``finalizer`` parameter to ``new``.
|
||||||
|
|
||||||
|
|
||||||
delegator pragma
|
|
||||||
----------------
|
|
||||||
|
|
||||||
**Note**: The design of the delegator feature is subject to change.
|
|
||||||
|
|
||||||
The delegator pragma can be used to intercept and rewrite proc call and field
|
|
||||||
access attempts referring to previously undeclared symbol names. It can be used
|
|
||||||
to provide a fluent interface to objects lying outside the static confines of
|
|
||||||
the Nimrod's type system such as values from dynamic scripting languages or
|
|
||||||
dynamic file formats such as JSON or XML.
|
|
||||||
|
|
||||||
A delegator is a special form of the `()` operator marked with the delagator
|
|
||||||
pragma. When Nimrod encounters an expression that cannot be resolved by the
|
|
||||||
standard overload resolution, any delegators in the current scope will be
|
|
||||||
matched against a rewritten form of the expression following the standard
|
|
||||||
signature matching rules. In the rewritten expression, the name of the unknown
|
|
||||||
proc or field name is inserted as an additional static string parameter always
|
|
||||||
appearing in the leading position:
|
|
||||||
|
|
||||||
.. code-block:: nimrod
|
|
||||||
a.b => delegator("b", a)
|
|
||||||
a.b(c, d) => delegator("b", a, c)
|
|
||||||
a b, c, d => delegator("a", b, c, d)
|
|
||||||
|
|
||||||
|
|
||||||
The delegators can be any callable symbol type (procs, templates, macros)
|
|
||||||
depending on the desired effect:
|
|
||||||
|
|
||||||
.. code-block:: nimrod
|
|
||||||
proc `()` (field: string, js: PJsonNode): JSON {.delegator.} = js[field]
|
|
||||||
|
|
||||||
var js = parseJson("{ x: 1, y: 2}")
|
|
||||||
echo js.x # outputs 1
|
|
||||||
echo js.y # outputs 2
|
|
||||||
|
|
||||||
|
|
||||||
procvar pragma
|
procvar pragma
|
||||||
--------------
|
--------------
|
||||||
The `procvar`:idx: pragma is used to mark a proc that it can be passed to a
|
The `procvar`:idx: pragma is used to mark a proc that it can be passed to a
|
||||||
|
|
@ -4876,16 +4982,16 @@ field which is used for runtime type identification is omitted. This is
|
||||||
necessary for binary compatibility with other compiled languages.
|
necessary for binary compatibility with other compiled languages.
|
||||||
|
|
||||||
|
|
||||||
NoStackFrame pragma
|
AsmNoStackFrame pragma
|
||||||
-------------------
|
----------------------
|
||||||
A proc can be marked with the `noStackFrame`:idx: pragma to tell the compiler
|
A proc can be marked with the `AsmNoStackFrame`:idx: pragma to tell the compiler
|
||||||
it should not generate a stack frame for the proc. There are also no exit
|
it should not generate a stack frame for the proc. There are also no exit
|
||||||
statements like ``return result;`` generated and the generated C function is
|
statements like ``return result;`` generated and the generated C function is
|
||||||
declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on
|
declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on
|
||||||
the used C compiler).
|
the used C compiler).
|
||||||
|
|
||||||
**Note**: This pragma should only be used by procs which consist solely of assembler
|
**Note**: This pragma should only be used by procs which consist solely of
|
||||||
statements.
|
assembler statements.
|
||||||
|
|
||||||
error pragma
|
error pragma
|
||||||
------------
|
------------
|
||||||
|
|
@ -5303,6 +5409,63 @@ strings automatically:
|
||||||
printf("hallo %s", "world") # "world" will be passed as C string
|
printf("hallo %s", "world") # "world" will be passed as C string
|
||||||
|
|
||||||
|
|
||||||
|
Union pragma
|
||||||
|
------------
|
||||||
|
The `union`:idx: pragma can be applied to any ``object`` type. It means all
|
||||||
|
of the object's fields are overlaid in memory. This produces a ``union``
|
||||||
|
instead of a ``struct`` in the generated C/C++ code. The object declaration
|
||||||
|
then must not use inheritance or any GC'ed memory but this is currently not
|
||||||
|
checked.
|
||||||
|
|
||||||
|
**Future directions**: GC'ed memory should be allowed in unions and the GC
|
||||||
|
should scan unions conservatively.
|
||||||
|
|
||||||
|
Packed pragma
|
||||||
|
-------------
|
||||||
|
The `packed`:idx: pragma can be applied to any ``object`` type. It ensures
|
||||||
|
that the fields of an object are packed back-to-back in memory. It is useful
|
||||||
|
to store packets or messages from/to network or hardware drivers, and for
|
||||||
|
interoperability with C. Combining packed pragma with inheritance is not
|
||||||
|
defined, and it should not be used with GC'ed memory (ref's).
|
||||||
|
|
||||||
|
**Future directions**: Using GC'ed memory in packed pragma will result in
|
||||||
|
compile-time error. Usage with inheritance should be defined and documented.
|
||||||
|
|
||||||
|
Unchecked pragma
|
||||||
|
----------------
|
||||||
|
The `unchecked`:idx: pragma can be used to mark a named array as ``unchecked``
|
||||||
|
meaning its bounds are not checked. This is often useful when one wishes to
|
||||||
|
implement his own flexibly sized arrays. Additionally an unchecked array is
|
||||||
|
translated into a C array of undetermined size:
|
||||||
|
|
||||||
|
.. code-block:: nimrod
|
||||||
|
type
|
||||||
|
ArrayPart{.unchecked.} = array[0..0, int]
|
||||||
|
MySeq = object
|
||||||
|
len, cap: int
|
||||||
|
data: ArrayPart
|
||||||
|
|
||||||
|
Produces roughly this C code:
|
||||||
|
|
||||||
|
.. code-block:: C
|
||||||
|
typedef struct {
|
||||||
|
NI len;
|
||||||
|
NI cap;
|
||||||
|
NI data[];
|
||||||
|
} MySeq;
|
||||||
|
|
||||||
|
The bounds checking done at compile time is not disabled for now, so to access
|
||||||
|
``s.data[C]`` (where ``C`` is a constant) the array's index needs needs to
|
||||||
|
include ``C``.
|
||||||
|
|
||||||
|
The base type of the unchecked array may not contain any GC'ed memory but this
|
||||||
|
is currently not checked.
|
||||||
|
|
||||||
|
**Future directions**: GC'ed memory should be allowed in unchecked arrays and
|
||||||
|
there should be an explicit annotation of how the GC is to determine the
|
||||||
|
runtime size of the array.
|
||||||
|
|
||||||
|
|
||||||
Dynlib pragma for import
|
Dynlib pragma for import
|
||||||
------------------------
|
------------------------
|
||||||
With the `dynlib`:idx: pragma a procedure or a variable can be imported from
|
With the `dynlib`:idx: pragma a procedure or a variable can be imported from
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ block mainLoop:
|
||||||
case x.kind
|
case x.kind
|
||||||
of xmlEof: break mainLoop
|
of xmlEof: break mainLoop
|
||||||
of xmlElementClose: break
|
of xmlElementClose: break
|
||||||
else: nil
|
else: discard
|
||||||
x.next() # skip ``xmlElementClose``
|
x.next() # skip ``xmlElementClose``
|
||||||
# now we have the description for the ``a`` element
|
# now we have the description for the ``a`` element
|
||||||
var desc = ""
|
var desc = ""
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import os, streams, parsexml, strutils
|
||||||
if paramCount() < 1:
|
if paramCount() < 1:
|
||||||
quit("Usage: htmltitle filename[.html]")
|
quit("Usage: htmltitle filename[.html]")
|
||||||
|
|
||||||
var filename = addFileExt(ParamStr(1), "html")
|
var filename = addFileExt(paramStr(1), "html")
|
||||||
var s = newFileStream(filename, fmRead)
|
var s = newFileStream(filename, fmRead)
|
||||||
if s == nil: quit("cannot open the file " & filename)
|
if s == nil: quit("cannot open the file " & filename)
|
||||||
var x: TXmlParser
|
var x: TXmlParser
|
||||||
|
|
@ -23,13 +23,13 @@ while true:
|
||||||
title.add(x.charData)
|
title.add(x.charData)
|
||||||
x.next()
|
x.next()
|
||||||
if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
|
if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
|
||||||
Echo("Title: " & title)
|
echo("Title: " & title)
|
||||||
quit(0) # Success!
|
quit(0) # Success!
|
||||||
else:
|
else:
|
||||||
echo(x.errorMsgExpected("/title"))
|
echo(x.errorMsgExpected("/title"))
|
||||||
|
|
||||||
of xmlEof: break # end of file reached
|
of xmlEof: break # end of file reached
|
||||||
else: nil # ignore other events
|
else: discard # ignore other events
|
||||||
|
|
||||||
x.close()
|
x.close()
|
||||||
quit("Could not determine title!")
|
quit("Could not determine title!")
|
||||||
|
|
|
||||||
27
koch.nim
27
koch.nim
|
|
@ -58,6 +58,15 @@ Boot options:
|
||||||
|
|
||||||
proc exe(f: string): string = return addFileExt(f, ExeExt)
|
proc exe(f: string): string = return addFileExt(f, ExeExt)
|
||||||
|
|
||||||
|
proc findNim(): string =
|
||||||
|
var nimrod = "nimrod".exe
|
||||||
|
result = "bin" / nimrod
|
||||||
|
if existsFile(result): return
|
||||||
|
for dir in split(getEnv("PATH"), PathSep):
|
||||||
|
if existsFile(dir / nimrod): return dir / nimrod
|
||||||
|
# assume there is a symlink to the exe or something:
|
||||||
|
return nimrod
|
||||||
|
|
||||||
proc exec(cmd: string) =
|
proc exec(cmd: string) =
|
||||||
echo(cmd)
|
echo(cmd)
|
||||||
if execShellCmd(cmd) != 0: quit("FAILURE")
|
if execShellCmd(cmd) != 0: quit("FAILURE")
|
||||||
|
|
@ -70,15 +79,15 @@ const
|
||||||
compileNimInst = "-d:useLibzipSrc tools/niminst/niminst"
|
compileNimInst = "-d:useLibzipSrc tools/niminst/niminst"
|
||||||
|
|
||||||
proc csource(args: string) =
|
proc csource(args: string) =
|
||||||
exec("nimrod cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" %
|
exec("$4 cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" %
|
||||||
[args, NimrodVersion, compileNimInst])
|
[args, NimrodVersion, compileNimInst, findNim()])
|
||||||
|
|
||||||
proc zip(args: string) =
|
proc zip(args: string) =
|
||||||
exec("nimrod cc -r $2 --var:version=$1 zip compiler/nimrod.ini" %
|
exec("$3 cc -r $2 --var:version=$1 zip compiler/nimrod.ini" %
|
||||||
[NimrodVersion, compileNimInst])
|
[NimrodVersion, compileNimInst, findNim()])
|
||||||
|
|
||||||
proc buildTool(toolname, args: string) =
|
proc buildTool(toolname, args: string) =
|
||||||
exec("nimrod cc $# $#" % [args, toolname])
|
exec("$# cc $# $#" % [findNim(), args, toolname])
|
||||||
copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)
|
copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)
|
||||||
|
|
||||||
proc inno(args: string) =
|
proc inno(args: string) =
|
||||||
|
|
@ -90,13 +99,13 @@ proc inno(args: string) =
|
||||||
NimrodVersion)
|
NimrodVersion)
|
||||||
|
|
||||||
proc install(args: string) =
|
proc install(args: string) =
|
||||||
exec("nimrod cc -r $# --var:version=$# scripts compiler/nimrod.ini" %
|
exec("$# cc -r $# --var:version=$# scripts compiler/nimrod.ini" %
|
||||||
[compileNimInst, NimrodVersion])
|
[findNim(), compileNimInst, NimrodVersion])
|
||||||
exec("sh ./install.sh $#" % args)
|
exec("sh ./install.sh $#" % args)
|
||||||
|
|
||||||
proc web(args: string) =
|
proc web(args: string) =
|
||||||
exec(("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" &
|
exec("$# cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" %
|
||||||
" --path:$#") % [NimrodVersion, getCurrentDir()])
|
[findNim(), NimrodVersion])
|
||||||
|
|
||||||
# -------------- boot ---------------------------------------------------------
|
# -------------- boot ---------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,17 +48,17 @@ type
|
||||||
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
|
nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt,
|
||||||
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
|
nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
|
||||||
nnkDiscardStmt, nnkStmtList,
|
nnkDiscardStmt, nnkStmtList,
|
||||||
|
|
||||||
nnkImportStmt,
|
nnkImportStmt,
|
||||||
nnkImportExceptStmt,
|
nnkImportExceptStmt,
|
||||||
nnkExportStmt,
|
nnkExportStmt,
|
||||||
nnkExportExceptStmt,
|
nnkExportExceptStmt,
|
||||||
nnkFromStmt,
|
nnkFromStmt,
|
||||||
nnkIncludeStmt,
|
nnkIncludeStmt,
|
||||||
|
|
||||||
nnkBindStmt, nnkMixinStmt, nnkUsingStmt,
|
nnkBindStmt, nnkMixinStmt, nnkUsingStmt,
|
||||||
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
|
nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
|
||||||
nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy,
|
nnkStmtListType, nnkBlockType,
|
||||||
|
nnkWith, nnkWithout,
|
||||||
|
nnkTypeOfExpr, nnkObjectTy,
|
||||||
nnkTupleTy, nnkTypeClassTy, nnkStaticTy,
|
nnkTupleTy, nnkTypeClassTy, nnkStaticTy,
|
||||||
nnkRecList, nnkRecCase, nnkRecWhen,
|
nnkRecList, nnkRecCase, nnkRecWhen,
|
||||||
nnkRefTy, nnkPtrTy, nnkVarTy,
|
nnkRefTy, nnkPtrTy, nnkVarTy,
|
||||||
|
|
@ -88,7 +88,7 @@ type
|
||||||
nskUnknown, nskConditional, nskDynLib, nskParam,
|
nskUnknown, nskConditional, nskDynLib, nskParam,
|
||||||
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
|
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
|
||||||
nskConst, nskResult,
|
nskConst, nskResult,
|
||||||
nskProc, nskMethod, nskIterator,
|
nskProc, nskMethod, nskIterator, nskClosureIterator,
|
||||||
nskConverter, nskMacro, nskTemplate, nskField,
|
nskConverter, nskMacro, nskTemplate, nskField,
|
||||||
nskEnumField, nskForVar, nskLabel,
|
nskEnumField, nskForVar, nskLabel,
|
||||||
nskStub
|
nskStub
|
||||||
|
|
@ -516,7 +516,7 @@ proc last*(node: PNimrodNode): PNimrodNode {.compileTime.} = node[node.high]
|
||||||
|
|
||||||
|
|
||||||
const
|
const
|
||||||
RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda}
|
RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda, nnkIteratorDef}
|
||||||
AtomicNodes* = {nnkNone..nnkNilLit}
|
AtomicNodes* = {nnkNone..nnkNilLit}
|
||||||
CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
|
CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
|
||||||
nnkCallStrLit, nnkHiddenCallConv}
|
nnkCallStrLit, nnkHiddenCallConv}
|
||||||
|
|
|
||||||
|
|
@ -1,227 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nimrod's Runtime Library
|
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## This module implements a higher level wrapper for `mongodb`:idx:. Example:
|
|
||||||
##
|
|
||||||
## .. code-block:: nimrod
|
|
||||||
##
|
|
||||||
## import mongo, db_mongo, oids, json
|
|
||||||
##
|
|
||||||
## var conn = db_mongo.open()
|
|
||||||
##
|
|
||||||
## # construct JSON data:
|
|
||||||
## var data = %{"a": %13, "b": %"my string value",
|
|
||||||
## "inner": %{"i": %71} }
|
|
||||||
##
|
|
||||||
## var id = insertID(conn, "test.test", data)
|
|
||||||
##
|
|
||||||
## for v in find(conn, "test.test", "this.a == 13"):
|
|
||||||
## print v
|
|
||||||
##
|
|
||||||
## delete(conn, "test.test", id)
|
|
||||||
## close(conn)
|
|
||||||
|
|
||||||
import mongo, oids, json
|
|
||||||
|
|
||||||
type
|
|
||||||
EDb* = object of EIO ## exception that is raised if a database error occurs
|
|
||||||
TDbConn* = TMongo ## a database connection; alias for ``TMongo``
|
|
||||||
|
|
||||||
FDb* = object of FIO ## effect that denotes a database operation
|
|
||||||
FReadDb* = object of FDB ## effect that denotes a read operation
|
|
||||||
FWriteDb* = object of FDB ## effect that denotes a write operation
|
|
||||||
|
|
||||||
proc dbError*(db: TDbConn, msg: string) {.noreturn.} =
|
|
||||||
## raises an EDb exception with message `msg`.
|
|
||||||
var e: ref EDb
|
|
||||||
new(e)
|
|
||||||
if db.errstr[0] != '\0':
|
|
||||||
e.msg = $db.errstr
|
|
||||||
else:
|
|
||||||
e.msg = $db.err & " " & msg
|
|
||||||
raise e
|
|
||||||
|
|
||||||
proc close*(db: var TDbConn) {.tags: [FDB].} =
|
|
||||||
## closes the database connection.
|
|
||||||
disconnect(db)
|
|
||||||
destroy(db)
|
|
||||||
|
|
||||||
proc open*(host: string = defaultHost, port: int = defaultPort): TDbConn {.
|
|
||||||
tags: [FDB].} =
|
|
||||||
## opens a database connection. Raises `EDb` if the connection could not
|
|
||||||
## be established.
|
|
||||||
init(result)
|
|
||||||
|
|
||||||
let x = client(result, host, port.cint)
|
|
||||||
if x != 0'i32:
|
|
||||||
dbError(result, "cannot open: " & host)
|
|
||||||
|
|
||||||
proc jsonToBSon(b: var TBSon, key: string, j: PJsonNode) =
|
|
||||||
case j.kind
|
|
||||||
of JString:
|
|
||||||
add(b, key, j.str)
|
|
||||||
of JInt:
|
|
||||||
add(b, key, j.num)
|
|
||||||
of JFloat:
|
|
||||||
add(b, key, j.fnum)
|
|
||||||
of JBool:
|
|
||||||
addBool(b, key, ord(j.bval))
|
|
||||||
of JNull:
|
|
||||||
addNull(b, key)
|
|
||||||
of JObject:
|
|
||||||
addStartObject(b, key)
|
|
||||||
for k, v in items(j.fields):
|
|
||||||
jsonToBSon(b, k, v)
|
|
||||||
addFinishObject(b)
|
|
||||||
of JArray:
|
|
||||||
addStartArray(b, key)
|
|
||||||
for i, e in pairs(j.elems):
|
|
||||||
jsonToBSon(b, $i, e)
|
|
||||||
addFinishArray(b)
|
|
||||||
|
|
||||||
proc jsonToBSon*(j: PJsonNode, oid: TOid): TBSon =
|
|
||||||
## converts a JSON value into the BSON format. The result must be
|
|
||||||
## ``destroyed`` explicitely!
|
|
||||||
init(result)
|
|
||||||
assert j.kind == JObject
|
|
||||||
add(result, "_id", oid)
|
|
||||||
for key, val in items(j.fields):
|
|
||||||
jsonToBSon(result, key, val)
|
|
||||||
finish(result)
|
|
||||||
|
|
||||||
proc `[]`*(obj: var TBSon, fieldname: cstring): TBSon =
|
|
||||||
## retrieves the value belonging to `fieldname`. Raises `EInvalidKey` if
|
|
||||||
## the attribute does not exist.
|
|
||||||
var it = initIter(obj)
|
|
||||||
let res = find(it, result, fieldname)
|
|
||||||
if res == bkEOO:
|
|
||||||
raise newException(EInvalidIndex, "key not in object")
|
|
||||||
|
|
||||||
proc getId*(obj: var TBSon): TOid =
|
|
||||||
## retrieves the ``_id`` attribute of `obj`.
|
|
||||||
var it = initIter(obj)
|
|
||||||
var b: TBSon
|
|
||||||
let res = find(it, b, "_id")
|
|
||||||
if res == bkOID:
|
|
||||||
result = oidVal(it)[]
|
|
||||||
else:
|
|
||||||
raise newException(EInvalidIndex, "_id not in object")
|
|
||||||
|
|
||||||
proc insertId*(db: var TDbConn, namespace: string, data: PJsonNode): TOid {.
|
|
||||||
tags: [FWriteDb].} =
|
|
||||||
## converts `data` to BSON format and inserts it in `namespace`. Returns
|
|
||||||
## the generated OID for the ``_id`` field.
|
|
||||||
result = genOid()
|
|
||||||
var x = jsonToBSon(data, result)
|
|
||||||
insert(db, namespace, x, nil)
|
|
||||||
destroy(x)
|
|
||||||
|
|
||||||
proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) {.
|
|
||||||
tags: [FWriteDb].} =
|
|
||||||
## converts `data` to BSON format and inserts it in `namespace`.
|
|
||||||
discard InsertID(db, namespace, data)
|
|
||||||
|
|
||||||
proc update*(db: var TDbConn, namespace: string, obj: var TBSon) {.
|
|
||||||
tags: [FReadDB, FWriteDb].} =
|
|
||||||
## updates `obj` in `namespace`.
|
|
||||||
var cond: TBson
|
|
||||||
init(cond)
|
|
||||||
cond.add("_id", getId(obj))
|
|
||||||
finish(cond)
|
|
||||||
update(db, namespace, cond, obj, ord(UPDATE_UPSERT))
|
|
||||||
destroy(cond)
|
|
||||||
|
|
||||||
proc update*(db: var TDbConn, namespace: string, oid: TOid, obj: PJsonNode) {.
|
|
||||||
tags: [FReadDB, FWriteDb].} =
|
|
||||||
## updates the data with `oid` to have the new data `obj`.
|
|
||||||
var a = jsonToBSon(obj, oid)
|
|
||||||
Update(db, namespace, a)
|
|
||||||
destroy(a)
|
|
||||||
|
|
||||||
proc delete*(db: var TDbConn, namespace: string, oid: TOid) {.
|
|
||||||
tags: [FWriteDb].} =
|
|
||||||
## Deletes the object belonging to `oid`.
|
|
||||||
var cond: TBson
|
|
||||||
init(cond)
|
|
||||||
cond.add("_id", oid)
|
|
||||||
finish(cond)
|
|
||||||
discard remove(db, namespace, cond)
|
|
||||||
destroy(cond)
|
|
||||||
|
|
||||||
proc delete*(db: var TDbConn, namespace: string, obj: var TBSon) {.
|
|
||||||
tags: [FWriteDb].} =
|
|
||||||
## Deletes the object `obj`.
|
|
||||||
delete(db, namespace, getId(obj))
|
|
||||||
|
|
||||||
iterator find*(db: var TDbConn, namespace: string): var TBSon {.
|
|
||||||
tags: [FReadDB].} =
|
|
||||||
## iterates over any object in `namespace`.
|
|
||||||
var cursor: TCursor
|
|
||||||
init(cursor, db, namespace)
|
|
||||||
while next(cursor) == mongo.OK:
|
|
||||||
yield bson(cursor)[]
|
|
||||||
destroy(cursor)
|
|
||||||
|
|
||||||
iterator find*(db: var TDbConn, namespace: string,
|
|
||||||
query, fields: var TBSon): var TBSon {.tags: [FReadDB].} =
|
|
||||||
## yields the `fields` of any document that suffices `query`.
|
|
||||||
var cursor = find(db, namespace, query, fields, 0'i32, 0'i32, 0'i32)
|
|
||||||
if cursor != nil:
|
|
||||||
while next(cursor[]) == mongo.OK:
|
|
||||||
yield bson(cursor[])[]
|
|
||||||
destroy(cursor[])
|
|
||||||
|
|
||||||
proc setupFieldnames(fields: varargs[string]): TBSon =
|
|
||||||
init(result)
|
|
||||||
for x in fields: add(result, x, 1'i32)
|
|
||||||
finish(result)
|
|
||||||
|
|
||||||
iterator find*(db: var TDbConn, namespace: string,
|
|
||||||
query: var TBSon, fields: varargs[string]): var TBSon {.
|
|
||||||
tags: [FReadDB].} =
|
|
||||||
## yields the `fields` of any document that suffices `query`. If `fields`
|
|
||||||
## is ``[]`` the whole document is yielded.
|
|
||||||
var f = setupFieldnames(fields)
|
|
||||||
var cursor = find(db, namespace, query, f, 0'i32, 0'i32, 0'i32)
|
|
||||||
if cursor != nil:
|
|
||||||
while next(cursor[]) == mongo.OK:
|
|
||||||
yield bson(cursor[])[]
|
|
||||||
destroy(cursor[])
|
|
||||||
destroy(f)
|
|
||||||
|
|
||||||
proc setupQuery(query: string): TBSon =
|
|
||||||
init(result)
|
|
||||||
add(result, "$where", query)
|
|
||||||
finish(result)
|
|
||||||
|
|
||||||
iterator find*(db: var TDbConn, namespace: string,
|
|
||||||
query: string, fields: varargs[string]): var TBSon {.
|
|
||||||
tags: [FReadDB].} =
|
|
||||||
## yields the `fields` of any document that suffices `query`. If `fields`
|
|
||||||
## is ``[]`` the whole document is yielded.
|
|
||||||
var f = setupFieldnames(fields)
|
|
||||||
var q = setupQuery(query)
|
|
||||||
var cursor = find(db, namespace, q, f, 0'i32, 0'i32, 0'i32)
|
|
||||||
if cursor != nil:
|
|
||||||
while next(cursor[]) == mongo.OK:
|
|
||||||
yield bson(cursor[])[]
|
|
||||||
destroy(cursor[])
|
|
||||||
destroy(q)
|
|
||||||
destroy(f)
|
|
||||||
|
|
||||||
when false:
|
|
||||||
# this doesn't work this way; would require low level hacking
|
|
||||||
iterator fieldPairs*(obj: var TBSon): tuple[key: cstring, value: TBSon] =
|
|
||||||
## iterates over `obj` and yields all (key, value)-Pairs.
|
|
||||||
var it = initIter(obj)
|
|
||||||
var v: TBSon
|
|
||||||
while next(it) != bkEOO:
|
|
||||||
let key = key(it)
|
|
||||||
discard init(v, value(it))
|
|
||||||
yield (key, v)
|
|
||||||
|
|
@ -87,7 +87,7 @@ proc newRow(L: int): TRow =
|
||||||
|
|
||||||
proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
|
proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
|
||||||
if row != nil:
|
if row != nil:
|
||||||
while mysql.FetchRow(sqlres) != nil: nil
|
while mysql.FetchRow(sqlres) != nil: discard
|
||||||
mysql.FreeResult(sqlres)
|
mysql.FreeResult(sqlres)
|
||||||
|
|
||||||
iterator fastRows*(db: TDbConn, query: TSqlQuery,
|
iterator fastRows*(db: TDbConn, query: TSqlQuery,
|
||||||
|
|
@ -195,8 +195,14 @@ proc open*(connection, user, password, database: string): TDbConn {.
|
||||||
## be established.
|
## be established.
|
||||||
result = mysql.Init(nil)
|
result = mysql.Init(nil)
|
||||||
if result == nil: dbError("could not open database connection")
|
if result == nil: dbError("could not open database connection")
|
||||||
if mysql.RealConnect(result, "", user, password, database,
|
let
|
||||||
0'i32, nil, 0) == nil:
|
colonPos = connection.find(':')
|
||||||
|
host = if colonPos < 0: connection
|
||||||
|
else: substr(connection, 0, colonPos-1)
|
||||||
|
port: int32 = if colonPos < 0: 0'i32
|
||||||
|
else: substr(connection, colonPos+1).parseInt.int32
|
||||||
|
if mysql.RealConnect(result, host, user, password, database,
|
||||||
|
port, nil, 0) == nil:
|
||||||
var errmsg = $mysql.error(result)
|
var errmsg = $mysql.error(result)
|
||||||
db_mysql.Close(result)
|
db_mysql.Close(result)
|
||||||
dbError(errmsg)
|
dbError(errmsg)
|
||||||
|
|
|
||||||
|
|
@ -285,8 +285,8 @@ static N_INLINE(NI32, float32ToInt32)(float x) {
|
||||||
|
|
||||||
typedef struct TStringDesc* string;
|
typedef struct TStringDesc* string;
|
||||||
|
|
||||||
/* declared size of a sequence: */
|
/* declared size of a sequence/variable length array: */
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
|
||||||
# define SEQ_DECL_SIZE /* empty is correct! */
|
# define SEQ_DECL_SIZE /* empty is correct! */
|
||||||
#else
|
#else
|
||||||
# define SEQ_DECL_SIZE 1000000
|
# define SEQ_DECL_SIZE 1000000
|
||||||
|
|
@ -314,6 +314,9 @@ static unsigned long nimNaN[2]={0xffffffff, 0x7fffffff};
|
||||||
# define INF INFINITY
|
# define INF INFINITY
|
||||||
# elif defined(HUGE_VAL)
|
# elif defined(HUGE_VAL)
|
||||||
# define INF HUGE_VAL
|
# define INF HUGE_VAL
|
||||||
|
# elif defined(_MSC_VER)
|
||||||
|
# include <float.h>
|
||||||
|
# define INF (DBL_MAX+DBL_MAX)
|
||||||
# else
|
# else
|
||||||
# define INF (1.0 / 0.0)
|
# define INF (1.0 / 0.0)
|
||||||
# endif
|
# endif
|
||||||
|
|
@ -373,5 +376,8 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
|
||||||
# define GC_GUARD
|
# define GC_GUARD
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Test to see if nimrod and the C compiler agrees on the size of a pointer.
|
||||||
|
On disagreement, your C compiler will say something like:
|
||||||
|
"error: 'assert_numbits' declared as an array with a negative size" */
|
||||||
typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
|
typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
6
lib/packages/docutils/docutils.babel
Normal file
6
lib/packages/docutils/docutils.babel
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[Package]
|
||||||
|
name = "docutils"
|
||||||
|
version = "0.9.0"
|
||||||
|
author = "Andreas Rumpf"
|
||||||
|
description = "Nimrod's reStructuredText processor."
|
||||||
|
license = "MIT"
|
||||||
|
|
@ -61,9 +61,8 @@ proc getSourceLanguage*(name: string): TSourceLanguage =
|
||||||
if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0:
|
if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0:
|
||||||
return i
|
return i
|
||||||
result = langNone
|
result = langNone
|
||||||
|
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: cstring) =
|
||||||
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) =
|
g.buf = buf
|
||||||
g.buf = cstring(buf)
|
|
||||||
g.kind = low(TTokenClass)
|
g.kind = low(TTokenClass)
|
||||||
g.start = 0
|
g.start = 0
|
||||||
g.length = 0
|
g.length = 0
|
||||||
|
|
@ -71,6 +70,8 @@ proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) =
|
||||||
var pos = 0 # skip initial whitespace:
|
var pos = 0 # skip initial whitespace:
|
||||||
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
|
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
|
||||||
g.pos = pos
|
g.pos = pos
|
||||||
|
proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) =
|
||||||
|
initGeneralTokenizer(g, cstring(buf))
|
||||||
|
|
||||||
proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) =
|
proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) =
|
||||||
discard
|
discard
|
||||||
|
|
|
||||||
|
|
@ -1543,7 +1543,7 @@ proc dirRaw(p: var TRstParser): PRstNode =
|
||||||
elif cmpIgnoreCase(result.sons[0].sons[0].text, "latex") == 0:
|
elif cmpIgnoreCase(result.sons[0].sons[0].text, "latex") == 0:
|
||||||
dirRawAux(p, result, rnRawLatex, parseLiteralBlock)
|
dirRawAux(p, result, rnRawLatex, parseLiteralBlock)
|
||||||
else:
|
else:
|
||||||
rstMessage(p, meInvalidDirective, result.sons[0].text)
|
rstMessage(p, meInvalidDirective, result.sons[0].sons[0].text)
|
||||||
else:
|
else:
|
||||||
dirRawAux(p, result, rnRaw, parseSectionWrapper)
|
dirRawAux(p, result, rnRaw, parseSectionWrapper)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from posix import TSocketHandle
|
||||||
|
|
||||||
const
|
const
|
||||||
EPOLLIN* = 0x00000001
|
EPOLLIN* = 0x00000001
|
||||||
EPOLLPRI* = 0x00000002
|
EPOLLPRI* = 0x00000002
|
||||||
|
|
@ -33,8 +35,8 @@ const
|
||||||
type
|
type
|
||||||
epoll_data* {.importc: "union epoll_data",
|
epoll_data* {.importc: "union epoll_data",
|
||||||
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
|
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
|
||||||
thePtr* {.importc: "ptr".}: pointer # \
|
#thePtr* {.importc: "ptr".}: pointer
|
||||||
#fd*: cint
|
fd* {.importc: "fd".}: cint # \
|
||||||
#u32*: uint32
|
#u32*: uint32
|
||||||
#u64*: uint64
|
#u64*: uint64
|
||||||
|
|
||||||
|
|
@ -54,7 +56,7 @@ proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
|
||||||
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
|
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
|
||||||
## parameter has been dropped.
|
## parameter has been dropped.
|
||||||
|
|
||||||
proc epoll_ctl*(epfd: cint; op: cint; fd: cint; event: ptr epoll_event): cint {.
|
proc epoll_ctl*(epfd: cint; op: cint; fd: cint | TSocketHandle; event: ptr epoll_event): cint {.
|
||||||
importc: "epoll_ctl", header: "<sys/epoll.h>".}
|
importc: "epoll_ctl", header: "<sys/epoll.h>".}
|
||||||
## Manipulate an epoll instance "epfd". Returns 0 in case of success,
|
## Manipulate an epoll instance "epfd". Returns 0 in case of success,
|
||||||
## -1 in case of error ( the "errno" variable will contain the
|
## -1 in case of error ( the "errno" variable will contain the
|
||||||
|
|
|
||||||
25
lib/posix/linux.nim
Normal file
25
lib/posix/linux.nim
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import posix
|
||||||
|
|
||||||
|
const
|
||||||
|
CSIGNAL* = 0x000000FF
|
||||||
|
CLONE_VM* = 0x00000100
|
||||||
|
CLONE_FS* = 0x00000200
|
||||||
|
CLONE_FILES* = 0x00000400
|
||||||
|
CLONE_SIGHAND* = 0x00000800
|
||||||
|
CLONE_PTRACE* = 0x00002000
|
||||||
|
CLONE_VFORK* = 0x00004000
|
||||||
|
CLONE_PARENT* = 0x00008000
|
||||||
|
CLONE_THREAD* = 0x00010000
|
||||||
|
CLONE_NEWNS* = 0x00020000
|
||||||
|
CLONE_SYSVSEM* = 0x00040000
|
||||||
|
CLONE_SETTLS* = 0x00080000
|
||||||
|
CLONE_PARENT_SETTID* = 0x00100000
|
||||||
|
CLONE_CHILD_CLEARTID* = 0x00200000
|
||||||
|
CLONE_DETACHED* = 0x00400000
|
||||||
|
CLONE_UNTRACED* = 0x00800000
|
||||||
|
CLONE_CHILD_SETTID* = 0x01000000
|
||||||
|
CLONE_STOPPED* = 0x02000000
|
||||||
|
|
||||||
|
# fn should be of type proc (a2: pointer): void {.cdecl.}
|
||||||
|
proc clone*(fn: pointer; child_stack: pointer; flags: cint;
|
||||||
|
arg: pointer; ptid: ptr TPid; tls: pointer; ctid: ptr TPid): cint {.importc, header: "<sched.h>".}
|
||||||
|
|
@ -2066,6 +2066,7 @@ proc pthread_spin_unlock*(a1: ptr Tpthread_spinlock): cint {.
|
||||||
proc pthread_testcancel*() {.importc, header: "<pthread.h>".}
|
proc pthread_testcancel*() {.importc, header: "<pthread.h>".}
|
||||||
|
|
||||||
|
|
||||||
|
proc exitnow*(code: int): void {.importc: "_exit", header: "<unistd.h>".}
|
||||||
proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".}
|
proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".}
|
||||||
proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".}
|
proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".}
|
||||||
proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
|
proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
|
||||||
|
|
@ -2265,6 +2266,7 @@ proc gmtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "<time.h>
|
||||||
proc localtime*(a1: var TTime): ptr Ttm {.importc, header: "<time.h>".}
|
proc localtime*(a1: var TTime): ptr Ttm {.importc, header: "<time.h>".}
|
||||||
proc localtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "<time.h>".}
|
proc localtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "<time.h>".}
|
||||||
proc mktime*(a1: var Ttm): TTime {.importc, header: "<time.h>".}
|
proc mktime*(a1: var Ttm): TTime {.importc, header: "<time.h>".}
|
||||||
|
proc timegm*(a1: var Ttm): TTime {.importc, header: "<time.h>".}
|
||||||
proc nanosleep*(a1, a2: var Ttimespec): cint {.importc, header: "<time.h>".}
|
proc nanosleep*(a1, a2: var Ttimespec): cint {.importc, header: "<time.h>".}
|
||||||
proc strftime*(a1: cstring, a2: int, a3: cstring,
|
proc strftime*(a1: cstring, a2: int, a3: cstring,
|
||||||
a4: var Ttm): int {.importc, header: "<time.h>".}
|
a4: var Ttm): int {.importc, header: "<time.h>".}
|
||||||
|
|
@ -2356,7 +2358,7 @@ proc FD_ZERO*(a1: var TFdSet) {.importc, header: "<sys/select.h>".}
|
||||||
|
|
||||||
proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec,
|
proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec,
|
||||||
a6: var Tsigset): cint {.importc, header: "<sys/select.h>".}
|
a6: var Tsigset): cint {.importc, header: "<sys/select.h>".}
|
||||||
proc select*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {.
|
proc select*(a1: cint | TSocketHandle, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {.
|
||||||
importc, header: "<sys/select.h>".}
|
importc, header: "<sys/select.h>".}
|
||||||
|
|
||||||
when hasSpawnH:
|
when hasSpawnH:
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,36 @@ proc smartBinarySearch*[T](a: openArray[T], key: T): int =
|
||||||
const
|
const
|
||||||
onlySafeCode = true
|
onlySafeCode = true
|
||||||
|
|
||||||
|
proc lowerBound*[T](a: openarray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int =
|
||||||
|
## same as binarySearch except that if key is not in `a` then this
|
||||||
|
## returns the location where `key` would be if it were. In other
|
||||||
|
## words if you have a sorted sequence and you call insert(thing, elm, lowerBound(thing, elm))
|
||||||
|
## the sequence will still be sorted
|
||||||
|
##
|
||||||
|
## `cmp` is the comparator function to use, the expected return values are the same as
|
||||||
|
## that of system.cmp
|
||||||
|
##
|
||||||
|
## example::
|
||||||
|
##
|
||||||
|
## var arr = @[1,2,3,5,6,7,8,9]
|
||||||
|
## arr.insert(4, arr.lowerBound(4))
|
||||||
|
## `after running the above arr is `[1,2,3,4,5,6,7,8,9]`
|
||||||
|
result = a.low
|
||||||
|
var pos = result
|
||||||
|
var count, step: int
|
||||||
|
count = a.high - a.low + 1
|
||||||
|
while count != 0:
|
||||||
|
pos = result
|
||||||
|
step = count div 2
|
||||||
|
pos += step
|
||||||
|
if cmp(a[pos], key) < 0:
|
||||||
|
pos.inc
|
||||||
|
result = pos
|
||||||
|
count -= step + 1
|
||||||
|
else:
|
||||||
|
count = step
|
||||||
|
|
||||||
|
proc lowerBound*[T](a: openarray[T], key: T): int = lowerBound(a, key, cmp[T])
|
||||||
proc merge[T](a, b: var openArray[T], lo, m, hi: int,
|
proc merge[T](a, b: var openArray[T], lo, m, hi: int,
|
||||||
cmp: proc (x, y: T): int {.closure.}, order: TSortOrder) =
|
cmp: proc (x, y: T): int {.closure.}, order: TSortOrder) =
|
||||||
template `<-` (a, b: expr) =
|
template `<-` (a, b: expr) =
|
||||||
|
|
|
||||||
956
lib/pure/asyncdispatch.nim
Normal file
956
lib/pure/asyncdispatch.nim
Normal file
|
|
@ -0,0 +1,956 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nimrod's Runtime Library
|
||||||
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os, oids, tables, strutils, macros
|
||||||
|
|
||||||
|
import rawsockets
|
||||||
|
|
||||||
|
## AsyncDispatch
|
||||||
|
## --------
|
||||||
|
##
|
||||||
|
## This module implements a brand new dispatcher based on Futures.
|
||||||
|
## On Windows IOCP is used and on other operating systems the selectors module
|
||||||
|
## is used instead.
|
||||||
|
|
||||||
|
# -- Futures
|
||||||
|
|
||||||
|
type
|
||||||
|
PFutureBase* = ref object of PObject
|
||||||
|
cb: proc () {.closure.}
|
||||||
|
finished: bool
|
||||||
|
|
||||||
|
PFuture*[T] = ref object of PFutureBase
|
||||||
|
value: T
|
||||||
|
error*: ref EBase # TODO: This shouldn't be necessary, generics bug?
|
||||||
|
|
||||||
|
proc newFuture*[T](): PFuture[T] =
|
||||||
|
## Creates a new future.
|
||||||
|
new(result)
|
||||||
|
result.finished = false
|
||||||
|
|
||||||
|
proc complete*[T](future: PFuture[T], val: T) =
|
||||||
|
## Completes ``future`` with value ``val``.
|
||||||
|
assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||||
|
assert(future.error == nil)
|
||||||
|
future.value = val
|
||||||
|
future.finished = true
|
||||||
|
if future.cb != nil:
|
||||||
|
future.cb()
|
||||||
|
|
||||||
|
proc complete*(future: PFuture[void]) =
|
||||||
|
## Completes a void ``future``.
|
||||||
|
assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||||
|
assert(future.error == nil)
|
||||||
|
future.finished = true
|
||||||
|
if future.cb != nil:
|
||||||
|
future.cb()
|
||||||
|
|
||||||
|
proc fail*[T](future: PFuture[T], error: ref EBase) =
|
||||||
|
## Completes ``future`` with ``error``.
|
||||||
|
assert(not future.finished, "Future already finished, cannot finish twice.")
|
||||||
|
future.finished = true
|
||||||
|
future.error = error
|
||||||
|
if future.cb != nil:
|
||||||
|
future.cb()
|
||||||
|
|
||||||
|
proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) =
|
||||||
|
## Sets the callback proc to be called when the future completes.
|
||||||
|
##
|
||||||
|
## If future has already completed then ``cb`` will be called immediately.
|
||||||
|
##
|
||||||
|
## **Note**: You most likely want the other ``callback`` setter which
|
||||||
|
## passes ``future`` as a param to the callback.
|
||||||
|
future.cb = cb
|
||||||
|
if future.finished:
|
||||||
|
future.cb()
|
||||||
|
|
||||||
|
proc `callback=`*[T](future: PFuture[T],
|
||||||
|
cb: proc (future: PFuture[T]) {.closure.}) =
|
||||||
|
## Sets the callback proc to be called when the future completes.
|
||||||
|
##
|
||||||
|
## If future has already completed then ``cb`` will be called immediately.
|
||||||
|
future.callback = proc () = cb(future)
|
||||||
|
|
||||||
|
proc read*[T](future: PFuture[T]): T =
|
||||||
|
## Retrieves the value of ``future``. Future must be finished otherwise
|
||||||
|
## this function will fail with a ``EInvalidValue`` exception.
|
||||||
|
##
|
||||||
|
## If the result of the future is an error then that error will be raised.
|
||||||
|
if future.finished:
|
||||||
|
if future.error != nil: raise future.error
|
||||||
|
when T isnot void:
|
||||||
|
return future.value
|
||||||
|
else:
|
||||||
|
# TODO: Make a custom exception type for this?
|
||||||
|
raise newException(EInvalidValue, "Future still in progress.")
|
||||||
|
|
||||||
|
proc readError*[T](future: PFuture[T]): ref EBase =
|
||||||
|
if future.error != nil: return future.error
|
||||||
|
else:
|
||||||
|
raise newException(EInvalidValue, "No error in future.")
|
||||||
|
|
||||||
|
proc finished*[T](future: PFuture[T]): bool =
|
||||||
|
## Determines whether ``future`` has completed.
|
||||||
|
##
|
||||||
|
## ``True`` may indicate an error or a value. Use ``failed`` to distinguish.
|
||||||
|
future.finished
|
||||||
|
|
||||||
|
proc failed*[T](future: PFuture[T]): bool =
|
||||||
|
## Determines whether ``future`` completed with an error.
|
||||||
|
future.error != nil
|
||||||
|
|
||||||
|
when defined(windows) or defined(nimdoc):
|
||||||
|
import winlean, sets, hashes
|
||||||
|
type
|
||||||
|
TCompletionKey = dword
|
||||||
|
|
||||||
|
TCompletionData* = object
|
||||||
|
sock: TAsyncFD
|
||||||
|
cb: proc (sock: TAsyncFD, bytesTransferred: DWORD,
|
||||||
|
errcode: TOSErrorCode) {.closure.}
|
||||||
|
|
||||||
|
PDispatcher* = ref object
|
||||||
|
ioPort: THandle
|
||||||
|
handles: TSet[TAsyncFD]
|
||||||
|
|
||||||
|
TCustomOverlapped = object
|
||||||
|
Internal*: DWORD
|
||||||
|
InternalHigh*: DWORD
|
||||||
|
Offset*: DWORD
|
||||||
|
OffsetHigh*: DWORD
|
||||||
|
hEvent*: THANDLE
|
||||||
|
data*: TCompletionData
|
||||||
|
|
||||||
|
PCustomOverlapped = ptr TCustomOverlapped
|
||||||
|
|
||||||
|
TAsyncFD* = distinct int
|
||||||
|
|
||||||
|
proc hash(x: TAsyncFD): THash {.borrow.}
|
||||||
|
proc `==`*(x: TAsyncFD, y: TAsyncFD): bool {.borrow.}
|
||||||
|
|
||||||
|
proc newDispatcher*(): PDispatcher =
|
||||||
|
## Creates a new Dispatcher instance.
|
||||||
|
new result
|
||||||
|
result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
|
||||||
|
result.handles = initSet[TAsyncFD]()
|
||||||
|
|
||||||
|
var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
|
||||||
|
proc getGlobalDispatcher*(): PDispatcher =
|
||||||
|
## Retrieves the global thread-local dispatcher.
|
||||||
|
if gDisp.isNil: gDisp = newDispatcher()
|
||||||
|
result = gDisp
|
||||||
|
|
||||||
|
proc register*(sock: TAsyncFD) =
|
||||||
|
## Registers ``sock`` with the dispatcher.
|
||||||
|
let p = getGlobalDispatcher()
|
||||||
|
if CreateIOCompletionPort(sock.THandle, p.ioPort,
|
||||||
|
cast[TCompletionKey](sock), 1) == 0:
|
||||||
|
OSError(OSLastError())
|
||||||
|
p.handles.incl(sock)
|
||||||
|
|
||||||
|
proc verifyPresence(sock: TAsyncFD) =
|
||||||
|
## Ensures that socket has been registered with the dispatcher.
|
||||||
|
let p = getGlobalDispatcher()
|
||||||
|
if sock notin p.handles:
|
||||||
|
raise newException(EInvalidValue,
|
||||||
|
"Operation performed on a socket which has not been registered with" &
|
||||||
|
" the dispatcher yet.")
|
||||||
|
|
||||||
|
proc poll*(timeout = 500) =
|
||||||
|
## Waits for completion events and processes them.
|
||||||
|
let p = getGlobalDispatcher()
|
||||||
|
if p.handles.len == 0:
|
||||||
|
raise newException(EInvalidValue, "No handles registered in dispatcher.")
|
||||||
|
|
||||||
|
let llTimeout =
|
||||||
|
if timeout == -1: winlean.INFINITE
|
||||||
|
else: timeout.int32
|
||||||
|
var lpNumberOfBytesTransferred: DWORD
|
||||||
|
var lpCompletionKey: ULONG
|
||||||
|
var lpOverlapped: POverlapped
|
||||||
|
let res = GetQueuedCompletionStatus(p.ioPort, addr lpNumberOfBytesTransferred,
|
||||||
|
addr lpCompletionKey, addr lpOverlapped, llTimeout).bool
|
||||||
|
|
||||||
|
# http://stackoverflow.com/a/12277264/492186
|
||||||
|
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
|
||||||
|
var customOverlapped = cast[PCustomOverlapped](lpOverlapped)
|
||||||
|
if res:
|
||||||
|
# This is useful for ensuring the reliability of the overlapped struct.
|
||||||
|
assert customOverlapped.data.sock == lpCompletionKey.TAsyncFD
|
||||||
|
|
||||||
|
customOverlapped.data.cb(customOverlapped.data.sock,
|
||||||
|
lpNumberOfBytesTransferred, TOSErrorCode(-1))
|
||||||
|
dealloc(customOverlapped)
|
||||||
|
else:
|
||||||
|
let errCode = OSLastError()
|
||||||
|
if lpOverlapped != nil:
|
||||||
|
assert customOverlapped.data.sock == lpCompletionKey.TAsyncFD
|
||||||
|
customOverlapped.data.cb(customOverlapped.data.sock,
|
||||||
|
lpNumberOfBytesTransferred, errCode)
|
||||||
|
dealloc(customOverlapped)
|
||||||
|
else:
|
||||||
|
if errCode.int32 == WAIT_TIMEOUT:
|
||||||
|
# Timed out
|
||||||
|
discard
|
||||||
|
else: OSError(errCode)
|
||||||
|
|
||||||
|
var connectExPtr: pointer = nil
|
||||||
|
var acceptExPtr: pointer = nil
|
||||||
|
var getAcceptExSockAddrsPtr: pointer = nil
|
||||||
|
|
||||||
|
proc initPointer(s: TSocketHandle, func: var pointer, guid: var TGUID): bool =
|
||||||
|
# Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
|
||||||
|
var bytesRet: DWord
|
||||||
|
func = nil
|
||||||
|
result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
|
||||||
|
sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD,
|
||||||
|
addr bytesRet, nil, nil) == 0
|
||||||
|
|
||||||
|
proc initAll() =
|
||||||
|
let dummySock = newRawSocket()
|
||||||
|
if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX):
|
||||||
|
OSError(OSLastError())
|
||||||
|
if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX):
|
||||||
|
OSError(OSLastError())
|
||||||
|
if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
|
||||||
|
OSError(OSLastError())
|
||||||
|
|
||||||
|
proc connectEx(s: TSocketHandle, name: ptr TSockAddr, namelen: cint,
|
||||||
|
lpSendBuffer: pointer, dwSendDataLength: dword,
|
||||||
|
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool =
|
||||||
|
if connectExPtr.isNil: raise newException(EInvalidValue, "Need to initialise ConnectEx().")
|
||||||
|
let func =
|
||||||
|
cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint,
|
||||||
|
lpSendBuffer: pointer, dwSendDataLength: dword,
|
||||||
|
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall.}](connectExPtr)
|
||||||
|
|
||||||
|
result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent,
|
||||||
|
lpOverlapped)
|
||||||
|
|
||||||
|
proc acceptEx(listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
|
||||||
|
dwReceiveDataLength, dwLocalAddressLength,
|
||||||
|
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
|
||||||
|
lpOverlapped: POverlapped): bool =
|
||||||
|
if acceptExPtr.isNil: raise newException(EInvalidValue, "Need to initialise AcceptEx().")
|
||||||
|
let func =
|
||||||
|
cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
|
||||||
|
dwReceiveDataLength, dwLocalAddressLength,
|
||||||
|
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
|
||||||
|
lpOverlapped: POverlapped): bool {.stdcall.}](acceptExPtr)
|
||||||
|
result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength,
|
||||||
|
dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
|
||||||
|
lpOverlapped)
|
||||||
|
|
||||||
|
proc getAcceptExSockaddrs(lpOutputBuffer: pointer,
|
||||||
|
dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: DWORD,
|
||||||
|
LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint,
|
||||||
|
RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) =
|
||||||
|
if getAcceptExSockAddrsPtr.isNil:
|
||||||
|
raise newException(EInvalidValue, "Need to initialise getAcceptExSockAddrs().")
|
||||||
|
|
||||||
|
let func =
|
||||||
|
cast[proc (lpOutputBuffer: pointer,
|
||||||
|
dwReceiveDataLength, dwLocalAddressLength,
|
||||||
|
dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr,
|
||||||
|
LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr,
|
||||||
|
RemoteSockaddrLength: lpint) {.stdcall.}](getAcceptExSockAddrsPtr)
|
||||||
|
|
||||||
|
func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
|
||||||
|
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
|
||||||
|
RemoteSockaddr, RemoteSockaddrLength)
|
||||||
|
|
||||||
|
proc connect*(socket: TAsyncFD, address: string, port: TPort,
|
||||||
|
af = AF_INET): PFuture[void] =
|
||||||
|
## Connects ``socket`` to server at ``address:port``.
|
||||||
|
##
|
||||||
|
## Returns a ``PFuture`` which will complete when the connection succeeds
|
||||||
|
## or an error occurs.
|
||||||
|
verifyPresence(socket)
|
||||||
|
var retFuture = newFuture[void]()
|
||||||
|
# Apparently ``ConnectEx`` expects the socket to be initially bound:
|
||||||
|
var saddr: Tsockaddr_in
|
||||||
|
saddr.sin_family = int16(toInt(af))
|
||||||
|
saddr.sin_port = 0
|
||||||
|
saddr.sin_addr.s_addr = INADDR_ANY
|
||||||
|
if bindAddr(socket.TSocketHandle, cast[ptr TSockAddr](addr(saddr)),
|
||||||
|
sizeof(saddr).TSockLen) < 0'i32:
|
||||||
|
OSError(OSLastError())
|
||||||
|
|
||||||
|
var aiList = getAddrInfo(address, port, af)
|
||||||
|
var success = false
|
||||||
|
var lastError: TOSErrorCode
|
||||||
|
var it = aiList
|
||||||
|
while it != nil:
|
||||||
|
# "the OVERLAPPED structure must remain valid until the I/O completes"
|
||||||
|
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
|
||||||
|
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
||||||
|
ol.data = TCompletionData(sock: socket, cb:
|
||||||
|
proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||||
|
if not retFuture.finished:
|
||||||
|
if errcode == TOSErrorCode(-1):
|
||||||
|
retFuture.complete()
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||||
|
)
|
||||||
|
|
||||||
|
var ret = connectEx(socket.TSocketHandle, it.ai_addr,
|
||||||
|
sizeof(TSockAddrIn).cint, nil, 0, nil,
|
||||||
|
cast[POverlapped](ol))
|
||||||
|
if ret:
|
||||||
|
# Request to connect completed immediately.
|
||||||
|
success = true
|
||||||
|
retFuture.complete()
|
||||||
|
# We don't deallocate ``ol`` here because even though this completed
|
||||||
|
# immediately poll will still be notified about its completion and it will
|
||||||
|
# free ``ol``.
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
lastError = OSLastError()
|
||||||
|
if lastError.int32 == ERROR_IO_PENDING:
|
||||||
|
# In this case ``ol`` will be deallocated in ``poll``.
|
||||||
|
success = true
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
dealloc(ol)
|
||||||
|
success = false
|
||||||
|
it = it.ai_next
|
||||||
|
|
||||||
|
dealloc(aiList)
|
||||||
|
if not success:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc recv*(socket: TAsyncFD, size: int,
|
||||||
|
flags: int = 0): PFuture[string] =
|
||||||
|
## Reads ``size`` bytes from ``socket``. Returned future will complete once
|
||||||
|
## all of the requested data is read. If socket is disconnected during the
|
||||||
|
## recv operation then the future may complete with only a part of the
|
||||||
|
## requested data read. If socket is disconnected and no data is available
|
||||||
|
## to be read then the future will complete with a value of ``""``.
|
||||||
|
verifyPresence(socket)
|
||||||
|
var retFuture = newFuture[string]()
|
||||||
|
|
||||||
|
var dataBuf: TWSABuf
|
||||||
|
dataBuf.buf = newString(size)
|
||||||
|
dataBuf.len = size
|
||||||
|
|
||||||
|
var bytesReceived: DWord
|
||||||
|
var flagsio = flags.dword
|
||||||
|
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
||||||
|
ol.data = TCompletionData(sock: socket, cb:
|
||||||
|
proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||||
|
if not retFuture.finished:
|
||||||
|
if errcode == TOSErrorCode(-1):
|
||||||
|
if bytesCount == 0 and dataBuf.buf[0] == '\0':
|
||||||
|
retFuture.complete("")
|
||||||
|
else:
|
||||||
|
var data = newString(size)
|
||||||
|
copyMem(addr data[0], addr dataBuf.buf[0], size)
|
||||||
|
retFuture.complete($data)
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||||
|
)
|
||||||
|
|
||||||
|
let ret = WSARecv(socket.TSocketHandle, addr dataBuf, 1, addr bytesReceived,
|
||||||
|
addr flagsio, cast[POverlapped](ol), nil)
|
||||||
|
if ret == -1:
|
||||||
|
let err = OSLastError()
|
||||||
|
if err.int32 != ERROR_IO_PENDING:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(err)))
|
||||||
|
dealloc(ol)
|
||||||
|
elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0':
|
||||||
|
# We have to ensure that the buffer is empty because WSARecv will tell
|
||||||
|
# us immediatelly when it was disconnected, even when there is still
|
||||||
|
# data in the buffer.
|
||||||
|
# We want to give the user as much data as we can. So we only return
|
||||||
|
# the empty string (which signals a disconnection) when there is
|
||||||
|
# nothing left to read.
|
||||||
|
retFuture.complete("")
|
||||||
|
# TODO: "For message-oriented sockets, where a zero byte message is often
|
||||||
|
# allowable, a failure with an error code of WSAEDISCON is used to
|
||||||
|
# indicate graceful closure."
|
||||||
|
# ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx
|
||||||
|
else:
|
||||||
|
# Request to read completed immediately.
|
||||||
|
var data = newString(size)
|
||||||
|
copyMem(addr data[0], addr dataBuf.buf[0], size)
|
||||||
|
retFuture.complete($data)
|
||||||
|
# We don't deallocate ``ol`` here because even though this completed
|
||||||
|
# immediately poll will still be notified about its completion and it will
|
||||||
|
# free ``ol``.
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc send*(socket: TAsyncFD, data: string): PFuture[void] =
|
||||||
|
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||||
|
## data has been sent.
|
||||||
|
verifyPresence(socket)
|
||||||
|
var retFuture = newFuture[void]()
|
||||||
|
|
||||||
|
var dataBuf: TWSABuf
|
||||||
|
dataBuf.buf = data
|
||||||
|
dataBuf.len = data.len
|
||||||
|
|
||||||
|
var bytesReceived, flags: DWord
|
||||||
|
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
||||||
|
ol.data = TCompletionData(sock: socket, cb:
|
||||||
|
proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||||
|
if not retFuture.finished:
|
||||||
|
if errcode == TOSErrorCode(-1):
|
||||||
|
retFuture.complete()
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||||
|
)
|
||||||
|
|
||||||
|
let ret = WSASend(socket.TSocketHandle, addr dataBuf, 1, addr bytesReceived,
|
||||||
|
flags, cast[POverlapped](ol), nil)
|
||||||
|
if ret == -1:
|
||||||
|
let err = osLastError()
|
||||||
|
if err.int32 != ERROR_IO_PENDING:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(err)))
|
||||||
|
dealloc(ol)
|
||||||
|
else:
|
||||||
|
retFuture.complete()
|
||||||
|
# We don't deallocate ``ol`` here because even though this completed
|
||||||
|
# immediately poll will still be notified about its completion and it will
|
||||||
|
# free ``ol``.
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc acceptAddr*(socket: TAsyncFD):
|
||||||
|
PFuture[tuple[address: string, client: TAsyncFD]] =
|
||||||
|
## Accepts a new connection. Returns a future containing the client socket
|
||||||
|
## corresponding to that connection and the remote address of the client.
|
||||||
|
## The future will complete when the connection is successfully accepted.
|
||||||
|
##
|
||||||
|
## The resulting client socket is automatically registered to dispatcher.
|
||||||
|
verifyPresence(socket)
|
||||||
|
var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]()
|
||||||
|
|
||||||
|
var clientSock = newRawSocket()
|
||||||
|
if clientSock == OSInvalidSocket: osError(osLastError())
|
||||||
|
|
||||||
|
const lpOutputLen = 1024
|
||||||
|
var lpOutputBuf = newString(lpOutputLen)
|
||||||
|
var dwBytesReceived: DWORD
|
||||||
|
let dwReceiveDataLength = 0.DWORD # We don't want any data to be read.
|
||||||
|
let dwLocalAddressLength = DWORD(sizeof (TSockaddr_in) + 16)
|
||||||
|
let dwRemoteAddressLength = DWORD(sizeof(TSockaddr_in) + 16)
|
||||||
|
|
||||||
|
template completeAccept(): stmt {.immediate, dirty.} =
|
||||||
|
var listenSock = socket
|
||||||
|
let setoptRet = setsockopt(clientSock, SOL_SOCKET,
|
||||||
|
SO_UPDATE_ACCEPT_CONTEXT, addr listenSock,
|
||||||
|
sizeof(listenSock).TSockLen)
|
||||||
|
if setoptRet != 0: osError(osLastError())
|
||||||
|
|
||||||
|
var LocalSockaddr, RemoteSockaddr: ptr TSockAddr
|
||||||
|
var localLen, remoteLen: int32
|
||||||
|
getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength,
|
||||||
|
dwLocalAddressLength, dwRemoteAddressLength,
|
||||||
|
addr LocalSockaddr, addr localLen,
|
||||||
|
addr RemoteSockaddr, addr remoteLen)
|
||||||
|
register(clientSock.TAsyncFD)
|
||||||
|
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
|
||||||
|
retFuture.complete(
|
||||||
|
(address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr),
|
||||||
|
client: clientSock.TAsyncFD)
|
||||||
|
)
|
||||||
|
|
||||||
|
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
||||||
|
ol.data = TCompletionData(sock: socket, cb:
|
||||||
|
proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) =
|
||||||
|
if not retFuture.finished:
|
||||||
|
if errcode == TOSErrorCode(-1):
|
||||||
|
completeAccept()
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
||||||
|
)
|
||||||
|
|
||||||
|
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
|
||||||
|
let ret = acceptEx(socket.TSocketHandle, clientSock, addr lpOutputBuf[0],
|
||||||
|
dwReceiveDataLength,
|
||||||
|
dwLocalAddressLength,
|
||||||
|
dwRemoteAddressLength,
|
||||||
|
addr dwBytesReceived, cast[POverlapped](ol))
|
||||||
|
|
||||||
|
if not ret:
|
||||||
|
let err = osLastError()
|
||||||
|
if err.int32 != ERROR_IO_PENDING:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(err)))
|
||||||
|
dealloc(ol)
|
||||||
|
else:
|
||||||
|
completeAccept()
|
||||||
|
# We don't deallocate ``ol`` here because even though this completed
|
||||||
|
# immediately poll will still be notified about its completion and it will
|
||||||
|
# free ``ol``.
|
||||||
|
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc newAsyncRawSocket*(domain: TDomain = AF_INET,
|
||||||
|
typ: TType = SOCK_STREAM,
|
||||||
|
protocol: TProtocol = IPPROTO_TCP): TAsyncFD =
|
||||||
|
## Creates a new socket and registers it with the dispatcher implicitly.
|
||||||
|
result = newRawSocket(domain, typ, protocol).TAsyncFD
|
||||||
|
result.TSocketHandle.setBlocking(false)
|
||||||
|
register(result)
|
||||||
|
|
||||||
|
proc close*(socket: TAsyncFD) =
|
||||||
|
## Closes a socket and ensures that it is unregistered.
|
||||||
|
socket.TSocketHandle.close()
|
||||||
|
getGlobalDispatcher().handles.excl(socket)
|
||||||
|
|
||||||
|
initAll()
|
||||||
|
else:
|
||||||
|
import selectors
|
||||||
|
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK
|
||||||
|
type
|
||||||
|
TAsyncFD* = distinct cint
|
||||||
|
TCallback = proc (sock: TAsyncFD): bool {.closure.}
|
||||||
|
|
||||||
|
PData* = ref object of PObject
|
||||||
|
sock: TAsyncFD
|
||||||
|
readCBs: seq[TCallback]
|
||||||
|
writeCBs: seq[TCallback]
|
||||||
|
|
||||||
|
PDispatcher* = ref object
|
||||||
|
selector: PSelector
|
||||||
|
|
||||||
|
proc `==`*(x, y: TAsyncFD): bool {.borrow.}
|
||||||
|
|
||||||
|
proc newDispatcher*(): PDispatcher =
|
||||||
|
new result
|
||||||
|
result.selector = newSelector()
|
||||||
|
|
||||||
|
var gDisp{.threadvar.}: PDispatcher ## Global dispatcher
|
||||||
|
proc getGlobalDispatcher*(): PDispatcher =
|
||||||
|
if gDisp.isNil: gDisp = newDispatcher()
|
||||||
|
result = gDisp
|
||||||
|
|
||||||
|
proc update(sock: TAsyncFD, events: set[TEvent]) =
|
||||||
|
let p = getGlobalDispatcher()
|
||||||
|
assert sock.TSocketHandle in p.selector
|
||||||
|
discard p.selector.update(sock.TSocketHandle, events)
|
||||||
|
|
||||||
|
proc register(sock: TAsyncFD) =
|
||||||
|
let p = getGlobalDispatcher()
|
||||||
|
var data = PData(sock: sock, readCBs: @[], writeCBs: @[])
|
||||||
|
p.selector.register(sock.TSocketHandle, {}, data.PObject)
|
||||||
|
|
||||||
|
proc newAsyncRawSocket*(domain: TDomain = AF_INET,
|
||||||
|
typ: TType = SOCK_STREAM,
|
||||||
|
protocol: TProtocol = IPPROTO_TCP): TAsyncFD =
|
||||||
|
result = newRawSocket(domain, typ, protocol).TAsyncFD
|
||||||
|
result.TSocketHandle.setBlocking(false)
|
||||||
|
register(result)
|
||||||
|
|
||||||
|
proc close*(sock: TAsyncFD) =
|
||||||
|
let disp = getGlobalDispatcher()
|
||||||
|
sock.TSocketHandle.close()
|
||||||
|
disp.selector.unregister(sock.TSocketHandle)
|
||||||
|
|
||||||
|
proc addRead(sock: TAsyncFD, cb: TCallback) =
|
||||||
|
let p = getGlobalDispatcher()
|
||||||
|
if sock.TSocketHandle notin p.selector:
|
||||||
|
raise newException(EInvalidValue, "File descriptor not registered.")
|
||||||
|
p.selector[sock.TSocketHandle].data.PData.readCBs.add(cb)
|
||||||
|
update(sock, p.selector[sock.TSocketHandle].events + {EvRead})
|
||||||
|
|
||||||
|
proc addWrite(sock: TAsyncFD, cb: TCallback) =
|
||||||
|
let p = getGlobalDispatcher()
|
||||||
|
if sock.TSocketHandle notin p.selector:
|
||||||
|
raise newException(EInvalidValue, "File descriptor not registered.")
|
||||||
|
p.selector[sock.TSocketHandle].data.PData.writeCBs.add(cb)
|
||||||
|
update(sock, p.selector[sock.TSocketHandle].events + {EvWrite})
|
||||||
|
|
||||||
|
proc poll*(timeout = 500) =
|
||||||
|
let p = getGlobalDispatcher()
|
||||||
|
for info in p.selector.select(timeout):
|
||||||
|
let data = PData(info.key.data)
|
||||||
|
assert data.sock == info.key.fd.TAsyncFD
|
||||||
|
#echo("In poll ", data.sock.cint)
|
||||||
|
if EvRead in info.events:
|
||||||
|
# Callback may add items to ``data.readCBs`` which causes issues if
|
||||||
|
# we are iterating over ``data.readCBs`` at the same time. We therefore
|
||||||
|
# make a copy to iterate over.
|
||||||
|
let currentCBs = data.readCBs
|
||||||
|
data.readCBs = @[]
|
||||||
|
for cb in currentCBs:
|
||||||
|
if not cb(data.sock):
|
||||||
|
# Callback wants to be called again.
|
||||||
|
data.readCBs.add(cb)
|
||||||
|
|
||||||
|
if EvWrite in info.events:
|
||||||
|
let currentCBs = data.writeCBs
|
||||||
|
data.writeCBs = @[]
|
||||||
|
for cb in currentCBs:
|
||||||
|
if not cb(data.sock):
|
||||||
|
# Callback wants to be called again.
|
||||||
|
data.writeCBs.add(cb)
|
||||||
|
|
||||||
|
if info.key in p.selector:
|
||||||
|
var newEvents: set[TEvent]
|
||||||
|
if data.readCBs.len != 0: newEvents = {EvRead}
|
||||||
|
if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite}
|
||||||
|
if newEvents != info.key.events:
|
||||||
|
update(data.sock, newEvents)
|
||||||
|
else:
|
||||||
|
# FD no longer a part of the selector. Likely been closed
|
||||||
|
# (e.g. socket disconnected).
|
||||||
|
|
||||||
|
proc connect*(socket: TAsyncFD, address: string, port: TPort,
|
||||||
|
af = AF_INET): PFuture[void] =
|
||||||
|
var retFuture = newFuture[void]()
|
||||||
|
|
||||||
|
proc cb(sock: TAsyncFD): bool =
|
||||||
|
# We have connected.
|
||||||
|
retFuture.complete()
|
||||||
|
return true
|
||||||
|
|
||||||
|
var aiList = getAddrInfo(address, port, af)
|
||||||
|
var success = false
|
||||||
|
var lastError: TOSErrorCode
|
||||||
|
var it = aiList
|
||||||
|
while it != nil:
|
||||||
|
var ret = connect(socket.TSocketHandle, it.ai_addr, it.ai_addrlen.TSocklen)
|
||||||
|
if ret == 0:
|
||||||
|
# Request to connect completed immediately.
|
||||||
|
success = true
|
||||||
|
retFuture.complete()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
lastError = osLastError()
|
||||||
|
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
||||||
|
success = true
|
||||||
|
addWrite(socket, cb)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
success = false
|
||||||
|
it = it.ai_next
|
||||||
|
|
||||||
|
dealloc(aiList)
|
||||||
|
if not success:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc recv*(socket: TAsyncFD, size: int,
|
||||||
|
flags: int = 0): PFuture[string] =
|
||||||
|
var retFuture = newFuture[string]()
|
||||||
|
|
||||||
|
var readBuffer = newString(size)
|
||||||
|
var sizeRead = 0
|
||||||
|
|
||||||
|
proc cb(sock: TAsyncFD): bool =
|
||||||
|
result = true
|
||||||
|
let netSize = size - sizeRead
|
||||||
|
let res = recv(sock.TSocketHandle, addr readBuffer[sizeRead], netSize,
|
||||||
|
flags.cint)
|
||||||
|
#echo("recv cb res: ", res)
|
||||||
|
if res < 0:
|
||||||
|
let lastError = osLastError()
|
||||||
|
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||||
|
else:
|
||||||
|
result = false # We still want this callback to be called.
|
||||||
|
elif res == 0:
|
||||||
|
#echo("Disconnected recv: ", sizeRead)
|
||||||
|
# Disconnected
|
||||||
|
if sizeRead == 0:
|
||||||
|
retFuture.complete("")
|
||||||
|
else:
|
||||||
|
readBuffer.setLen(sizeRead)
|
||||||
|
retFuture.complete(readBuffer)
|
||||||
|
else:
|
||||||
|
sizeRead.inc(res)
|
||||||
|
if res != netSize:
|
||||||
|
result = false # We want to read all the data requested.
|
||||||
|
else:
|
||||||
|
retFuture.complete(readBuffer)
|
||||||
|
#echo("Recv cb result: ", result)
|
||||||
|
|
||||||
|
addRead(socket, cb)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc send*(socket: TAsyncFD, data: string): PFuture[void] =
|
||||||
|
var retFuture = newFuture[void]()
|
||||||
|
|
||||||
|
var written = 0
|
||||||
|
|
||||||
|
proc cb(sock: TAsyncFD): bool =
|
||||||
|
result = true
|
||||||
|
let netSize = data.len-written
|
||||||
|
var d = data.cstring
|
||||||
|
let res = send(sock.TSocketHandle, addr d[written], netSize, 0.cint)
|
||||||
|
if res < 0:
|
||||||
|
let lastError = osLastError()
|
||||||
|
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||||
|
else:
|
||||||
|
result = false # We still want this callback to be called.
|
||||||
|
else:
|
||||||
|
written.inc(res)
|
||||||
|
if res != netSize:
|
||||||
|
result = false # We still have data to send.
|
||||||
|
else:
|
||||||
|
retFuture.complete()
|
||||||
|
addWrite(socket, cb)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc acceptAddr*(socket: TAsyncFD):
|
||||||
|
PFuture[tuple[address: string, client: TAsyncFD]] =
|
||||||
|
var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]()
|
||||||
|
proc cb(sock: TAsyncFD): bool =
|
||||||
|
result = true
|
||||||
|
var sockAddress: Tsockaddr_in
|
||||||
|
var addrLen = sizeof(sockAddress).TSocklen
|
||||||
|
var client = accept(sock.TSocketHandle,
|
||||||
|
cast[ptr TSockAddr](addr(sockAddress)), addr(addrLen))
|
||||||
|
if client == osInvalidSocket:
|
||||||
|
let lastError = osLastError()
|
||||||
|
assert lastError.int32 notin {EWOULDBLOCK, EAGAIN}
|
||||||
|
if lastError.int32 == EINTR:
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
||||||
|
else:
|
||||||
|
register(client.TAsyncFD)
|
||||||
|
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD))
|
||||||
|
addRead(socket, cb)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc accept*(socket: TAsyncFD): PFuture[TAsyncFD] =
|
||||||
|
## Accepts a new connection. Returns a future containing the client socket
|
||||||
|
## corresponding to that connection.
|
||||||
|
## The future will complete when the connection is successfully accepted.
|
||||||
|
var retFut = newFuture[TAsyncFD]()
|
||||||
|
var fut = acceptAddr(socket)
|
||||||
|
fut.callback =
|
||||||
|
proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) =
|
||||||
|
assert future.finished
|
||||||
|
if future.failed:
|
||||||
|
retFut.fail(future.error)
|
||||||
|
else:
|
||||||
|
retFut.complete(future.read.client)
|
||||||
|
return retFut
|
||||||
|
|
||||||
|
# -- Await Macro
|
||||||
|
|
||||||
|
template createCb*(cbName, varNameIterSym, retFutureSym: expr): stmt {.immediate, dirty.} =
|
||||||
|
proc cbName {.closure.} =
|
||||||
|
if not varNameIterSym.finished:
|
||||||
|
var next = varNameIterSym()
|
||||||
|
if next == nil:
|
||||||
|
assert retFutureSym.finished, "Async procedure's return Future was not finished."
|
||||||
|
else:
|
||||||
|
next.callback = cbName
|
||||||
|
|
||||||
|
template createVar(futSymName: string, asyncProc: PNimrodNode,
|
||||||
|
valueReceiver: expr) {.immediate, dirty.} =
|
||||||
|
# TODO: Used template here due to bug #926
|
||||||
|
result = newNimNode(nnkStmtList)
|
||||||
|
var futSym = genSym(nskVar, "future")
|
||||||
|
result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
|
||||||
|
result.add newNimNode(nnkYieldStmt).add(futSym) # -> yield future<x>
|
||||||
|
valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future<x>.read
|
||||||
|
|
||||||
|
proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
result = node
|
||||||
|
case node.kind
|
||||||
|
of nnkReturnStmt:
|
||||||
|
result = newNimNode(nnkStmtList)
|
||||||
|
result.add newCall(newIdentNode("complete"), retFutureSym,
|
||||||
|
if node[0].kind == nnkEmpty: newIdentNode("result") else: node[0])
|
||||||
|
result.add newNimNode(nnkYieldStmt).add(newNilLit())
|
||||||
|
of nnkCommand:
|
||||||
|
if node[0].kind == nnkIdent and node[0].ident == !"await":
|
||||||
|
case node[1].kind
|
||||||
|
of nnkIdent:
|
||||||
|
# await x
|
||||||
|
result = newNimNode(nnkYieldStmt).add(node[1]) # -> yield x
|
||||||
|
of nnkCall:
|
||||||
|
# await foo(p, x)
|
||||||
|
var futureValue: PNimrodNode
|
||||||
|
createVar("future" & $node[1][0].toStrLit, node[1], futureValue)
|
||||||
|
result.add futureValue
|
||||||
|
else:
|
||||||
|
error("Invalid node kind in 'await', got: " & $node[1].kind)
|
||||||
|
elif node[1].kind == nnkCommand and node[1][0].kind == nnkIdent and
|
||||||
|
node[1][0].ident == !"await":
|
||||||
|
# foo await x
|
||||||
|
var newCommand = node
|
||||||
|
createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1])
|
||||||
|
result.add newCommand
|
||||||
|
|
||||||
|
of nnkVarSection, nnkLetSection:
|
||||||
|
case node[0][2].kind
|
||||||
|
of nnkCommand:
|
||||||
|
if node[0][2][0].ident == !"await":
|
||||||
|
# var x = await y
|
||||||
|
var newVarSection = node # TODO: Should this use copyNimNode?
|
||||||
|
createVar("future" & $node[0][0].ident, node[0][2][1],
|
||||||
|
newVarSection[0][2])
|
||||||
|
result.add newVarSection
|
||||||
|
else: discard
|
||||||
|
of nnkAsgn:
|
||||||
|
case node[1].kind
|
||||||
|
of nnkCommand:
|
||||||
|
if node[1][0].ident == !"await":
|
||||||
|
# x = await y
|
||||||
|
var newAsgn = node
|
||||||
|
createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1])
|
||||||
|
result.add newAsgn
|
||||||
|
else: discard
|
||||||
|
of nnkDiscardStmt:
|
||||||
|
# discard await x
|
||||||
|
if node[0][0].kind == nnkIdent and node[0][0].ident == !"await":
|
||||||
|
var dummy = newNimNode(nnkStmtList)
|
||||||
|
createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy)
|
||||||
|
else: discard
|
||||||
|
|
||||||
|
for i in 0 .. <result.len:
|
||||||
|
result[i] = processBody(result[i], retFutureSym)
|
||||||
|
#echo(treeRepr(result))
|
||||||
|
|
||||||
|
proc getName(node: PNimrodNode): string {.compileTime.} =
|
||||||
|
case node.kind
|
||||||
|
of nnkPostfix:
|
||||||
|
return $node[1].ident
|
||||||
|
of nnkIdent:
|
||||||
|
return $node.ident
|
||||||
|
else:
|
||||||
|
assert false
|
||||||
|
|
||||||
|
macro async*(prc: stmt): stmt {.immediate.} =
|
||||||
|
## Macro which processes async procedures into the appropriate
|
||||||
|
## iterators and yield statements.
|
||||||
|
|
||||||
|
expectKind(prc, nnkProcDef)
|
||||||
|
|
||||||
|
hint("Processing " & prc[0].getName & " as an async proc.")
|
||||||
|
|
||||||
|
let returnType = prc[3][0]
|
||||||
|
var subtypeName = ""
|
||||||
|
# Verify that the return type is a PFuture[T]
|
||||||
|
if returnType.kind == nnkIdent:
|
||||||
|
error("Expected return type of 'PFuture' got '" & $returnType & "'")
|
||||||
|
elif returnType.kind == nnkBracketExpr:
|
||||||
|
if $returnType[0] != "PFuture":
|
||||||
|
error("Expected return type of 'PFuture' got '" & $returnType[0] & "'")
|
||||||
|
subtypeName = $returnType[1].ident
|
||||||
|
elif returnType.kind == nnkEmpty:
|
||||||
|
subtypeName = "void"
|
||||||
|
|
||||||
|
var outerProcBody = newNimNode(nnkStmtList)
|
||||||
|
|
||||||
|
# -> var retFuture = newFuture[T]()
|
||||||
|
var retFutureSym = genSym(nskVar, "retFuture")
|
||||||
|
outerProcBody.add(
|
||||||
|
newVarStmt(retFutureSym,
|
||||||
|
newCall(
|
||||||
|
newNimNode(nnkBracketExpr).add(
|
||||||
|
newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
|
||||||
|
newIdentNode(subtypeName))))) # Get type from return type of this proc
|
||||||
|
|
||||||
|
# -> iterator nameIter(): PFutureBase {.closure.} =
|
||||||
|
# -> var result: T
|
||||||
|
# -> <proc_body>
|
||||||
|
# -> complete(retFuture, result)
|
||||||
|
var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter")
|
||||||
|
var procBody = prc[6].processBody(retFutureSym)
|
||||||
|
if subtypeName != "void":
|
||||||
|
procBody.insert(0, newNimNode(nnkVarSection).add(
|
||||||
|
newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T
|
||||||
|
procBody.add(
|
||||||
|
newCall(newIdentNode("complete"),
|
||||||
|
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
|
||||||
|
else:
|
||||||
|
# -> complete(retFuture)
|
||||||
|
procBody.add(newCall(newIdentNode("complete"), retFutureSym))
|
||||||
|
|
||||||
|
var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")],
|
||||||
|
procBody, nnkIteratorDef)
|
||||||
|
closureIterator[4] = newNimNode(nnkPragma).add(newIdentNode("closure"))
|
||||||
|
outerProcBody.add(closureIterator)
|
||||||
|
|
||||||
|
# -> var nameIterVar = nameIter
|
||||||
|
# -> var first = nameIterVar()
|
||||||
|
var varNameIterSym = genSym(nskVar, $prc[0].getName & "IterVar")
|
||||||
|
var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym)
|
||||||
|
outerProcBody.add varNameIter
|
||||||
|
var varFirstSym = genSym(nskVar, "first")
|
||||||
|
var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym))
|
||||||
|
outerProcBody.add varFirst
|
||||||
|
|
||||||
|
# -> createCb(cb, nameIter, retFuture)
|
||||||
|
var cbName = newIdentNode("cb")
|
||||||
|
var procCb = newCall("createCb", cbName, varNameIterSym, retFutureSym)
|
||||||
|
outerProcBody.add procCb
|
||||||
|
|
||||||
|
# -> first.callback = cb
|
||||||
|
outerProcBody.add newAssignment(
|
||||||
|
newDotExpr(varFirstSym, newIdentNode("callback")),
|
||||||
|
cbName)
|
||||||
|
|
||||||
|
# -> return retFuture
|
||||||
|
outerProcBody.add newNimNode(nnkReturnStmt).add(retFutureSym)
|
||||||
|
|
||||||
|
result = prc
|
||||||
|
|
||||||
|
# Remove the 'async' pragma.
|
||||||
|
for i in 0 .. <result[4].len:
|
||||||
|
if result[4][i].ident == !"async":
|
||||||
|
result[4].del(i)
|
||||||
|
if subtypeName == "void":
|
||||||
|
# Add discardable pragma.
|
||||||
|
result[4].add(newIdentNode("discardable"))
|
||||||
|
if returnType.kind == nnkEmpty:
|
||||||
|
# Add PFuture[void]
|
||||||
|
result[3][0] = parseExpr("PFuture[void]")
|
||||||
|
|
||||||
|
result[6] = outerProcBody
|
||||||
|
|
||||||
|
echo(toStrLit(result))
|
||||||
|
|
||||||
|
proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} =
|
||||||
|
## Reads a line of data from ``socket``. Returned future will complete once
|
||||||
|
## a full line is read or an error occurs.
|
||||||
|
##
|
||||||
|
## If a full line is read ``\r\L`` is not
|
||||||
|
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
||||||
|
## will be set to it.
|
||||||
|
##
|
||||||
|
## If the socket is disconnected, ``line`` will be set to ``""``.
|
||||||
|
##
|
||||||
|
## If the socket is disconnected in the middle of a line (before ``\r\L``
|
||||||
|
## is read) then line will be set to ``""``.
|
||||||
|
## The partial line **will be lost**.
|
||||||
|
|
||||||
|
template addNLIfEmpty(): stmt =
|
||||||
|
if result.len == 0:
|
||||||
|
result.add("\c\L")
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
var c = ""
|
||||||
|
while true:
|
||||||
|
c = await recv(socket, 1)
|
||||||
|
if c.len == 0:
|
||||||
|
return ""
|
||||||
|
if c == "\r":
|
||||||
|
c = await recv(socket, 1, MSG_PEEK)
|
||||||
|
if c.len > 0 and c == "\L":
|
||||||
|
discard await recv(socket, 1)
|
||||||
|
addNLIfEmpty()
|
||||||
|
return
|
||||||
|
elif c == "\L":
|
||||||
|
addNLIfEmpty()
|
||||||
|
return
|
||||||
|
add(result, c)
|
||||||
|
|
||||||
|
proc runForever*() =
|
||||||
|
## Begins a never ending global dispatcher poll loop.
|
||||||
|
while true:
|
||||||
|
poll()
|
||||||
3
lib/pure/asyncdispatch.nimrod.cfg
Normal file
3
lib/pure/asyncdispatch.nimrod.cfg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
@if nimdoc:
|
||||||
|
--os:linux
|
||||||
|
@end
|
||||||
|
|
@ -167,7 +167,7 @@ proc asyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||||
result = newAsyncSocket()
|
result = newAsyncSocket()
|
||||||
result.socket = socket(domain, typ, protocol, buffered)
|
result.socket = socket(domain, typ, protocol, buffered)
|
||||||
result.proto = protocol
|
result.proto = protocol
|
||||||
if result.socket == InvalidSocket: OSError(OSLastError())
|
if result.socket == invalidSocket: osError(osLastError())
|
||||||
result.socket.setBlocking(false)
|
result.socket.setBlocking(false)
|
||||||
|
|
||||||
proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket =
|
proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket =
|
||||||
|
|
@ -357,7 +357,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket,
|
||||||
client.sslNeedAccept = false
|
client.sslNeedAccept = false
|
||||||
client.info = SockConnected
|
client.info = SockConnected
|
||||||
|
|
||||||
if c == InvalidSocket: SocketError(server.socket)
|
if c == invalidSocket: socketError(server.socket)
|
||||||
c.setBlocking(false) # TODO: Needs to be tested.
|
c.setBlocking(false) # TODO: Needs to be tested.
|
||||||
|
|
||||||
# deleg.open is set in ``toDelegate``.
|
# deleg.open is set in ``toDelegate``.
|
||||||
|
|
@ -481,7 +481,7 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool {.deprecated.} =
|
||||||
of RecvDisconnected:
|
of RecvDisconnected:
|
||||||
result = true
|
result = true
|
||||||
of RecvFail:
|
of RecvFail:
|
||||||
s.SocketError(async = true)
|
s.socketError(async = true)
|
||||||
result = false
|
result = false
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
|
@ -615,11 +615,11 @@ proc poll*(d: PDispatcher, timeout: int = 500): bool =
|
||||||
if d.hasDataBuffered(d.deleVal):
|
if d.hasDataBuffered(d.deleVal):
|
||||||
hasDataBufferedCount.inc()
|
hasDataBufferedCount.inc()
|
||||||
d.handleRead(d.deleVal)
|
d.handleRead(d.deleVal)
|
||||||
if hasDataBufferedCount > 0: return True
|
if hasDataBufferedCount > 0: return true
|
||||||
|
|
||||||
if readDg.len() == 0 and writeDg.len() == 0:
|
if readDg.len() == 0 and writeDg.len() == 0:
|
||||||
## TODO: Perhaps this shouldn't return if errorDg has something?
|
## TODO: Perhaps this shouldn't return if errorDg has something?
|
||||||
return False
|
return false
|
||||||
|
|
||||||
if select(readDg, writeDg, errorDg, timeout) != 0:
|
if select(readDg, writeDg, errorDg, timeout) != 0:
|
||||||
for i in 0..len(d.delegates)-1:
|
for i in 0..len(d.delegates)-1:
|
||||||
|
|
|
||||||
|
|
@ -1,485 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nimrod's Runtime Library
|
|
||||||
# (c) Copyright 2014 Dominik Picheta
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
import os, oids, tables, strutils
|
|
||||||
|
|
||||||
import winlean
|
|
||||||
|
|
||||||
import sockets2, net
|
|
||||||
|
|
||||||
## Asyncio2
|
|
||||||
## --------
|
|
||||||
##
|
|
||||||
## This module implements a brand new asyncio module based on Futures.
|
|
||||||
## IOCP is used under the hood on Windows and the selectors module is used for
|
|
||||||
## other operating systems.
|
|
||||||
|
|
||||||
# -- Futures
|
|
||||||
|
|
||||||
type
|
|
||||||
PFutureVoid* = ref object of PObject
|
|
||||||
cbVoid: proc () {.closure.}
|
|
||||||
finished: bool
|
|
||||||
|
|
||||||
PFuture*[T] = ref object of PFutureVoid
|
|
||||||
value: T
|
|
||||||
error: ref EBase
|
|
||||||
cb: proc (future: PFuture[T]) {.closure.}
|
|
||||||
|
|
||||||
proc newFuture*[T](): PFuture[T] =
|
|
||||||
## Creates a new future.
|
|
||||||
new(result)
|
|
||||||
result.finished = false
|
|
||||||
|
|
||||||
proc complete*[T](future: PFuture[T], val: T) =
|
|
||||||
## Completes ``future`` with value ``val``.
|
|
||||||
assert(not future.finished)
|
|
||||||
assert(future.error == nil)
|
|
||||||
future.value = val
|
|
||||||
future.finished = true
|
|
||||||
if future.cb != nil:
|
|
||||||
future.cb(future)
|
|
||||||
if future.cbVoid != nil:
|
|
||||||
future.cbVoid()
|
|
||||||
|
|
||||||
proc fail*[T](future: PFuture[T], error: ref EBase) =
|
|
||||||
## Completes ``future`` with ``error``.
|
|
||||||
assert(not future.finished)
|
|
||||||
future.finished = true
|
|
||||||
future.error = error
|
|
||||||
if future.cb != nil:
|
|
||||||
future.cb(future)
|
|
||||||
|
|
||||||
proc `callback=`*[T](future: PFuture[T],
|
|
||||||
cb: proc (future: PFuture[T]) {.closure.}) =
|
|
||||||
## Sets the callback proc to be called when the future completes.
|
|
||||||
##
|
|
||||||
## If future has already completed then ``cb`` will be called immediately.
|
|
||||||
future.cb = cb
|
|
||||||
if future.finished:
|
|
||||||
future.cb(future)
|
|
||||||
|
|
||||||
proc `callbackVoid=`*(future: PFutureVoid, cb: proc () {.closure.}) =
|
|
||||||
## Sets the **void** callback proc to be called when the future completes.
|
|
||||||
##
|
|
||||||
## If future has already completed then ``cb`` will be called immediately.
|
|
||||||
##
|
|
||||||
## **Note**: This is used for the ``await`` functionality, you most likely
|
|
||||||
## want to use ``callback``.
|
|
||||||
future.cbVoid = cb
|
|
||||||
if future.finished:
|
|
||||||
future.cbVoid()
|
|
||||||
|
|
||||||
proc read*[T](future: PFuture[T]): T =
|
|
||||||
## Retrieves the value of ``future``. Future must be finished otherwise
|
|
||||||
## this function will fail with a ``EInvalidValue`` exception.
|
|
||||||
##
|
|
||||||
## If the result of the future is an error then that error will be raised.
|
|
||||||
if future.finished:
|
|
||||||
if future.error != nil: raise future.error
|
|
||||||
return future.value
|
|
||||||
else:
|
|
||||||
# TODO: Make a custom exception type for this?
|
|
||||||
raise newException(EInvalidValue, "Future still in progress.")
|
|
||||||
|
|
||||||
proc finished*[T](future: PFuture[T]): bool =
|
|
||||||
## Determines whether ``future`` has completed.
|
|
||||||
##
|
|
||||||
## ``True`` may indicate an error or a value. Use ``hasError`` to distinguish.
|
|
||||||
future.finished
|
|
||||||
|
|
||||||
proc failed*[T](future: PFuture[T]): bool =
|
|
||||||
## Determines whether ``future`` completed with an error.
|
|
||||||
future.error != nil
|
|
||||||
|
|
||||||
when defined(windows):
|
|
||||||
type
|
|
||||||
TCompletionKey = dword
|
|
||||||
|
|
||||||
TCompletionData* = object
|
|
||||||
sock: TSocketHandle
|
|
||||||
cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.}
|
|
||||||
|
|
||||||
PDispatcher* = ref object
|
|
||||||
ioPort: THandle
|
|
||||||
|
|
||||||
TCustomOverlapped = object
|
|
||||||
Internal*: DWORD
|
|
||||||
InternalHigh*: DWORD
|
|
||||||
Offset*: DWORD
|
|
||||||
OffsetHigh*: DWORD
|
|
||||||
hEvent*: THANDLE
|
|
||||||
data*: TCompletionData
|
|
||||||
|
|
||||||
PCustomOverlapped = ptr TCustomOverlapped
|
|
||||||
|
|
||||||
proc newDispatcher*(): PDispatcher =
|
|
||||||
## Creates a new Dispatcher instance.
|
|
||||||
new result
|
|
||||||
result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
|
|
||||||
|
|
||||||
proc register*(p: PDispatcher, sock: TSocketHandle) =
|
|
||||||
## Registers ``sock`` with the dispatcher ``p``.
|
|
||||||
if CreateIOCompletionPort(sock.THandle, p.ioPort,
|
|
||||||
cast[TCompletionKey](sock), 1) == 0:
|
|
||||||
OSError(OSLastError())
|
|
||||||
|
|
||||||
proc poll*(p: PDispatcher, timeout = 500) =
|
|
||||||
## Waits for completion events and processes them.
|
|
||||||
let llTimeout =
|
|
||||||
if timeout == -1: winlean.INFINITE
|
|
||||||
else: timeout.int32
|
|
||||||
var lpNumberOfBytesTransferred: DWORD
|
|
||||||
var lpCompletionKey: ULONG
|
|
||||||
var lpOverlapped: POverlapped
|
|
||||||
let res = GetQueuedCompletionStatus(p.ioPort, addr lpNumberOfBytesTransferred,
|
|
||||||
addr lpCompletionKey, addr lpOverlapped, llTimeout).bool
|
|
||||||
|
|
||||||
# http://stackoverflow.com/a/12277264/492186
|
|
||||||
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
|
|
||||||
var customOverlapped = cast[PCustomOverlapped](lpOverlapped)
|
|
||||||
if res:
|
|
||||||
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
|
|
||||||
|
|
||||||
customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1))
|
|
||||||
dealloc(customOverlapped)
|
|
||||||
else:
|
|
||||||
let errCode = OSLastError()
|
|
||||||
if lpOverlapped != nil:
|
|
||||||
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
|
|
||||||
dealloc(customOverlapped)
|
|
||||||
customOverlapped.data.cb(customOverlapped.data.sock, errCode)
|
|
||||||
else:
|
|
||||||
if errCode.int32 == WAIT_TIMEOUT:
|
|
||||||
# Timed out
|
|
||||||
discard
|
|
||||||
else: OSError(errCode)
|
|
||||||
|
|
||||||
var connectExPtr: pointer = nil
|
|
||||||
var acceptExPtr: pointer = nil
|
|
||||||
var getAcceptExSockAddrsPtr: pointer = nil
|
|
||||||
|
|
||||||
proc initPointer(s: TSocketHandle, func: var pointer, guid: var TGUID): bool =
|
|
||||||
# Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
|
|
||||||
var bytesRet: DWord
|
|
||||||
func = nil
|
|
||||||
result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
|
|
||||||
sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD,
|
|
||||||
addr bytesRet, nil, nil) == 0
|
|
||||||
|
|
||||||
proc initAll() =
|
|
||||||
let dummySock = socket()
|
|
||||||
if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX):
|
|
||||||
OSError(OSLastError())
|
|
||||||
if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX):
|
|
||||||
OSError(OSLastError())
|
|
||||||
if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
|
|
||||||
OSError(OSLastError())
|
|
||||||
|
|
||||||
proc connectEx(s: TSocketHandle, name: ptr TSockAddr, namelen: cint,
|
|
||||||
lpSendBuffer: pointer, dwSendDataLength: dword,
|
|
||||||
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool =
|
|
||||||
if connectExPtr.isNil: raise newException(EInvalidValue, "Need to initialise ConnectEx().")
|
|
||||||
let func =
|
|
||||||
cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint,
|
|
||||||
lpSendBuffer: pointer, dwSendDataLength: dword,
|
|
||||||
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall.}](connectExPtr)
|
|
||||||
|
|
||||||
result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent,
|
|
||||||
lpOverlapped)
|
|
||||||
|
|
||||||
proc acceptEx(listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
|
|
||||||
dwReceiveDataLength, dwLocalAddressLength,
|
|
||||||
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
|
|
||||||
lpOverlapped: POverlapped): bool =
|
|
||||||
if acceptExPtr.isNil: raise newException(EInvalidValue, "Need to initialise AcceptEx().")
|
|
||||||
let func =
|
|
||||||
cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
|
|
||||||
dwReceiveDataLength, dwLocalAddressLength,
|
|
||||||
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
|
|
||||||
lpOverlapped: POverlapped): bool {.stdcall.}](acceptExPtr)
|
|
||||||
result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength,
|
|
||||||
dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
|
|
||||||
lpOverlapped)
|
|
||||||
|
|
||||||
proc getAcceptExSockaddrs(lpOutputBuffer: pointer,
|
|
||||||
dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: DWORD,
|
|
||||||
LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint,
|
|
||||||
RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) =
|
|
||||||
if getAcceptExSockAddrsPtr.isNil:
|
|
||||||
raise newException(EInvalidValue, "Need to initialise getAcceptExSockAddrs().")
|
|
||||||
|
|
||||||
let func =
|
|
||||||
cast[proc (lpOutputBuffer: pointer,
|
|
||||||
dwReceiveDataLength, dwLocalAddressLength,
|
|
||||||
dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr,
|
|
||||||
LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr,
|
|
||||||
RemoteSockaddrLength: lpint) {.stdcall.}](getAcceptExSockAddrsPtr)
|
|
||||||
|
|
||||||
func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
|
|
||||||
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
|
|
||||||
RemoteSockaddr, RemoteSockaddrLength)
|
|
||||||
|
|
||||||
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
|
|
||||||
af = AF_INET): PFuture[int] =
|
|
||||||
## Connects ``socket`` to server at ``address:port``.
|
|
||||||
##
|
|
||||||
## Returns a ``PFuture`` which will complete when the connection succeeds
|
|
||||||
## or an error occurs.
|
|
||||||
|
|
||||||
var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed.
|
|
||||||
# Apparently ``ConnectEx`` expects the socket to be initially bound:
|
|
||||||
var saddr: Tsockaddr_in
|
|
||||||
saddr.sin_family = int16(toInt(af))
|
|
||||||
saddr.sin_port = 0
|
|
||||||
saddr.sin_addr.s_addr = INADDR_ANY
|
|
||||||
if bindAddr(socket, cast[ptr TSockAddr](addr(saddr)),
|
|
||||||
sizeof(saddr).TSockLen) < 0'i32:
|
|
||||||
OSError(OSLastError())
|
|
||||||
|
|
||||||
var aiList = getAddrInfo(address, port, af)
|
|
||||||
var success = false
|
|
||||||
var lastError: TOSErrorCode
|
|
||||||
var it = aiList
|
|
||||||
while it != nil:
|
|
||||||
# "the OVERLAPPED structure must remain valid until the I/O completes"
|
|
||||||
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
|
|
||||||
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
|
||||||
ol.data = TCompletionData(sock: socket, cb:
|
|
||||||
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
|
|
||||||
if errcode == TOSErrorCode(-1):
|
|
||||||
retFuture.complete(0)
|
|
||||||
else:
|
|
||||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
|
||||||
)
|
|
||||||
|
|
||||||
var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint,
|
|
||||||
nil, 0, nil, cast[POverlapped](ol))
|
|
||||||
if ret:
|
|
||||||
# Request to connect completed immediately.
|
|
||||||
success = true
|
|
||||||
retFuture.complete(0)
|
|
||||||
dealloc(ol)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
lastError = OSLastError()
|
|
||||||
if lastError.int32 == ERROR_IO_PENDING:
|
|
||||||
# In this case ``ol`` will be deallocated in ``poll``.
|
|
||||||
success = true
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
dealloc(ol)
|
|
||||||
success = false
|
|
||||||
it = it.ai_next
|
|
||||||
|
|
||||||
dealloc(aiList)
|
|
||||||
if not success:
|
|
||||||
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
|
|
||||||
return retFuture
|
|
||||||
|
|
||||||
proc recv*(p: PDispatcher, socket: TSocketHandle, size: int): PFuture[string] =
|
|
||||||
## Reads ``size`` bytes from ``socket``. Returned future will complete once
|
|
||||||
## all of the requested data is read.
|
|
||||||
|
|
||||||
var retFuture = newFuture[string]()
|
|
||||||
|
|
||||||
var dataBuf: TWSABuf
|
|
||||||
dataBuf.buf = newString(size)
|
|
||||||
dataBuf.len = size
|
|
||||||
|
|
||||||
var bytesReceived, flags: DWord
|
|
||||||
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
|
||||||
ol.data = TCompletionData(sock: socket, cb:
|
|
||||||
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
|
|
||||||
if errcode == TOSErrorCode(-1):
|
|
||||||
var data = newString(size)
|
|
||||||
copyMem(addr data[0], addr dataBuf.buf[0], size)
|
|
||||||
retFuture.complete($data)
|
|
||||||
else:
|
|
||||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
|
||||||
)
|
|
||||||
|
|
||||||
let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived,
|
|
||||||
addr flags, cast[POverlapped](ol), nil)
|
|
||||||
if ret == -1:
|
|
||||||
let err = OSLastError()
|
|
||||||
if err.int32 != ERROR_IO_PENDING:
|
|
||||||
retFuture.fail(newException(EOS, osErrorMsg(err)))
|
|
||||||
dealloc(ol)
|
|
||||||
else:
|
|
||||||
# Request to read completed immediately.
|
|
||||||
var data = newString(size)
|
|
||||||
copyMem(addr data[0], addr dataBuf.buf[0], size)
|
|
||||||
retFuture.complete($data)
|
|
||||||
dealloc(ol)
|
|
||||||
return retFuture
|
|
||||||
|
|
||||||
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
|
|
||||||
## Sends ``data`` to ``socket``. The returned future will complete once all
|
|
||||||
## data has been sent.
|
|
||||||
var retFuture = newFuture[int]()
|
|
||||||
|
|
||||||
var dataBuf: TWSABuf
|
|
||||||
dataBuf.buf = data
|
|
||||||
dataBuf.len = data.len
|
|
||||||
|
|
||||||
var bytesReceived, flags: DWord
|
|
||||||
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
|
||||||
ol.data = TCompletionData(sock: socket, cb:
|
|
||||||
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
|
|
||||||
if errcode == TOSErrorCode(-1):
|
|
||||||
retFuture.complete(0)
|
|
||||||
else:
|
|
||||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
|
||||||
)
|
|
||||||
|
|
||||||
let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived,
|
|
||||||
flags, cast[POverlapped](ol), nil)
|
|
||||||
if ret == -1:
|
|
||||||
let err = osLastError()
|
|
||||||
if err.int32 != ERROR_IO_PENDING:
|
|
||||||
retFuture.fail(newException(EOS, osErrorMsg(err)))
|
|
||||||
dealloc(ol)
|
|
||||||
else:
|
|
||||||
retFuture.complete(0)
|
|
||||||
dealloc(ol)
|
|
||||||
return retFuture
|
|
||||||
|
|
||||||
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
|
|
||||||
PFuture[tuple[address: string, client: TSocketHandle]] =
|
|
||||||
## Accepts a new connection. Returns a future containing the client socket
|
|
||||||
## corresponding to that connection and the remote address of the client.
|
|
||||||
## The future will complete when the connection is successfully accepted.
|
|
||||||
|
|
||||||
var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
|
|
||||||
|
|
||||||
var clientSock = socket()
|
|
||||||
if clientSock == OSInvalidSocket: osError(osLastError())
|
|
||||||
|
|
||||||
const lpOutputLen = 1024
|
|
||||||
var lpOutputBuf = newString(lpOutputLen)
|
|
||||||
var dwBytesReceived: DWORD
|
|
||||||
let dwReceiveDataLength = 0.DWORD # We don't want any data to be read.
|
|
||||||
let dwLocalAddressLength = DWORD(sizeof (TSockaddr_in) + 16)
|
|
||||||
let dwRemoteAddressLength = DWORD(sizeof(TSockaddr_in) + 16)
|
|
||||||
|
|
||||||
template completeAccept(): stmt {.immediate, dirty.} =
|
|
||||||
var listenSock = socket
|
|
||||||
let setoptRet = setsockopt(clientSock, SOL_SOCKET,
|
|
||||||
SO_UPDATE_ACCEPT_CONTEXT, addr listenSock,
|
|
||||||
sizeof(listenSock).TSockLen)
|
|
||||||
if setoptRet != 0: osError(osLastError())
|
|
||||||
|
|
||||||
var LocalSockaddr, RemoteSockaddr: ptr TSockAddr
|
|
||||||
var localLen, remoteLen: int32
|
|
||||||
getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength,
|
|
||||||
dwLocalAddressLength, dwRemoteAddressLength,
|
|
||||||
addr LocalSockaddr, addr localLen,
|
|
||||||
addr RemoteSockaddr, addr remoteLen)
|
|
||||||
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
|
|
||||||
retFuture.complete(
|
|
||||||
(address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr),
|
|
||||||
client: clientSock)
|
|
||||||
)
|
|
||||||
|
|
||||||
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
|
|
||||||
ol.data = TCompletionData(sock: socket, cb:
|
|
||||||
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
|
|
||||||
if errcode == TOSErrorCode(-1):
|
|
||||||
completeAccept()
|
|
||||||
else:
|
|
||||||
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
|
|
||||||
)
|
|
||||||
|
|
||||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
|
|
||||||
let ret = acceptEx(socket, clientSock, addr lpOutputBuf[0],
|
|
||||||
dwReceiveDataLength,
|
|
||||||
dwLocalAddressLength,
|
|
||||||
dwRemoteAddressLength,
|
|
||||||
addr dwBytesReceived, cast[POverlapped](ol))
|
|
||||||
|
|
||||||
if not ret:
|
|
||||||
let err = osLastError()
|
|
||||||
if err.int32 != ERROR_IO_PENDING:
|
|
||||||
retFuture.fail(newException(EOS, osErrorMsg(err)))
|
|
||||||
dealloc(ol)
|
|
||||||
else:
|
|
||||||
completeAccept()
|
|
||||||
dealloc(ol)
|
|
||||||
|
|
||||||
return retFuture
|
|
||||||
|
|
||||||
proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] =
|
|
||||||
## Accepts a new connection. Returns a future containing the client socket
|
|
||||||
## corresponding to that connection.
|
|
||||||
## The future will complete when the connection is successfully accepted.
|
|
||||||
var retFut = newFuture[TSocketHandle]()
|
|
||||||
var fut = p.acceptAddr(socket)
|
|
||||||
fut.callback =
|
|
||||||
proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) =
|
|
||||||
assert future.finished
|
|
||||||
if future.failed:
|
|
||||||
retFut.fail(future.error)
|
|
||||||
else:
|
|
||||||
retFut.complete(future.read.client)
|
|
||||||
return retFut
|
|
||||||
|
|
||||||
initAll()
|
|
||||||
else:
|
|
||||||
# TODO: Selectors.
|
|
||||||
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
|
|
||||||
var p = newDispatcher()
|
|
||||||
var sock = socket()
|
|
||||||
#sock.setBlocking false
|
|
||||||
p.register(sock)
|
|
||||||
|
|
||||||
when true:
|
|
||||||
|
|
||||||
var f = p.connect(sock, "irc.freenode.org", TPort(6667))
|
|
||||||
f.callback =
|
|
||||||
proc (future: PFuture[int]) =
|
|
||||||
echo("Connected in future!")
|
|
||||||
echo(future.read)
|
|
||||||
for i in 0 .. 50:
|
|
||||||
var recvF = p.recv(sock, 10)
|
|
||||||
recvF.callback =
|
|
||||||
proc (future: PFuture[string]) =
|
|
||||||
echo("Read: ", future.read)
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
sock.bindAddr(TPort(6667))
|
|
||||||
sock.listen()
|
|
||||||
proc onAccept(future: PFuture[TSocketHandle]) =
|
|
||||||
echo "Accepted"
|
|
||||||
var t = p.send(future.read, "test\c\L")
|
|
||||||
t.callback =
|
|
||||||
proc (future: PFuture[int]) =
|
|
||||||
echo(future.read)
|
|
||||||
|
|
||||||
var f = p.accept(sock)
|
|
||||||
f.callback = onAccept
|
|
||||||
|
|
||||||
var f = p.accept(sock)
|
|
||||||
f.callback = onAccept
|
|
||||||
|
|
||||||
while true:
|
|
||||||
p.poll()
|
|
||||||
echo "polled"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
195
lib/pure/asyncnet.nim
Normal file
195
lib/pure/asyncnet.nim
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nimrod's Runtime Library
|
||||||
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
import asyncdispatch
|
||||||
|
import rawsockets
|
||||||
|
import net
|
||||||
|
|
||||||
|
when defined(ssl):
|
||||||
|
import openssl
|
||||||
|
|
||||||
|
type
|
||||||
|
# TODO: I would prefer to just do:
|
||||||
|
# PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work.
|
||||||
|
TAsyncSocket {.borrow: `.`.} = distinct TSocketImpl
|
||||||
|
PAsyncSocket* = ref TAsyncSocket
|
||||||
|
|
||||||
|
# TODO: Save AF, domain etc info and reuse it in procs which need it like connect.
|
||||||
|
|
||||||
|
proc newSocket(fd: TAsyncFD, isBuff: bool): PAsyncSocket =
|
||||||
|
assert fd != osInvalidSocket.TAsyncFD
|
||||||
|
new(result.PSocket)
|
||||||
|
result.fd = fd.TSocketHandle
|
||||||
|
result.isBuffered = isBuff
|
||||||
|
if isBuff:
|
||||||
|
result.currPos = 0
|
||||||
|
|
||||||
|
proc newAsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||||
|
protocol: TProtocol = IPPROTO_TCP, buffered = true): PAsyncSocket =
|
||||||
|
## Creates a new asynchronous socket.
|
||||||
|
result = newSocket(newAsyncRawSocket(domain, typ, protocol), buffered)
|
||||||
|
|
||||||
|
proc connect*(socket: PAsyncSocket, address: string, port: TPort,
|
||||||
|
af = AF_INET): PFuture[void] =
|
||||||
|
## Connects ``socket`` to server at ``address:port``.
|
||||||
|
##
|
||||||
|
## Returns a ``PFuture`` which will complete when the connection succeeds
|
||||||
|
## or an error occurs.
|
||||||
|
result = connect(socket.fd.TAsyncFD, address, port, af)
|
||||||
|
|
||||||
|
proc recv*(socket: PAsyncSocket, size: int,
|
||||||
|
flags: int = 0): PFuture[string] =
|
||||||
|
## Reads ``size`` bytes from ``socket``. Returned future will complete once
|
||||||
|
## all of the requested data is read. If socket is disconnected during the
|
||||||
|
## recv operation then the future may complete with only a part of the
|
||||||
|
## requested data read. If socket is disconnected and no data is available
|
||||||
|
## to be read then the future will complete with a value of ``""``.
|
||||||
|
result = recv(socket.fd.TAsyncFD, size, flags)
|
||||||
|
|
||||||
|
proc send*(socket: PAsyncSocket, data: string): PFuture[void] =
|
||||||
|
## Sends ``data`` to ``socket``. The returned future will complete once all
|
||||||
|
## data has been sent.
|
||||||
|
result = send(socket.fd.TAsyncFD, data)
|
||||||
|
|
||||||
|
proc acceptAddr*(socket: PAsyncSocket):
|
||||||
|
PFuture[tuple[address: string, client: PAsyncSocket]] =
|
||||||
|
## Accepts a new connection. Returns a future containing the client socket
|
||||||
|
## corresponding to that connection and the remote address of the client.
|
||||||
|
## The future will complete when the connection is successfully accepted.
|
||||||
|
var retFuture = newFuture[tuple[address: string, client: PAsyncSocket]]()
|
||||||
|
var fut = acceptAddr(socket.fd.TAsyncFD)
|
||||||
|
fut.callback =
|
||||||
|
proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) =
|
||||||
|
assert future.finished
|
||||||
|
if future.failed:
|
||||||
|
retFuture.fail(future.readError)
|
||||||
|
else:
|
||||||
|
let resultTup = (future.read.address,
|
||||||
|
newSocket(future.read.client, socket.isBuffered))
|
||||||
|
retFuture.complete(resultTup)
|
||||||
|
return retFuture
|
||||||
|
|
||||||
|
proc accept*(socket: PAsyncSocket): PFuture[PAsyncSocket] =
|
||||||
|
## Accepts a new connection. Returns a future containing the client socket
|
||||||
|
## corresponding to that connection.
|
||||||
|
## The future will complete when the connection is successfully accepted.
|
||||||
|
var retFut = newFuture[PAsyncSocket]()
|
||||||
|
var fut = acceptAddr(socket)
|
||||||
|
fut.callback =
|
||||||
|
proc (future: PFuture[tuple[address: string, client: PAsyncSocket]]) =
|
||||||
|
assert future.finished
|
||||||
|
if future.failed:
|
||||||
|
retFut.fail(future.readError)
|
||||||
|
else:
|
||||||
|
retFut.complete(future.read.client)
|
||||||
|
return retFut
|
||||||
|
|
||||||
|
proc recvLine*(socket: PAsyncSocket): PFuture[string] {.async.} =
|
||||||
|
## Reads a line of data from ``socket``. Returned future will complete once
|
||||||
|
## a full line is read or an error occurs.
|
||||||
|
##
|
||||||
|
## If a full line is read ``\r\L`` is not
|
||||||
|
## added to ``line``, however if solely ``\r\L`` is read then ``line``
|
||||||
|
## will be set to it.
|
||||||
|
##
|
||||||
|
## If the socket is disconnected, ``line`` will be set to ``""``.
|
||||||
|
##
|
||||||
|
## If the socket is disconnected in the middle of a line (before ``\r\L``
|
||||||
|
## is read) then line will be set to ``""``.
|
||||||
|
## The partial line **will be lost**.
|
||||||
|
|
||||||
|
template addNLIfEmpty(): stmt =
|
||||||
|
if result.len == 0:
|
||||||
|
result.add("\c\L")
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
var c = ""
|
||||||
|
while true:
|
||||||
|
c = await recv(socket, 1)
|
||||||
|
if c.len == 0:
|
||||||
|
return ""
|
||||||
|
if c == "\r":
|
||||||
|
c = await recv(socket, 1, MSG_PEEK)
|
||||||
|
if c.len > 0 and c == "\L":
|
||||||
|
discard await recv(socket, 1)
|
||||||
|
addNLIfEmpty()
|
||||||
|
return
|
||||||
|
elif c == "\L":
|
||||||
|
addNLIfEmpty()
|
||||||
|
return
|
||||||
|
add(result.string, c)
|
||||||
|
|
||||||
|
proc bindAddr*(socket: PAsyncSocket, port = TPort(0), address = "") =
|
||||||
|
## Binds ``address``:``port`` to the socket.
|
||||||
|
##
|
||||||
|
## If ``address`` is "" then ADDR_ANY will be bound.
|
||||||
|
socket.PSocket.bindAddr(port, address)
|
||||||
|
|
||||||
|
proc listen*(socket: PAsyncSocket, backlog = SOMAXCONN) =
|
||||||
|
## Marks ``socket`` as accepting connections.
|
||||||
|
## ``Backlog`` specifies the maximum length of the
|
||||||
|
## queue of pending connections.
|
||||||
|
##
|
||||||
|
## Raises an EOS error upon failure.
|
||||||
|
socket.PSocket.listen(backlog)
|
||||||
|
|
||||||
|
proc close*(socket: PAsyncSocket) =
|
||||||
|
## Closes the socket.
|
||||||
|
socket.fd.TAsyncFD.close()
|
||||||
|
# TODO SSL
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
type
|
||||||
|
TestCases = enum
|
||||||
|
HighClient, LowClient, LowServer
|
||||||
|
|
||||||
|
const test = LowServer
|
||||||
|
|
||||||
|
when test == HighClient:
|
||||||
|
proc main() {.async.} =
|
||||||
|
var sock = newAsyncSocket()
|
||||||
|
await sock.connect("irc.freenode.net", TPort(6667))
|
||||||
|
while true:
|
||||||
|
let line = await sock.recvLine()
|
||||||
|
if line == "":
|
||||||
|
echo("Disconnected")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
echo("Got line: ", line)
|
||||||
|
main()
|
||||||
|
elif test == LowClient:
|
||||||
|
var sock = newAsyncSocket()
|
||||||
|
var f = connect(sock, "irc.freenode.net", TPort(6667))
|
||||||
|
f.callback =
|
||||||
|
proc (future: PFuture[void]) =
|
||||||
|
echo("Connected in future!")
|
||||||
|
for i in 0 .. 50:
|
||||||
|
var recvF = recv(sock, 10)
|
||||||
|
recvF.callback =
|
||||||
|
proc (future: PFuture[string]) =
|
||||||
|
echo("Read ", future.read.len, ": ", future.read.repr)
|
||||||
|
elif test == LowServer:
|
||||||
|
var sock = newAsyncSocket()
|
||||||
|
sock.bindAddr(TPort(6667))
|
||||||
|
sock.listen()
|
||||||
|
proc onAccept(future: PFuture[PAsyncSocket]) =
|
||||||
|
let client = future.read
|
||||||
|
echo "Accepted ", client.fd.cint
|
||||||
|
var t = send(client, "test\c\L")
|
||||||
|
t.callback =
|
||||||
|
proc (future: PFuture[void]) =
|
||||||
|
echo("Send")
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
var f = accept(sock)
|
||||||
|
f.callback = onAccept
|
||||||
|
|
||||||
|
var f = accept(sock)
|
||||||
|
f.callback = onAccept
|
||||||
|
runForever()
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
type
|
type
|
||||||
TLibHandle* = pointer ## a handle to a dynamically loaded library
|
TLibHandle* = pointer ## a handle to a dynamically loaded library
|
||||||
|
|
||||||
proc loadLib*(path: string): TLibHandle
|
proc loadLib*(path: string, global_symbols=false): TLibHandle
|
||||||
## loads a library from `path`. Returns nil if the library could not
|
## loads a library from `path`. Returns nil if the library could not
|
||||||
## be loaded.
|
## be loaded.
|
||||||
|
|
||||||
|
|
@ -53,6 +53,7 @@ when defined(posix):
|
||||||
#
|
#
|
||||||
var
|
var
|
||||||
RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int
|
RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int
|
||||||
|
RTLD_GLOBAL {.importc: "RTLD_GLOBAL", header: "<dlfcn.h>".}: int
|
||||||
|
|
||||||
proc dlclose(lib: TLibHandle) {.importc, header: "<dlfcn.h>".}
|
proc dlclose(lib: TLibHandle) {.importc, header: "<dlfcn.h>".}
|
||||||
proc dlopen(path: CString, mode: int): TLibHandle {.
|
proc dlopen(path: CString, mode: int): TLibHandle {.
|
||||||
|
|
@ -60,7 +61,10 @@ when defined(posix):
|
||||||
proc dlsym(lib: TLibHandle, name: cstring): pointer {.
|
proc dlsym(lib: TLibHandle, name: cstring): pointer {.
|
||||||
importc, header: "<dlfcn.h>".}
|
importc, header: "<dlfcn.h>".}
|
||||||
|
|
||||||
proc loadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
|
proc loadLib(path: string, global_symbols=false): TLibHandle =
|
||||||
|
var flags = RTLD_NOW
|
||||||
|
if global_symbols: flags = flags or RTLD_GLOBAL
|
||||||
|
return dlopen(path, flags)
|
||||||
proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
|
proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
|
||||||
proc unloadLib(lib: TLibHandle) = dlclose(lib)
|
proc unloadLib(lib: TLibHandle) = dlclose(lib)
|
||||||
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
||||||
|
|
@ -81,14 +85,14 @@ elif defined(windows) or defined(dos):
|
||||||
proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
|
proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
|
||||||
importc: "GetProcAddress", header: "<windows.h>", stdcall.}
|
importc: "GetProcAddress", header: "<windows.h>", stdcall.}
|
||||||
|
|
||||||
proc loadLib(path: string): TLibHandle =
|
proc loadLib(path: string, global_symbols=false): TLibHandle =
|
||||||
result = cast[TLibHandle](winLoadLibrary(path))
|
result = cast[TLibHandle](winLoadLibrary(path))
|
||||||
proc loadLib(): TLibHandle =
|
proc loadLib(): TLibHandle =
|
||||||
result = cast[TLibHandle](winLoadLibrary(nil))
|
result = cast[TLibHandle](winLoadLibrary(nil))
|
||||||
proc unloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
|
proc unloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
|
||||||
|
|
||||||
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
||||||
result = GetProcAddress(cast[THINSTANCE](lib), name)
|
result = getProcAddress(cast[THINSTANCE](lib), name)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
{.error: "no implementation for dynlib".}
|
{.error: "no implementation for dynlib".}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2014 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
## **Warning**: This module uses ``immediate`` macros which are known to
|
||||||
|
## cause problems. Do yourself a favor and import the module
|
||||||
|
## as ``from htmlgen import nil`` and then fully qualify the macros.
|
||||||
|
##
|
||||||
|
##
|
||||||
## This module implements a simple `XML`:idx: and `HTML`:idx: code
|
## This module implements a simple `XML`:idx: and `HTML`:idx: code
|
||||||
## generator. Each commonly used HTML tag has a corresponding macro
|
## generator. Each commonly used HTML tag has a corresponding macro
|
||||||
## that generates a string with its HTML representation.
|
## that generates a string with its HTML representation.
|
||||||
|
|
@ -15,11 +20,11 @@
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
## var nim = "Nimrod"
|
## var nim = "Nimrod"
|
||||||
## echo h1(a(href="http://nimrod-code.org", nim))
|
## echo h1(a(href="http://nimrod-lang.org", nim))
|
||||||
##
|
##
|
||||||
## Writes the string::
|
## Writes the string::
|
||||||
##
|
##
|
||||||
## <h1><a href="http://nimrod-code.org">Nimrod</a></h1>
|
## <h1><a href="http://nimrod-lang.org">Nimrod</a></h1>
|
||||||
##
|
##
|
||||||
|
|
||||||
import
|
import
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@
|
||||||
## currently only basic authentication is supported.
|
## currently only basic authentication is supported.
|
||||||
|
|
||||||
import sockets, strutils, parseurl, parseutils, strtabs, base64
|
import sockets, strutils, parseurl, parseutils, strtabs, base64
|
||||||
|
import asyncnet, asyncdispatch
|
||||||
|
import rawsockets
|
||||||
|
|
||||||
type
|
type
|
||||||
TResponse* = tuple[
|
TResponse* = tuple[
|
||||||
|
|
@ -286,16 +288,16 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
|
||||||
add(headers, "\c\L")
|
add(headers, "\c\L")
|
||||||
|
|
||||||
var s = socket()
|
var s = socket()
|
||||||
var port = TPort(80)
|
var port = sockets.TPort(80)
|
||||||
if r.scheme == "https":
|
if r.scheme == "https":
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
sslContext.wrapSocket(s)
|
sslContext.wrapSocket(s)
|
||||||
port = TPort(443)
|
port = sockets.TPort(443)
|
||||||
else:
|
else:
|
||||||
raise newException(EHttpRequestErr,
|
raise newException(EHttpRequestErr,
|
||||||
"SSL support is not available. Cannot connect over SSL.")
|
"SSL support is not available. Cannot connect over SSL.")
|
||||||
if r.port != "":
|
if r.port != "":
|
||||||
port = TPort(r.port.parseInt)
|
port = sockets.TPort(r.port.parseInt)
|
||||||
|
|
||||||
if timeout == -1:
|
if timeout == -1:
|
||||||
s.connect(r.hostname, port)
|
s.connect(r.hostname, port)
|
||||||
|
|
@ -413,28 +415,217 @@ proc downloadFile*(url: string, outputFilename: string,
|
||||||
else:
|
else:
|
||||||
fileError("Unable to open file")
|
fileError("Unable to open file")
|
||||||
|
|
||||||
|
proc generateHeaders(r: TURL, httpMethod: THttpMethod,
|
||||||
|
headers: PStringTable): string =
|
||||||
|
result = substr($httpMethod, len("http"))
|
||||||
|
# TODO: Proxies
|
||||||
|
result.add(" /" & r.path & r.query)
|
||||||
|
result.add(" HTTP/1.1\c\L")
|
||||||
|
|
||||||
|
add(result, "Host: " & r.hostname & "\c\L")
|
||||||
|
add(result, "Connection: Keep-Alive\c\L")
|
||||||
|
for key, val in headers:
|
||||||
|
add(result, key & ": " & val & "\c\L")
|
||||||
|
|
||||||
|
add(result, "\c\L")
|
||||||
|
|
||||||
|
type
|
||||||
|
PAsyncHttpClient = ref object
|
||||||
|
socket: PAsyncSocket
|
||||||
|
connected: bool
|
||||||
|
currentURL: TURL ## Where we are currently connected.
|
||||||
|
headers: PStringTable
|
||||||
|
userAgent: string
|
||||||
|
|
||||||
|
proc newAsyncHttpClient*(): PAsyncHttpClient =
|
||||||
|
new result
|
||||||
|
result.socket = newAsyncSocket()
|
||||||
|
result.headers = newStringTable(modeCaseInsensitive)
|
||||||
|
result.userAgent = defUserAgent
|
||||||
|
|
||||||
|
proc parseChunks(client: PAsyncHttpClient): PFuture[string] {.async.} =
|
||||||
|
result = ""
|
||||||
|
var ri = 0
|
||||||
|
while true:
|
||||||
|
var chunkSize = 0
|
||||||
|
var chunkSizeStr = await client.socket.recvLine()
|
||||||
|
var i = 0
|
||||||
|
if chunkSizeStr == "":
|
||||||
|
httpError("Server terminated connection prematurely")
|
||||||
|
while true:
|
||||||
|
case chunkSizeStr[i]
|
||||||
|
of '0'..'9':
|
||||||
|
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
|
||||||
|
of 'a'..'f':
|
||||||
|
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
|
||||||
|
of 'A'..'F':
|
||||||
|
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
|
||||||
|
of '\0':
|
||||||
|
break
|
||||||
|
of ';':
|
||||||
|
# http://tools.ietf.org/html/rfc2616#section-3.6.1
|
||||||
|
# We don't care about chunk-extensions.
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
httpError("Invalid chunk size: " & chunkSizeStr)
|
||||||
|
inc(i)
|
||||||
|
if chunkSize <= 0: break
|
||||||
|
result.add await recv(client.socket, chunkSize)
|
||||||
|
discard await recv(client.socket, 2) # Skip \c\L
|
||||||
|
# Trailer headers will only be sent if the request specifies that we want
|
||||||
|
# them: http://tools.ietf.org/html/rfc2616#section-3.6.1
|
||||||
|
|
||||||
|
proc parseBody(client: PAsyncHttpClient,
|
||||||
|
headers: PStringTable): PFuture[string] {.async.} =
|
||||||
|
result = ""
|
||||||
|
if headers["Transfer-Encoding"] == "chunked":
|
||||||
|
result = await parseChunks(client)
|
||||||
|
else:
|
||||||
|
# -REGION- Content-Length
|
||||||
|
# (http://tools.ietf.org/html/rfc2616#section-4.4) NR.3
|
||||||
|
var contentLengthHeader = headers["Content-Length"]
|
||||||
|
if contentLengthHeader != "":
|
||||||
|
var length = contentLengthHeader.parseint()
|
||||||
|
result = await client.socket.recv(length)
|
||||||
|
if result == "":
|
||||||
|
httpError("Got disconnected while trying to recv body.")
|
||||||
|
else:
|
||||||
|
# (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO
|
||||||
|
|
||||||
|
# -REGION- Connection: Close
|
||||||
|
# (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5
|
||||||
|
if headers["Connection"] == "close":
|
||||||
|
var buf = ""
|
||||||
|
while True:
|
||||||
|
buf = await client.socket.recv(4000)
|
||||||
|
if buf == "": break
|
||||||
|
result.add(buf)
|
||||||
|
|
||||||
|
proc parseResponse(client: PAsyncHttpClient,
|
||||||
|
getBody: bool): PFuture[TResponse] {.async.} =
|
||||||
|
var parsedStatus = false
|
||||||
|
var linei = 0
|
||||||
|
var fullyRead = false
|
||||||
|
var line = ""
|
||||||
|
result.headers = newStringTable(modeCaseInsensitive)
|
||||||
|
while True:
|
||||||
|
linei = 0
|
||||||
|
line = await client.socket.recvLine()
|
||||||
|
if line == "": break # We've been disconnected.
|
||||||
|
if line == "\c\L":
|
||||||
|
fullyRead = true
|
||||||
|
break
|
||||||
|
if not parsedStatus:
|
||||||
|
# Parse HTTP version info and status code.
|
||||||
|
var le = skipIgnoreCase(line, "HTTP/", linei)
|
||||||
|
if le <= 0: httpError("invalid http version")
|
||||||
|
inc(linei, le)
|
||||||
|
le = skipIgnoreCase(line, "1.1", linei)
|
||||||
|
if le > 0: result.version = "1.1"
|
||||||
|
else:
|
||||||
|
le = skipIgnoreCase(line, "1.0", linei)
|
||||||
|
if le <= 0: httpError("unsupported http version")
|
||||||
|
result.version = "1.0"
|
||||||
|
inc(linei, le)
|
||||||
|
# Status code
|
||||||
|
linei.inc skipWhitespace(line, linei)
|
||||||
|
result.status = line[linei .. -1]
|
||||||
|
parsedStatus = true
|
||||||
|
else:
|
||||||
|
# Parse headers
|
||||||
|
var name = ""
|
||||||
|
var le = parseUntil(line, name, ':', linei)
|
||||||
|
if le <= 0: httpError("invalid headers")
|
||||||
|
inc(linei, le)
|
||||||
|
if line[linei] != ':': httpError("invalid headers")
|
||||||
|
inc(linei) # Skip :
|
||||||
|
|
||||||
|
result.headers[name] = line[linei.. -1].strip()
|
||||||
|
if not fullyRead:
|
||||||
|
httpError("Connection was closed before full request has been made")
|
||||||
|
if getBody:
|
||||||
|
result.body = await parseBody(client, result.headers)
|
||||||
|
else:
|
||||||
|
result.body = ""
|
||||||
|
|
||||||
|
proc close*(client: PAsyncHttpClient) =
|
||||||
|
## Closes any connections held by the HttpClient.
|
||||||
|
if client.connected:
|
||||||
|
client.socket.close()
|
||||||
|
client.connected = false
|
||||||
|
#client.socket = newAsyncSocket()
|
||||||
|
|
||||||
|
proc newConnection(client: PAsyncHttpClient, url: TURL) {.async.} =
|
||||||
|
if not client.connected or client.currentURL.hostname != url.hostname or
|
||||||
|
client.currentURL.scheme != url.scheme:
|
||||||
|
if client.connected: client.close()
|
||||||
|
if url.scheme == "https":
|
||||||
|
assert false, "TODO SSL"
|
||||||
|
|
||||||
|
# TODO: I should be able to write 'net.TPort' here...
|
||||||
|
let port =
|
||||||
|
if url.port == "": rawsockets.TPort(80)
|
||||||
|
else: rawsockets.TPort(url.port.parseInt)
|
||||||
|
|
||||||
|
await client.socket.connect(url.hostname, port)
|
||||||
|
client.currentURL = url
|
||||||
|
|
||||||
|
proc request*(client: PAsyncHttpClient, url: string, httpMethod = httpGET,
|
||||||
|
body = ""): PFuture[TResponse] {.async.} =
|
||||||
|
let r = parseUrl(url)
|
||||||
|
await newConnection(client, r)
|
||||||
|
|
||||||
|
if not client.headers.hasKey("user-agent") and client.userAgent != "":
|
||||||
|
client.headers["User-Agent"] = client.userAgent
|
||||||
|
|
||||||
|
var headers = generateHeaders(r, httpMethod, client.headers)
|
||||||
|
|
||||||
|
await client.socket.send(headers)
|
||||||
|
if body != "":
|
||||||
|
await client.socket.send(body)
|
||||||
|
|
||||||
|
result = await parseResponse(client, httpMethod != httpHEAD)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
#downloadFile("http://force7.de/nimrod/index.html", "nimrodindex.html")
|
when true:
|
||||||
#downloadFile("http://www.httpwatch.com/", "ChunkTest.html")
|
# Async
|
||||||
#downloadFile("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com",
|
proc main() {.async.} =
|
||||||
# "validator.html")
|
var client = newAsyncHttpClient()
|
||||||
|
var resp = await client.request("http://picheta.me")
|
||||||
|
|
||||||
#var r = get("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com&
|
echo("Got response: ", resp.status)
|
||||||
# charset=%28detect+automatically%29&doctype=Inline&group=0")
|
echo("Body:\n")
|
||||||
|
echo(resp.body)
|
||||||
|
|
||||||
var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L"
|
#var resp1 = await client.request("http://freenode.net")
|
||||||
var body: string = "--xyz\c\L"
|
#echo("Got response: ", resp1.status)
|
||||||
# soap 1.2 output
|
|
||||||
body.add("Content-Disposition: form-data; name=\"output\"\c\L")
|
|
||||||
body.add("\c\Lsoap12\c\L")
|
|
||||||
|
|
||||||
# html
|
var resp2 = await client.request("http://picheta.me/aasfasgf.html")
|
||||||
body.add("--xyz\c\L")
|
echo("Got response: ", resp2.status)
|
||||||
body.add("Content-Disposition: form-data; name=\"uploaded_file\";" &
|
main()
|
||||||
" filename=\"test.html\"\c\L")
|
runForever()
|
||||||
body.add("Content-Type: text/html\c\L")
|
|
||||||
body.add("\c\L<html><head></head><body><p>test</p></body></html>\c\L")
|
|
||||||
body.add("--xyz--")
|
|
||||||
|
|
||||||
echo(postContent("http://validator.w3.org/check", headers, body))
|
else:
|
||||||
|
#downloadFile("http://force7.de/nimrod/index.html", "nimrodindex.html")
|
||||||
|
#downloadFile("http://www.httpwatch.com/", "ChunkTest.html")
|
||||||
|
#downloadFile("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com",
|
||||||
|
# "validator.html")
|
||||||
|
|
||||||
|
#var r = get("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com&
|
||||||
|
# charset=%28detect+automatically%29&doctype=Inline&group=0")
|
||||||
|
|
||||||
|
var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L"
|
||||||
|
var body: string = "--xyz\c\L"
|
||||||
|
# soap 1.2 output
|
||||||
|
body.add("Content-Disposition: form-data; name=\"output\"\c\L")
|
||||||
|
body.add("\c\Lsoap12\c\L")
|
||||||
|
|
||||||
|
# html
|
||||||
|
body.add("--xyz\c\L")
|
||||||
|
body.add("Content-Disposition: form-data; name=\"uploaded_file\";" &
|
||||||
|
" filename=\"test.html\"\c\L")
|
||||||
|
body.add("Content-Type: text/html\c\L")
|
||||||
|
body.add("\c\L<html><head></head><body><p>test</p></body></html>\c\L")
|
||||||
|
body.add("--xyz--")
|
||||||
|
|
||||||
|
echo(postContent("http://validator.w3.org/check", headers, body))
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@
|
||||||
when defined(Posix) and not defined(haiku):
|
when defined(Posix) and not defined(haiku):
|
||||||
{.passl: "-lm".}
|
{.passl: "-lm".}
|
||||||
|
|
||||||
|
import times
|
||||||
|
|
||||||
const
|
const
|
||||||
PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number)
|
PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number)
|
||||||
E* = 2.71828182845904523536028747 ## Euler's number
|
E* = 2.71828182845904523536028747 ## Euler's number
|
||||||
|
|
@ -201,7 +203,7 @@ when not defined(JS):
|
||||||
result = drand48() * max
|
result = drand48() * max
|
||||||
|
|
||||||
proc randomize() =
|
proc randomize() =
|
||||||
randomize(gettime(nil))
|
randomize(cast[int](epochTime()))
|
||||||
|
|
||||||
proc randomize(seed: int) =
|
proc randomize(seed: int) =
|
||||||
srand(cint(seed))
|
srand(cint(seed))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2012 Nimrod Contributors
|
# (c) Copyright 2014 Nimrod Contributors
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -34,6 +34,43 @@ type
|
||||||
else:
|
else:
|
||||||
handle: cint
|
handle: cint
|
||||||
|
|
||||||
|
|
||||||
|
proc mapMem*(m: var TMemFile, mode: TFileMode = fmRead,
|
||||||
|
mappedSize = -1, offset = 0): pointer =
|
||||||
|
var readonly = mode == fmRead
|
||||||
|
when defined(windows):
|
||||||
|
result = mapViewOfFileEx(
|
||||||
|
m.mapHandle,
|
||||||
|
if readonly: FILE_MAP_READ else: FILE_MAP_WRITE,
|
||||||
|
int32(offset shr 32),
|
||||||
|
int32(offset and 0xffffffff),
|
||||||
|
if mappedSize == -1: 0 else: mappedSize,
|
||||||
|
nil)
|
||||||
|
if result == nil:
|
||||||
|
osError(osLastError())
|
||||||
|
else:
|
||||||
|
assert mappedSize > 0
|
||||||
|
result = mmap(
|
||||||
|
nil,
|
||||||
|
mappedSize,
|
||||||
|
if readonly: PROT_READ else: PROT_READ or PROT_WRITE,
|
||||||
|
if readonly: MAP_PRIVATE else: MAP_SHARED,
|
||||||
|
m.handle, offset)
|
||||||
|
if result == cast[pointer](MAP_FAILED):
|
||||||
|
osError(osLastError())
|
||||||
|
|
||||||
|
|
||||||
|
proc unmapMem*(f: var TMemFile, p: pointer, size: int) =
|
||||||
|
## unmaps the memory region ``(p, <p+size)`` of the mapped file `f`.
|
||||||
|
## All changes are written back to the file system, if `f` was opened
|
||||||
|
## with write access. ``size`` must be of exactly the size that was requested
|
||||||
|
## via ``mapMem``.
|
||||||
|
when defined(windows):
|
||||||
|
if unmapViewOfFile(p) == 0: osError(osLastError())
|
||||||
|
else:
|
||||||
|
if munmap(p, size) != 0: osError(osLastError())
|
||||||
|
|
||||||
|
|
||||||
proc open*(filename: string, mode: TFileMode = fmRead,
|
proc open*(filename: string, mode: TFileMode = fmRead,
|
||||||
mappedSize = -1, offset = 0, newFileSize = -1): TMemFile =
|
mappedSize = -1, offset = 0, newFileSize = -1): TMemFile =
|
||||||
## opens a memory mapped file. If this fails, ``EOS`` is raised.
|
## opens a memory mapped file. If this fails, ``EOS`` is raised.
|
||||||
|
|
@ -71,7 +108,7 @@ proc open*(filename: string, mode: TFileMode = fmRead,
|
||||||
when useWinUnicode:
|
when useWinUnicode:
|
||||||
result.fHandle = callCreateFile(createFileW, newWideCString(filename))
|
result.fHandle = callCreateFile(createFileW, newWideCString(filename))
|
||||||
else:
|
else:
|
||||||
result.fHandle = callCreateFile(CreateFileA, filename)
|
result.fHandle = callCreateFile(createFileA, filename)
|
||||||
|
|
||||||
if result.fHandle == INVALID_HANDLE_VALUE:
|
if result.fHandle == INVALID_HANDLE_VALUE:
|
||||||
fail(osLastError(), "error opening file")
|
fail(osLastError(), "error opening file")
|
||||||
|
|
@ -170,14 +207,14 @@ proc close*(f: var TMemFile) =
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
if f.fHandle != INVALID_HANDLE_VALUE:
|
if f.fHandle != INVALID_HANDLE_VALUE:
|
||||||
lastErr = osLastError()
|
|
||||||
error = unmapViewOfFile(f.mem) == 0
|
error = unmapViewOfFile(f.mem) == 0
|
||||||
|
lastErr = osLastError()
|
||||||
error = (closeHandle(f.mapHandle) == 0) or error
|
error = (closeHandle(f.mapHandle) == 0) or error
|
||||||
error = (closeHandle(f.fHandle) == 0) or error
|
error = (closeHandle(f.fHandle) == 0) or error
|
||||||
else:
|
else:
|
||||||
if f.handle != 0:
|
if f.handle != 0:
|
||||||
lastErr = osLastError()
|
|
||||||
error = munmap(f.mem, f.size) != 0
|
error = munmap(f.mem, f.size) != 0
|
||||||
|
lastErr = osLastError()
|
||||||
error = (close(f.handle) != 0) or error
|
error = (close(f.handle) != 0) or error
|
||||||
|
|
||||||
f.size = 0
|
f.size = 0
|
||||||
|
|
|
||||||
1107
lib/pure/net.nim
1107
lib/pure/net.nim
File diff suppressed because it is too large
Load diff
|
|
@ -67,7 +67,7 @@ when withThreads:
|
||||||
|
|
||||||
proc hookAux(st: TStackTrace, costs: int) =
|
proc hookAux(st: TStackTrace, costs: int) =
|
||||||
# this is quite performance sensitive!
|
# this is quite performance sensitive!
|
||||||
when withThreads: Acquire profilingLock
|
when withThreads: acquire profilingLock
|
||||||
inc totalCalls
|
inc totalCalls
|
||||||
var last = high(st)
|
var last = high(st)
|
||||||
while last > 0 and isNil(st[last]): dec last
|
while last > 0 and isNil(st[last]): dec last
|
||||||
|
|
@ -106,7 +106,7 @@ proc hookAux(st: TStackTrace, costs: int) =
|
||||||
h = ((5 * h) + 1) and high(profileData)
|
h = ((5 * h) + 1) and high(profileData)
|
||||||
inc chain
|
inc chain
|
||||||
maxChainLen = max(maxChainLen, chain)
|
maxChainLen = max(maxChainLen, chain)
|
||||||
when withThreads: Release profilingLock
|
when withThreads: release profilingLock
|
||||||
|
|
||||||
when defined(memProfiler):
|
when defined(memProfiler):
|
||||||
const
|
const
|
||||||
|
|
|
||||||
|
|
@ -260,11 +260,12 @@ proc osError*(errorCode: TOSErrorCode) =
|
||||||
##
|
##
|
||||||
## If the error code is ``0`` or an error message could not be retrieved,
|
## If the error code is ``0`` or an error message could not be retrieved,
|
||||||
## the message ``unknown OS error`` will be used.
|
## the message ``unknown OS error`` will be used.
|
||||||
let msg = osErrorMsg(errorCode)
|
var e: ref EOS; new(e)
|
||||||
if msg == "":
|
e.errorCode = errorCode.int32
|
||||||
raise newException(EOS, "unknown OS error")
|
e.msg = osErrorMsg(errorCode)
|
||||||
else:
|
if e.msg == "":
|
||||||
raise newException(EOS, msg)
|
e.msg = "unknown OS error"
|
||||||
|
raise e
|
||||||
|
|
||||||
{.push stackTrace:off.}
|
{.push stackTrace:off.}
|
||||||
proc osLastError*(): TOSErrorCode =
|
proc osLastError*(): TOSErrorCode =
|
||||||
|
|
@ -1037,7 +1038,10 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
||||||
## the process has finished. To execute a program without having a
|
## the process has finished. To execute a program without having a
|
||||||
## shell involved, use the `execProcess` proc of the `osproc`
|
## shell involved, use the `execProcess` proc of the `osproc`
|
||||||
## module.
|
## module.
|
||||||
result = c_system(command) shr 8
|
when defined(linux):
|
||||||
|
result = c_system(command) shr 8
|
||||||
|
else:
|
||||||
|
result = c_system(command)
|
||||||
|
|
||||||
# Environment handling cannot be put into RTL, because the ``envPairs``
|
# Environment handling cannot be put into RTL, because the ``envPairs``
|
||||||
# iterator depends on ``environment``.
|
# iterator depends on ``environment``.
|
||||||
|
|
@ -1189,7 +1193,8 @@ iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} =
|
||||||
res = findFirstFile(pattern, f)
|
res = findFirstFile(pattern, f)
|
||||||
if res != -1:
|
if res != -1:
|
||||||
while true:
|
while true:
|
||||||
if not skipFindData(f):
|
if not skipFindData(f) and
|
||||||
|
(f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) == 0'i32:
|
||||||
yield splitFile(pattern).dir / extractFilename(getFilename(f))
|
yield splitFile(pattern).dir / extractFilename(getFilename(f))
|
||||||
if findNextFile(res, f) == 0'i32: break
|
if findNextFile(res, f) == 0'i32: break
|
||||||
findClose(res)
|
findClose(res)
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,16 @@
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils, os, strtabs, streams, sequtils
|
strutils, os, strtabs, streams
|
||||||
|
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
import winlean
|
import winlean
|
||||||
else:
|
else:
|
||||||
import posix
|
import posix
|
||||||
|
|
||||||
|
when defined(linux):
|
||||||
|
import linux
|
||||||
|
|
||||||
type
|
type
|
||||||
TProcess = object of TObject
|
TProcess = object of TObject
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
@ -44,7 +47,7 @@ type
|
||||||
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
|
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
|
||||||
poParentStreams ## use the parent's streams
|
poParentStreams ## use the parent's streams
|
||||||
|
|
||||||
template poUseShell*: TProcessOption {.deprecated.} = poUsePath
|
const poUseShell* {.deprecated.} = poUsePath
|
||||||
## Deprecated alias for poUsePath.
|
## Deprecated alias for poUsePath.
|
||||||
|
|
||||||
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
|
||||||
|
|
@ -165,6 +168,9 @@ proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
|
||||||
proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl,
|
proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl,
|
||||||
extern: "nosp$1", tags: [].}
|
extern: "nosp$1", tags: [].}
|
||||||
## waits for the process to finish and returns `p`'s error code.
|
## waits for the process to finish and returns `p`'s error code.
|
||||||
|
##
|
||||||
|
## **Warning**: Be careful when using waitForExit for processes created without
|
||||||
|
## poParentStreams because they may fill output buffers, causing deadlock.
|
||||||
|
|
||||||
proc peekExitCode*(p: PProcess): int {.tags: [].}
|
proc peekExitCode*(p: PProcess): int {.tags: [].}
|
||||||
## return -1 if the process is still running. Otherwise the process' exit code
|
## return -1 if the process is still running. Otherwise the process' exit code
|
||||||
|
|
@ -590,6 +596,24 @@ elif not defined(useNimRtl):
|
||||||
copyMem(result[i], addr(x[0]), x.len+1)
|
copyMem(result[i], addr(x[0]), x.len+1)
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
|
type TStartProcessData = object
|
||||||
|
sysCommand: cstring
|
||||||
|
sysArgs: cstringArray
|
||||||
|
sysEnv: cstringArray
|
||||||
|
workingDir: cstring
|
||||||
|
pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint]
|
||||||
|
optionPoUsePath: bool
|
||||||
|
optionPoParentStreams: bool
|
||||||
|
optionPoStdErrToStdOut: bool
|
||||||
|
|
||||||
|
when not defined(useFork):
|
||||||
|
proc startProcessAuxSpawn(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].}
|
||||||
|
proc startProcessAuxFork(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].}
|
||||||
|
{.push stacktrace: off, profiler: off.}
|
||||||
|
proc startProcessAfterFork(data: ptr TStartProcessData) {.
|
||||||
|
tags: [FExecIO, FReadEnv], cdecl.}
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
proc startProcess(command: string,
|
proc startProcess(command: string,
|
||||||
workingDir: string = "",
|
workingDir: string = "",
|
||||||
args: openArray[string] = [],
|
args: openArray[string] = [],
|
||||||
|
|
@ -604,100 +628,49 @@ elif not defined(useNimRtl):
|
||||||
pipe(pStderr) != 0'i32:
|
pipe(pStderr) != 0'i32:
|
||||||
osError(osLastError())
|
osError(osLastError())
|
||||||
|
|
||||||
var sys_command: string
|
var sysCommand: string
|
||||||
var sys_args_raw: seq[string]
|
var sysArgsRaw: seq[string]
|
||||||
if poEvalCommand in options:
|
if poEvalCommand in options:
|
||||||
sys_command = "/bin/sh"
|
sysCommand = "/bin/sh"
|
||||||
sys_args_raw = @[sys_command, "-c", command]
|
sysArgsRaw = @[sysCommand, "-c", command]
|
||||||
assert args.len == 0
|
assert args.len == 0
|
||||||
else:
|
else:
|
||||||
sys_command = command
|
sysCommand = command
|
||||||
sys_args_raw = @[command]
|
sysArgsRaw = @[command]
|
||||||
for arg in args.items:
|
for arg in args.items:
|
||||||
sys_args_raw.add arg
|
sysArgsRaw.add arg
|
||||||
|
|
||||||
var sys_args = allocCStringArray(sys_args_raw)
|
|
||||||
finally: deallocCStringArray(sys_args)
|
|
||||||
|
|
||||||
var pid: TPid
|
var pid: TPid
|
||||||
when defined(posix_spawn) and not defined(useFork):
|
|
||||||
var attr: Tposix_spawnattr
|
|
||||||
var fops: Tposix_spawn_file_actions
|
|
||||||
|
|
||||||
template chck(e: expr) =
|
var sysArgs = allocCStringArray(sysArgsRaw)
|
||||||
if e != 0'i32: osError(osLastError())
|
finally: deallocCStringArray(sysArgs)
|
||||||
|
|
||||||
chck posix_spawn_file_actions_init(fops)
|
var sysEnv = if env == nil:
|
||||||
chck posix_spawnattr_init(attr)
|
envToCStringArray()
|
||||||
|
|
||||||
var mask: Tsigset
|
|
||||||
chck sigemptyset(mask)
|
|
||||||
chck posix_spawnattr_setsigmask(attr, mask)
|
|
||||||
chck posix_spawnattr_setpgroup(attr, 0'i32)
|
|
||||||
|
|
||||||
chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
|
|
||||||
POSIX_SPAWN_SETSIGMASK or
|
|
||||||
POSIX_SPAWN_SETPGROUP)
|
|
||||||
|
|
||||||
if poParentStreams notin options:
|
|
||||||
chck posix_spawn_file_actions_addclose(fops, pStdin[writeIdx])
|
|
||||||
chck posix_spawn_file_actions_adddup2(fops, pStdin[readIdx], readIdx)
|
|
||||||
chck posix_spawn_file_actions_addclose(fops, pStdout[readIdx])
|
|
||||||
chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], writeIdx)
|
|
||||||
chck posix_spawn_file_actions_addclose(fops, pStderr[readIdx])
|
|
||||||
if poStdErrToStdOut in options:
|
|
||||||
chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], 2)
|
|
||||||
else:
|
|
||||||
chck posix_spawn_file_actions_adddup2(fops, p_stderr[writeIdx], 2)
|
|
||||||
|
|
||||||
var sys_env = if env == nil: envToCStringArray() else: envToCStringArray(env)
|
|
||||||
var res: cint
|
|
||||||
# This is incorrect!
|
|
||||||
if workingDir.len > 0: os.setCurrentDir(workingDir)
|
|
||||||
if poUsePath in options:
|
|
||||||
res = posix_spawnp(pid, sys_command, fops, attr, sys_args, sys_env)
|
|
||||||
else:
|
else:
|
||||||
res = posix_spawn(pid, sys_command, fops, attr, sys_args, sys_env)
|
envToCStringArray(env)
|
||||||
deallocCStringArray(sys_env)
|
|
||||||
discard posix_spawn_file_actions_destroy(fops)
|
|
||||||
discard posix_spawnattr_destroy(attr)
|
|
||||||
chck res
|
|
||||||
|
|
||||||
|
finally: deallocCStringArray(sysEnv)
|
||||||
|
|
||||||
|
var data: TStartProcessData
|
||||||
|
data.sysCommand = sysCommand
|
||||||
|
data.sysArgs = sysArgs
|
||||||
|
data.sysEnv = sysEnv
|
||||||
|
data.pStdin = pStdin
|
||||||
|
data.pStdout = pStdout
|
||||||
|
data.pStderr = pStderr
|
||||||
|
data.optionPoParentStreams = poParentStreams in options
|
||||||
|
data.optionPoUsePath = poUsePath in options
|
||||||
|
data.optionPoStdErrToStdOut = poStdErrToStdOut in options
|
||||||
|
data.workingDir = workingDir
|
||||||
|
|
||||||
|
|
||||||
|
when defined(posix_spawn) and not defined(useFork) and
|
||||||
|
not defined(useClone) and not defined(linux):
|
||||||
|
pid = startProcessAuxSpawn(data)
|
||||||
else:
|
else:
|
||||||
pid = fork()
|
pid = startProcessAuxFork(data)
|
||||||
if pid < 0: osError(osLastError())
|
|
||||||
if pid == 0:
|
|
||||||
## child process:
|
|
||||||
|
|
||||||
if poParentStreams notin options:
|
|
||||||
discard close(p_stdin[writeIdx])
|
|
||||||
if dup2(p_stdin[readIdx], readIdx) < 0: osError(osLastError())
|
|
||||||
discard close(p_stdout[readIdx])
|
|
||||||
if dup2(p_stdout[writeIdx], writeIdx) < 0: osError(osLastError())
|
|
||||||
discard close(p_stderr[readIdx])
|
|
||||||
if poStdErrToStdOut in options:
|
|
||||||
if dup2(p_stdout[writeIdx], 2) < 0: osError(osLastError())
|
|
||||||
else:
|
|
||||||
if dup2(p_stderr[writeIdx], 2) < 0: osError(osLastError())
|
|
||||||
|
|
||||||
# Create a new process group
|
|
||||||
if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno))
|
|
||||||
|
|
||||||
if workingDir.len > 0: os.setCurrentDir(workingDir)
|
|
||||||
|
|
||||||
if env == nil:
|
|
||||||
if poUsePath in options:
|
|
||||||
discard execvp(sys_command, sys_args)
|
|
||||||
else:
|
|
||||||
discard execv(sys_command, sys_args)
|
|
||||||
else:
|
|
||||||
var c_env = envToCStringArray(env)
|
|
||||||
if poUsePath in options:
|
|
||||||
discard execvpe(sys_command, sys_args, c_env)
|
|
||||||
else:
|
|
||||||
discard execve(sys_command, sys_args, c_env)
|
|
||||||
# too risky to raise an exception here:
|
|
||||||
quit("execve call failed: " & $strerror(errno))
|
|
||||||
# Parent process. Copy process information.
|
# Parent process. Copy process information.
|
||||||
if poEchoCmd in options:
|
if poEchoCmd in options:
|
||||||
echo(command, " ", join(args, " "))
|
echo(command, " ", join(args, " "))
|
||||||
|
|
@ -723,6 +696,138 @@ elif not defined(useNimRtl):
|
||||||
discard close(pStdin[readIdx])
|
discard close(pStdin[readIdx])
|
||||||
discard close(pStdout[writeIdx])
|
discard close(pStdout[writeIdx])
|
||||||
|
|
||||||
|
when not defined(useFork):
|
||||||
|
proc startProcessAuxSpawn(data: TStartProcessData): TPid =
|
||||||
|
var attr: Tposix_spawnattr
|
||||||
|
var fops: Tposix_spawn_file_actions
|
||||||
|
|
||||||
|
template chck(e: expr) =
|
||||||
|
if e != 0'i32: osError(osLastError())
|
||||||
|
|
||||||
|
chck posix_spawn_file_actions_init(fops)
|
||||||
|
chck posix_spawnattr_init(attr)
|
||||||
|
|
||||||
|
var mask: Tsigset
|
||||||
|
chck sigemptyset(mask)
|
||||||
|
chck posix_spawnattr_setsigmask(attr, mask)
|
||||||
|
chck posix_spawnattr_setpgroup(attr, 0'i32)
|
||||||
|
|
||||||
|
chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
|
||||||
|
POSIX_SPAWN_SETSIGMASK or
|
||||||
|
POSIX_SPAWN_SETPGROUP)
|
||||||
|
|
||||||
|
if not data.optionPoParentStreams:
|
||||||
|
chck posix_spawn_file_actions_addclose(fops, data.pStdin[writeIdx])
|
||||||
|
chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx)
|
||||||
|
chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx])
|
||||||
|
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx)
|
||||||
|
chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx])
|
||||||
|
if data.optionPoStdErrToStdOut:
|
||||||
|
chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2)
|
||||||
|
else:
|
||||||
|
chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2)
|
||||||
|
|
||||||
|
var res: cint
|
||||||
|
# FIXME: chdir is global to process
|
||||||
|
if data.workingDir.len > 0:
|
||||||
|
setCurrentDir($data.workingDir)
|
||||||
|
var pid: TPid
|
||||||
|
|
||||||
|
if data.optionPoUsePath:
|
||||||
|
res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
|
||||||
|
else:
|
||||||
|
res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
|
||||||
|
|
||||||
|
discard posix_spawn_file_actions_destroy(fops)
|
||||||
|
discard posix_spawnattr_destroy(attr)
|
||||||
|
chck res
|
||||||
|
return pid
|
||||||
|
|
||||||
|
proc startProcessAuxFork(data: TStartProcessData): TPid =
|
||||||
|
if pipe(data.pErrorPipe) != 0:
|
||||||
|
osError(osLastError())
|
||||||
|
|
||||||
|
finally:
|
||||||
|
discard close(data.pErrorPipe[readIdx])
|
||||||
|
|
||||||
|
var pid: TPid
|
||||||
|
var dataCopy = data
|
||||||
|
|
||||||
|
when defined(useClone):
|
||||||
|
const stackSize = 65536
|
||||||
|
let stackEnd = cast[clong](alloc(stackSize))
|
||||||
|
let stack = cast[pointer](stackEnd + stackSize)
|
||||||
|
let fn: pointer = startProcessAfterFork
|
||||||
|
pid = clone(fn, stack,
|
||||||
|
cint(CLONE_VM or CLONE_VFORK or SIGCHLD),
|
||||||
|
pointer(addr dataCopy), nil, nil, nil)
|
||||||
|
discard close(data.pErrorPipe[writeIdx])
|
||||||
|
dealloc(stack)
|
||||||
|
else:
|
||||||
|
pid = fork()
|
||||||
|
if pid == 0:
|
||||||
|
startProcessAfterFork(addr(dataCopy))
|
||||||
|
exitnow(1)
|
||||||
|
|
||||||
|
discard close(data.pErrorPipe[writeIdx])
|
||||||
|
if pid < 0: osError(osLastError())
|
||||||
|
|
||||||
|
var error: cint
|
||||||
|
let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error))
|
||||||
|
if sizeRead == sizeof(error):
|
||||||
|
osError($strerror(error))
|
||||||
|
|
||||||
|
return pid
|
||||||
|
|
||||||
|
{.push stacktrace: off, profiler: off.}
|
||||||
|
proc startProcessFail(data: ptr TStartProcessData) =
|
||||||
|
var error: cint = errno
|
||||||
|
discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
|
||||||
|
exitnow(1)
|
||||||
|
|
||||||
|
when defined(macosx):
|
||||||
|
var environ {.importc.}: cstringArray
|
||||||
|
|
||||||
|
proc startProcessAfterFork(data: ptr TStartProcessData) =
|
||||||
|
# Warning: no GC here!
|
||||||
|
# Or anythink that touches global structures - all called nimrod procs
|
||||||
|
# must be marked with noStackFrame. Inspect C code after making changes.
|
||||||
|
if not data.optionPoParentStreams:
|
||||||
|
discard close(data.pStdin[writeIdx])
|
||||||
|
if dup2(data.pStdin[readIdx], readIdx) < 0:
|
||||||
|
startProcessFail(data)
|
||||||
|
discard close(data.pStdout[readIdx])
|
||||||
|
if dup2(data.pStdout[writeIdx], writeIdx) < 0:
|
||||||
|
startProcessFail(data)
|
||||||
|
discard close(data.pStderr[readIdx])
|
||||||
|
if data.optionPoStdErrToStdOut:
|
||||||
|
if dup2(data.pStdout[writeIdx], 2) < 0:
|
||||||
|
startProcessFail(data)
|
||||||
|
else:
|
||||||
|
if dup2(data.pStderr[writeIdx], 2) < 0:
|
||||||
|
startProcessFail(data)
|
||||||
|
|
||||||
|
if data.workingDir.len > 0:
|
||||||
|
if chdir(data.workingDir) < 0:
|
||||||
|
startProcessFail(data)
|
||||||
|
|
||||||
|
discard close(data.pErrorPipe[readIdx])
|
||||||
|
discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
|
||||||
|
|
||||||
|
if data.optionPoUsePath:
|
||||||
|
when defined(macosx):
|
||||||
|
# MacOSX doesn't have execvpe, so we need workaround.
|
||||||
|
# On MacOSX we can arrive here only from fork, so this is safe:
|
||||||
|
environ = data.sysEnv
|
||||||
|
discard execvp(data.sysCommand, data.sysArgs)
|
||||||
|
else:
|
||||||
|
discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv)
|
||||||
|
else:
|
||||||
|
discard execve(data.sysCommand, data.sysArgs, data.sysEnv)
|
||||||
|
|
||||||
|
startProcessFail(data)
|
||||||
|
{.pop}
|
||||||
|
|
||||||
proc close(p: PProcess) =
|
proc close(p: PProcess) =
|
||||||
if p.inStream != nil: close(p.inStream)
|
if p.inStream != nil: close(p.inStream)
|
||||||
if p.outStream != nil: close(p.outStream)
|
if p.outStream != nil: close(p.outStream)
|
||||||
|
|
@ -791,7 +896,10 @@ elif not defined(useNimRtl):
|
||||||
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
|
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
|
||||||
|
|
||||||
proc execCmd(command: string): int =
|
proc execCmd(command: string): int =
|
||||||
result = csystem(command) shr 8
|
when defined(linux):
|
||||||
|
result = csystem(command) shr 8
|
||||||
|
else:
|
||||||
|
result = csystem(command)
|
||||||
|
|
||||||
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
|
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
|
||||||
FD_ZERO(fd)
|
FD_ZERO(fd)
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,7 @@ proc getSymbol(c: var TSqlLexer, tok: var TToken) =
|
||||||
while true:
|
while true:
|
||||||
add(tok.literal, buf[pos])
|
add(tok.literal, buf[pos])
|
||||||
Inc(pos)
|
Inc(pos)
|
||||||
if not (buf[pos] in {'a'..'z','A'..'Z','0'..'9','_','$', '\128'..'\255'}):
|
if buf[pos] notin {'a'..'z','A'..'Z','0'..'9','_','$', '\128'..'\255'}:
|
||||||
break
|
break
|
||||||
c.bufpos = pos
|
c.bufpos = pos
|
||||||
tok.kind = tkIdentifier
|
tok.kind = tkIdentifier
|
||||||
|
|
|
||||||
|
|
@ -836,9 +836,11 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string =
|
||||||
while i < s.len:
|
while i < s.len:
|
||||||
c.ml = 0
|
c.ml = 0
|
||||||
var L = rawMatch(s, pattern, i, c)
|
var L = rawMatch(s, pattern, i, c)
|
||||||
if L < 0: break
|
if L < 0:
|
||||||
yield substr(s, i, i+L-1)
|
inc(i, 1)
|
||||||
inc(i, L)
|
else:
|
||||||
|
yield substr(s, i, i+L-1)
|
||||||
|
inc(i, L)
|
||||||
|
|
||||||
proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
|
proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
|
||||||
nosideEffect, rtl, extern: "npegs$1".} =
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
|
|
|
||||||
421
lib/pure/rawsockets.nim
Normal file
421
lib/pure/rawsockets.nim
Normal file
|
|
@ -0,0 +1,421 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nimrod's Runtime Library
|
||||||
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module implements a low-level cross-platform sockets interface. Look
|
||||||
|
## at the ``net`` module for the higher-level version.
|
||||||
|
|
||||||
|
# TODO: Clean up the exports a bit and everything else in general.
|
||||||
|
|
||||||
|
import unsigned, os
|
||||||
|
|
||||||
|
when hostos == "solaris":
|
||||||
|
{.passl: "-lsocket -lnsl".}
|
||||||
|
|
||||||
|
when defined(Windows):
|
||||||
|
import winlean
|
||||||
|
export WSAEWOULDBLOCK
|
||||||
|
else:
|
||||||
|
import posix
|
||||||
|
export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
|
||||||
|
EINTR, EINPROGRESS
|
||||||
|
|
||||||
|
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
|
||||||
|
inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto
|
||||||
|
|
||||||
|
export
|
||||||
|
SO_ERROR,
|
||||||
|
SOL_SOCKET,
|
||||||
|
SOMAXCONN,
|
||||||
|
SO_ACCEPTCONN, SO_BROADCAST, SO_DEBUG, SO_DONTROUTE,
|
||||||
|
SO_KEEPALIVE, SO_OOBINLINE, SO_REUSEADDR,
|
||||||
|
MSG_PEEK
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
TPort* = distinct uint16 ## port type
|
||||||
|
|
||||||
|
TDomain* = enum ## domain, which specifies the protocol family of the
|
||||||
|
## created socket. Other domains than those that are listed
|
||||||
|
## here are unsupported.
|
||||||
|
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
|
||||||
|
AF_INET = 2, ## for network protocol IPv4 or
|
||||||
|
AF_INET6 = 23 ## for network protocol IPv6.
|
||||||
|
|
||||||
|
TType* = enum ## second argument to `socket` proc
|
||||||
|
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
|
||||||
|
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
|
||||||
|
SOCK_RAW = 3, ## raw protocols atop the network layer.
|
||||||
|
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
|
||||||
|
|
||||||
|
TProtocol* = enum ## third argument to `socket` proc
|
||||||
|
IPPROTO_TCP = 6, ## Transmission control protocol.
|
||||||
|
IPPROTO_UDP = 17, ## User datagram protocol.
|
||||||
|
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
|
||||||
|
IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows.
|
||||||
|
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
|
||||||
|
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
|
||||||
|
|
||||||
|
TServent* {.pure, final.} = object ## information about a service
|
||||||
|
name*: string
|
||||||
|
aliases*: seq[string]
|
||||||
|
port*: TPort
|
||||||
|
proto*: string
|
||||||
|
|
||||||
|
Thostent* {.pure, final.} = object ## information about a given host
|
||||||
|
name*: string
|
||||||
|
aliases*: seq[string]
|
||||||
|
addrtype*: TDomain
|
||||||
|
length*: int
|
||||||
|
addrList*: seq[string]
|
||||||
|
|
||||||
|
when defined(windows):
|
||||||
|
let
|
||||||
|
osInvalidSocket* = winlean.INVALID_SOCKET
|
||||||
|
|
||||||
|
const
|
||||||
|
IOCPARM_MASK* = 127
|
||||||
|
IOC_IN* = int(-2147483648)
|
||||||
|
FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
|
||||||
|
(102 shl 8) or 126
|
||||||
|
|
||||||
|
proc ioctlsocket*(s: TSocketHandle, cmd: clong,
|
||||||
|
argptr: ptr clong): cint {.
|
||||||
|
stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
|
||||||
|
else:
|
||||||
|
let
|
||||||
|
osInvalidSocket* = posix.INVALID_SOCKET
|
||||||
|
|
||||||
|
proc `==`*(a, b: TPort): bool {.borrow.}
|
||||||
|
## ``==`` for ports.
|
||||||
|
|
||||||
|
proc `$`*(p: TPort): string {.borrow.}
|
||||||
|
## returns the port number as a string
|
||||||
|
|
||||||
|
proc toInt*(domain: TDomain): cint
|
||||||
|
## Converts the TDomain enum to a platform-dependent ``cint``.
|
||||||
|
|
||||||
|
proc toInt*(typ: TType): cint
|
||||||
|
## Converts the TType enum to a platform-dependent ``cint``.
|
||||||
|
|
||||||
|
proc toInt*(p: TProtocol): cint
|
||||||
|
## Converts the TProtocol enum to a platform-dependent ``cint``.
|
||||||
|
|
||||||
|
when defined(posix):
|
||||||
|
proc toInt(domain: TDomain): cint =
|
||||||
|
case domain
|
||||||
|
of AF_UNIX: result = posix.AF_UNIX
|
||||||
|
of AF_INET: result = posix.AF_INET
|
||||||
|
of AF_INET6: result = posix.AF_INET6
|
||||||
|
else: discard
|
||||||
|
|
||||||
|
proc toInt(typ: TType): cint =
|
||||||
|
case typ
|
||||||
|
of SOCK_STREAM: result = posix.SOCK_STREAM
|
||||||
|
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
||||||
|
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
|
||||||
|
of SOCK_RAW: result = posix.SOCK_RAW
|
||||||
|
else: discard
|
||||||
|
|
||||||
|
proc toInt(p: TProtocol): cint =
|
||||||
|
case p
|
||||||
|
of IPPROTO_TCP: result = posix.IPPROTO_TCP
|
||||||
|
of IPPROTO_UDP: result = posix.IPPROTO_UDP
|
||||||
|
of IPPROTO_IP: result = posix.IPPROTO_IP
|
||||||
|
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
|
||||||
|
of IPPROTO_RAW: result = posix.IPPROTO_RAW
|
||||||
|
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
|
||||||
|
else: discard
|
||||||
|
|
||||||
|
else:
|
||||||
|
proc toInt(domain: TDomain): cint =
|
||||||
|
result = toU16(ord(domain))
|
||||||
|
|
||||||
|
proc toInt(typ: TType): cint =
|
||||||
|
result = cint(ord(typ))
|
||||||
|
|
||||||
|
proc toInt(p: TProtocol): cint =
|
||||||
|
result = cint(ord(p))
|
||||||
|
|
||||||
|
|
||||||
|
proc newRawSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||||
|
protocol: TProtocol = IPPROTO_TCP): TSocketHandle =
|
||||||
|
## Creates a new socket; returns `InvalidSocket` if an error occurs.
|
||||||
|
socket(toInt(domain), toInt(typ), toInt(protocol))
|
||||||
|
|
||||||
|
proc close*(socket: TSocketHandle) =
|
||||||
|
## closes a socket.
|
||||||
|
when defined(windows):
|
||||||
|
discard winlean.closeSocket(socket)
|
||||||
|
else:
|
||||||
|
discard posix.close(socket)
|
||||||
|
# TODO: These values should not be discarded. An EOS should be raised.
|
||||||
|
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
|
||||||
|
|
||||||
|
proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint =
|
||||||
|
result = bindSocket(socket, name, namelen)
|
||||||
|
|
||||||
|
proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} =
|
||||||
|
## Marks ``socket`` as accepting connections.
|
||||||
|
## ``Backlog`` specifies the maximum length of the
|
||||||
|
## queue of pending connections.
|
||||||
|
when defined(windows):
|
||||||
|
result = winlean.listen(socket, cint(backlog))
|
||||||
|
else:
|
||||||
|
result = posix.listen(socket, cint(backlog))
|
||||||
|
|
||||||
|
proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||||
|
prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo =
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
|
||||||
|
var hints: TAddrInfo
|
||||||
|
result = nil
|
||||||
|
hints.ai_family = toInt(af)
|
||||||
|
hints.ai_socktype = toInt(typ)
|
||||||
|
hints.ai_protocol = toInt(prot)
|
||||||
|
var gaiResult = getAddrInfo(address, $port, addr(hints), result)
|
||||||
|
if gaiResult != 0'i32:
|
||||||
|
when defined(windows):
|
||||||
|
OSError(OSLastError())
|
||||||
|
else:
|
||||||
|
raise newException(EOS, $gai_strerror(gaiResult))
|
||||||
|
|
||||||
|
proc dealloc*(ai: ptr TAddrInfo) =
|
||||||
|
freeaddrinfo(ai)
|
||||||
|
|
||||||
|
proc ntohl*(x: int32): int32 =
|
||||||
|
## Converts 32-bit integers from network to host byte order.
|
||||||
|
## On machines where the host byte order is the same as network byte order,
|
||||||
|
## this is a no-op; otherwise, it performs a 4-byte swap operation.
|
||||||
|
when cpuEndian == bigEndian: result = x
|
||||||
|
else: result = (x shr 24'i32) or
|
||||||
|
(x shr 8'i32 and 0xff00'i32) or
|
||||||
|
(x shl 8'i32 and 0xff0000'i32) or
|
||||||
|
(x shl 24'i32)
|
||||||
|
|
||||||
|
proc ntohs*(x: int16): int16 =
|
||||||
|
## Converts 16-bit integers from network to host byte order. On machines
|
||||||
|
## where the host byte order is the same as network byte order, this is
|
||||||
|
## a no-op; otherwise, it performs a 2-byte swap operation.
|
||||||
|
when cpuEndian == bigEndian: result = x
|
||||||
|
else: result = (x shr 8'i16) or (x shl 8'i16)
|
||||||
|
|
||||||
|
proc htonl*(x: int32): int32 =
|
||||||
|
## Converts 32-bit integers from host to network byte order. On machines
|
||||||
|
## where the host byte order is the same as network byte order, this is
|
||||||
|
## a no-op; otherwise, it performs a 4-byte swap operation.
|
||||||
|
result = rawsockets.ntohl(x)
|
||||||
|
|
||||||
|
proc htons*(x: int16): int16 =
|
||||||
|
## Converts 16-bit positive integers from host to network byte order.
|
||||||
|
## On machines where the host byte order is the same as network byte
|
||||||
|
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
|
||||||
|
result = rawsockets.ntohs(x)
|
||||||
|
|
||||||
|
proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} =
|
||||||
|
## Searches the database from the beginning and finds the first entry for
|
||||||
|
## which the service name specified by ``name`` matches the s_name member
|
||||||
|
## and the protocol name specified by ``proto`` matches the s_proto member.
|
||||||
|
##
|
||||||
|
## On posix this will search through the ``/etc/services`` file.
|
||||||
|
when defined(Windows):
|
||||||
|
var s = winlean.getservbyname(name, proto)
|
||||||
|
else:
|
||||||
|
var s = posix.getservbyname(name, proto)
|
||||||
|
if s == nil: raise newException(EOS, "Service not found.")
|
||||||
|
result.name = $s.s_name
|
||||||
|
result.aliases = cstringArrayToSeq(s.s_aliases)
|
||||||
|
result.port = TPort(s.s_port)
|
||||||
|
result.proto = $s.s_proto
|
||||||
|
|
||||||
|
proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} =
|
||||||
|
## Searches the database from the beginning and finds the first entry for
|
||||||
|
## which the port specified by ``port`` matches the s_port member and the
|
||||||
|
## protocol name specified by ``proto`` matches the s_proto member.
|
||||||
|
##
|
||||||
|
## On posix this will search through the ``/etc/services`` file.
|
||||||
|
when defined(Windows):
|
||||||
|
var s = winlean.getservbyport(ze(int16(port)).cint, proto)
|
||||||
|
else:
|
||||||
|
var s = posix.getservbyport(ze(int16(port)).cint, proto)
|
||||||
|
if s == nil: raise newException(EOS, "Service not found.")
|
||||||
|
result.name = $s.s_name
|
||||||
|
result.aliases = cstringArrayToSeq(s.s_aliases)
|
||||||
|
result.port = TPort(s.s_port)
|
||||||
|
result.proto = $s.s_proto
|
||||||
|
|
||||||
|
proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} =
|
||||||
|
## This function will lookup the hostname of an IP Address.
|
||||||
|
var myaddr: TInAddr
|
||||||
|
myaddr.s_addr = inet_addr(ip)
|
||||||
|
|
||||||
|
when defined(windows):
|
||||||
|
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
|
||||||
|
cint(rawsockets.AF_INET))
|
||||||
|
if s == nil: osError(osLastError())
|
||||||
|
else:
|
||||||
|
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen,
|
||||||
|
cint(posix.AF_INET))
|
||||||
|
if s == nil:
|
||||||
|
raise newException(EOS, $hstrerror(h_errno))
|
||||||
|
|
||||||
|
result.name = $s.h_name
|
||||||
|
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||||
|
when defined(windows):
|
||||||
|
result.addrtype = TDomain(s.h_addrtype)
|
||||||
|
else:
|
||||||
|
if s.h_addrtype == posix.AF_INET:
|
||||||
|
result.addrtype = AF_INET
|
||||||
|
elif s.h_addrtype == posix.AF_INET6:
|
||||||
|
result.addrtype = AF_INET6
|
||||||
|
else:
|
||||||
|
raise newException(EOS, "unknown h_addrtype")
|
||||||
|
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
||||||
|
result.length = int(s.h_length)
|
||||||
|
|
||||||
|
proc getHostByName*(name: string): Thostent {.tags: [FReadIO].} =
|
||||||
|
## This function will lookup the IP address of a hostname.
|
||||||
|
when defined(Windows):
|
||||||
|
var s = winlean.gethostbyname(name)
|
||||||
|
else:
|
||||||
|
var s = posix.gethostbyname(name)
|
||||||
|
if s == nil: osError(osLastError())
|
||||||
|
result.name = $s.h_name
|
||||||
|
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||||
|
when defined(windows):
|
||||||
|
result.addrtype = TDomain(s.h_addrtype)
|
||||||
|
else:
|
||||||
|
if s.h_addrtype == posix.AF_INET:
|
||||||
|
result.addrtype = AF_INET
|
||||||
|
elif s.h_addrtype == posix.AF_INET6:
|
||||||
|
result.addrtype = AF_INET6
|
||||||
|
else:
|
||||||
|
raise newException(EOS, "unknown h_addrtype")
|
||||||
|
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
||||||
|
result.length = int(s.h_length)
|
||||||
|
|
||||||
|
proc getSockName*(socket: TSocketHandle): TPort =
|
||||||
|
## returns the socket's associated port number.
|
||||||
|
var name: Tsockaddr_in
|
||||||
|
when defined(Windows):
|
||||||
|
name.sin_family = int16(ord(AF_INET))
|
||||||
|
else:
|
||||||
|
name.sin_family = posix.AF_INET
|
||||||
|
#name.sin_port = htons(cint16(port))
|
||||||
|
#name.sin_addr.s_addr = htonl(INADDR_ANY)
|
||||||
|
var namelen = sizeof(name).TSocklen
|
||||||
|
if getsockname(socket, cast[ptr TSockAddr](addr(name)),
|
||||||
|
addr(namelen)) == -1'i32:
|
||||||
|
osError(osLastError())
|
||||||
|
result = TPort(rawsockets.ntohs(name.sin_port))
|
||||||
|
|
||||||
|
proc getSockOptInt*(socket: TSocketHandle, level, optname: int): int {.
|
||||||
|
tags: [FReadIO].} =
|
||||||
|
## getsockopt for integer options.
|
||||||
|
var res: cint
|
||||||
|
var size = sizeof(res).TSocklen
|
||||||
|
if getsockopt(socket, cint(level), cint(optname),
|
||||||
|
addr(res), addr(size)) < 0'i32:
|
||||||
|
osError(osLastError())
|
||||||
|
result = int(res)
|
||||||
|
|
||||||
|
proc setSockOptInt*(socket: TSocketHandle, level, optname, optval: int) {.
|
||||||
|
tags: [FWriteIO].} =
|
||||||
|
## setsockopt for integer options.
|
||||||
|
var value = cint(optval)
|
||||||
|
if setsockopt(socket, cint(level), cint(optname), addr(value),
|
||||||
|
sizeof(value).TSocklen) < 0'i32:
|
||||||
|
osError(osLastError())
|
||||||
|
|
||||||
|
proc setBlocking*(s: TSocketHandle, blocking: bool) =
|
||||||
|
## Sets blocking mode on socket.
|
||||||
|
##
|
||||||
|
## Raises EOS on error.
|
||||||
|
when defined(Windows):
|
||||||
|
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
||||||
|
if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
|
||||||
|
osError(osLastError())
|
||||||
|
else: # BSD sockets
|
||||||
|
var x: int = fcntl(s, F_GETFL, 0)
|
||||||
|
if x == -1:
|
||||||
|
osError(osLastError())
|
||||||
|
else:
|
||||||
|
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
|
||||||
|
if fcntl(s, F_SETFL, mode) == -1:
|
||||||
|
osError(osLastError())
|
||||||
|
|
||||||
|
proc timeValFromMilliseconds(timeout = 500): Ttimeval =
|
||||||
|
if timeout != -1:
|
||||||
|
var seconds = timeout div 1000
|
||||||
|
result.tv_sec = seconds.int32
|
||||||
|
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
||||||
|
|
||||||
|
proc createFdSet(fd: var TFdSet, s: seq[TSocketHandle], m: var int) =
|
||||||
|
FD_ZERO(fd)
|
||||||
|
for i in items(s):
|
||||||
|
m = max(m, int(i))
|
||||||
|
FD_SET(i, fd)
|
||||||
|
|
||||||
|
proc pruneSocketSet(s: var seq[TSocketHandle], fd: var TFdSet) =
|
||||||
|
var i = 0
|
||||||
|
var L = s.len
|
||||||
|
while i < L:
|
||||||
|
if FD_ISSET(s[i], fd) == 0'i32:
|
||||||
|
s[i] = s[L-1]
|
||||||
|
dec(L)
|
||||||
|
else:
|
||||||
|
inc(i)
|
||||||
|
setLen(s, L)
|
||||||
|
|
||||||
|
proc select*(readfds: var seq[TSocketHandle], timeout = 500): int =
|
||||||
|
## Traditional select function. This function will return the number of
|
||||||
|
## sockets that are ready to be read from, written to, or which have errors.
|
||||||
|
## If there are none; 0 is returned.
|
||||||
|
## ``Timeout`` is in miliseconds and -1 can be specified for no timeout.
|
||||||
|
##
|
||||||
|
## A socket is removed from the specific ``seq`` when it has data waiting to
|
||||||
|
## be read/written to or has errors (``exceptfds``).
|
||||||
|
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout)
|
||||||
|
|
||||||
|
var rd: TFdSet
|
||||||
|
var m = 0
|
||||||
|
createFdSet((rd), readfds, m)
|
||||||
|
|
||||||
|
if timeout != -1:
|
||||||
|
result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
|
||||||
|
else:
|
||||||
|
result = int(select(cint(m+1), addr(rd), nil, nil, nil))
|
||||||
|
|
||||||
|
pruneSocketSet(readfds, (rd))
|
||||||
|
|
||||||
|
proc selectWrite*(writefds: var seq[TSocketHandle],
|
||||||
|
timeout = 500): int {.tags: [FReadIO].} =
|
||||||
|
## When a socket in ``writefds`` is ready to be written to then a non-zero
|
||||||
|
## value will be returned specifying the count of the sockets which can be
|
||||||
|
## written to. The sockets which can be written to will also be removed
|
||||||
|
## from ``writefds``.
|
||||||
|
##
|
||||||
|
## ``timeout`` is specified in miliseconds and ``-1`` can be specified for
|
||||||
|
## an unlimited time.
|
||||||
|
var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout)
|
||||||
|
|
||||||
|
var wr: TFdSet
|
||||||
|
var m = 0
|
||||||
|
createFdSet((wr), writefds, m)
|
||||||
|
|
||||||
|
if timeout != -1:
|
||||||
|
result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
|
||||||
|
else:
|
||||||
|
result = int(select(cint(m+1), nil, addr(wr), nil, nil))
|
||||||
|
|
||||||
|
pruneSocketSet(writefds, (wr))
|
||||||
|
|
||||||
|
when defined(Windows):
|
||||||
|
var wsa: TWSADATA
|
||||||
|
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2013 Dominik Picheta
|
# (c) Copyright 2014 Dominik Picheta
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -9,212 +9,230 @@
|
||||||
|
|
||||||
# TODO: Docs.
|
# TODO: Docs.
|
||||||
|
|
||||||
import tables, os, unsigned
|
import tables, os, unsigned, hashes
|
||||||
when defined(windows):
|
|
||||||
import winlean
|
when defined(linux): import posix, epoll
|
||||||
else:
|
elif defined(windows): import winlean
|
||||||
import posix
|
|
||||||
|
proc hash*(x: TSocketHandle): THash {.borrow.}
|
||||||
|
proc `$`*(x: TSocketHandle): string {.borrow.}
|
||||||
|
|
||||||
type
|
type
|
||||||
TEvent* = enum
|
TEvent* = enum
|
||||||
EvRead, EvWrite
|
EvRead, EvWrite
|
||||||
|
|
||||||
TSelectorKey* = object
|
PSelectorKey* = ref object
|
||||||
fd: cint
|
fd*: TSocketHandle
|
||||||
events: set[TEvent]
|
events*: set[TEvent] ## The events which ``fd`` listens for.
|
||||||
data: PObject
|
data*: PObject ## User object.
|
||||||
|
|
||||||
TReadyInfo* = tuple[key: TSelectorKey, events: set[TEvent]]
|
TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]]
|
||||||
|
|
||||||
PSelector* = ref object of PObject ## Selector interface.
|
when defined(linux) or defined(nimdoc):
|
||||||
fds*: TTable[cint, TSelectorKey]
|
|
||||||
registerImpl*: proc (s: PSelector, fd: cint, events: set[TEvent],
|
|
||||||
data: PObject): TSelectorKey {.nimcall, tags: [FWriteIO].}
|
|
||||||
unregisterImpl*: proc (s: PSelector, fd: cint): TSelectorKey {.nimcall, tags: [FWriteIO].}
|
|
||||||
selectImpl*: proc (s: PSelector, timeout: int): seq[TReadyInfo] {.nimcall, tags: [FReadIO].}
|
|
||||||
closeImpl*: proc (s: PSelector) {.nimcall.}
|
|
||||||
|
|
||||||
template initSelector(r: expr) =
|
|
||||||
new r
|
|
||||||
r.fds = initTable[cint, TSelectorKey]()
|
|
||||||
|
|
||||||
proc register*(s: PSelector, fd: cint, events: set[TEvent], data: PObject):
|
|
||||||
TSelectorKey =
|
|
||||||
if not s.registerImpl.isNil: result = s.registerImpl(s, fd, events, data)
|
|
||||||
|
|
||||||
proc unregister*(s: PSelector, fd: cint): TSelectorKey =
|
|
||||||
##
|
|
||||||
## **Note:** For the ``epoll`` implementation the resulting ``TSelectorKey``
|
|
||||||
## will only have the ``fd`` field set. This is an optimisation and may
|
|
||||||
## change in the future if a viable use case is presented.
|
|
||||||
if not s.unregisterImpl.isNil: result = s.unregisterImpl(s, fd)
|
|
||||||
|
|
||||||
proc select*(s: PSelector, timeout = 500): seq[TReadyInfo] =
|
|
||||||
##
|
|
||||||
## The ``events`` field of the returned ``key`` contains the original events
|
|
||||||
## for which the ``fd`` was bound. This is contrary to the ``events`` field
|
|
||||||
## of the ``TReadyInfo`` tuple which determines which events are ready
|
|
||||||
## on the ``fd``.
|
|
||||||
|
|
||||||
if not s.selectImpl.isNil: result = s.selectImpl(s, timeout)
|
|
||||||
|
|
||||||
proc close*(s: PSelector) =
|
|
||||||
if not s.closeImpl.isNil: s.closeImpl(s)
|
|
||||||
|
|
||||||
# ---- Select() ----------------------------------------------------------------
|
|
||||||
|
|
||||||
type
|
|
||||||
PSelectSelector* = ref object of PSelector ## Implementation of select()
|
|
||||||
|
|
||||||
proc ssRegister(s: PSelector, fd: cint, events: set[TEvent],
|
|
||||||
data: PObject): TSelectorKey =
|
|
||||||
if s.fds.hasKey(fd):
|
|
||||||
raise newException(EInvalidValue, "FD already exists in selector.")
|
|
||||||
var sk = TSelectorKey(fd: fd, events: events, data: data)
|
|
||||||
s.fds[fd] = sk
|
|
||||||
result = sk
|
|
||||||
|
|
||||||
proc ssUnregister(s: PSelector, fd: cint): TSelectorKey =
|
|
||||||
result = s.fds[fd]
|
|
||||||
s.fds.del(fd)
|
|
||||||
|
|
||||||
proc ssClose(s: PSelector) = nil
|
|
||||||
|
|
||||||
proc timeValFromMilliseconds(timeout: int): TTimeVal =
|
|
||||||
if timeout != -1:
|
|
||||||
var seconds = timeout div 1000
|
|
||||||
result.tv_sec = seconds.int32
|
|
||||||
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
|
||||||
|
|
||||||
proc createFdSet(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey],
|
|
||||||
m: var int) =
|
|
||||||
FD_ZERO(rd); FD_ZERO(wr)
|
|
||||||
for k, v in pairs(fds):
|
|
||||||
if EvRead in v.events:
|
|
||||||
m = max(m, int(k))
|
|
||||||
FD_SET(k, rd)
|
|
||||||
if EvWrite in v.events:
|
|
||||||
m = max(m, int(k))
|
|
||||||
FD_SET(k, wr)
|
|
||||||
|
|
||||||
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey]):
|
|
||||||
seq[TReadyInfo] =
|
|
||||||
result = @[]
|
|
||||||
for k, v in pairs(fds):
|
|
||||||
var events: set[TEvent] = {}
|
|
||||||
if FD_ISSET(k, rd) != 0'i32:
|
|
||||||
events = events + {EvRead}
|
|
||||||
if FD_ISSET(k, wr) != 0'i32:
|
|
||||||
events = events + {EvWrite}
|
|
||||||
result.add((v, events))
|
|
||||||
|
|
||||||
proc select(fds: TTable[cint, TSelectorKey], timeout = 500):
|
|
||||||
seq[TReadyInfo] =
|
|
||||||
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
|
|
||||||
|
|
||||||
var rd, wr: TFdSet
|
|
||||||
var m = 0
|
|
||||||
createFdSet(rd, wr, fds, m)
|
|
||||||
|
|
||||||
var retCode = 0
|
|
||||||
if timeout != -1:
|
|
||||||
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
|
|
||||||
else:
|
|
||||||
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
|
|
||||||
|
|
||||||
if retCode < 0:
|
|
||||||
OSError(OSLastError())
|
|
||||||
elif retCode == 0:
|
|
||||||
return @[]
|
|
||||||
else:
|
|
||||||
return getReadyFDs(rd, wr, fds)
|
|
||||||
|
|
||||||
proc ssSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
|
|
||||||
result = select(s.fds, timeout)
|
|
||||||
|
|
||||||
proc newSelectSelector*(): PSelectSelector =
|
|
||||||
initSelector(result)
|
|
||||||
result.registerImpl = ssRegister
|
|
||||||
result.unregisterImpl = ssUnregister
|
|
||||||
result.selectImpl = ssSelect
|
|
||||||
result.closeImpl = ssClose
|
|
||||||
|
|
||||||
# ---- Epoll -------------------------------------------------------------------
|
|
||||||
|
|
||||||
when defined(linux):
|
|
||||||
import epoll
|
|
||||||
type
|
type
|
||||||
PEpollSelector* = ref object of PSelector
|
PSelector* = ref object
|
||||||
epollFD: cint
|
epollFD: cint
|
||||||
events: array[64, ptr epoll_event]
|
events: array[64, epoll_event]
|
||||||
|
fds: TTable[TSocketHandle, PSelectorKey]
|
||||||
|
|
||||||
TDataWrapper = object
|
proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event =
|
||||||
fd: cint
|
|
||||||
boundEvents: set[TEvent] ## The events which ``fd`` listens for.
|
|
||||||
data: PObject ## User object.
|
|
||||||
|
|
||||||
proc esRegister(s: PSelector, fd: cint, events: set[TEvent],
|
|
||||||
data: PObject): TSelectorKey =
|
|
||||||
var es = PEpollSelector(s)
|
|
||||||
var event: epoll_event
|
|
||||||
if EvRead in events:
|
if EvRead in events:
|
||||||
event.events = EPOLLIN
|
result.events = EPOLLIN
|
||||||
if EvWrite in events:
|
if EvWrite in events:
|
||||||
event.events = event.events or EPOLLOUT
|
result.events = result.events or EPOLLOUT
|
||||||
|
result.events = result.events or EPOLLRDHUP
|
||||||
|
result.data.fd = fd.cint
|
||||||
|
|
||||||
var dw = cast[ptr TDataWrapper](alloc0(sizeof(TDataWrapper))) # TODO: This needs to be dealloc'd
|
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
|
||||||
dw.fd = fd
|
data: PObject): PSelectorKey {.discardable.} =
|
||||||
dw.boundEvents = events
|
## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
|
||||||
dw.data = data
|
## ``events``.
|
||||||
event.data.thePtr = dw
|
var event = createEventStruct(events, fd)
|
||||||
|
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
|
||||||
if epoll_ctl(es.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
|
|
||||||
OSError(OSLastError())
|
OSError(OSLastError())
|
||||||
|
|
||||||
result = TSelectorKey(fd: fd, events: events, data: data)
|
var key = PSelectorKey(fd: fd, events: events, data: data)
|
||||||
|
|
||||||
proc esUnregister(s: PSelector, fd: cint): TSelectorKey =
|
s.fds[fd] = key
|
||||||
# We cannot find out the information about this ``fd`` from the epoll
|
result = key
|
||||||
# context. As such I will simply return an almost empty TSelectorKey.
|
|
||||||
var es = PEpollSelector(s)
|
proc update*(s: PSelector, fd: TSocketHandle,
|
||||||
if epoll_ctl(es.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
events: set[TEvent]): PSelectorKey {.discardable.} =
|
||||||
|
## Updates the events which ``fd`` wants notifications for.
|
||||||
|
if s.fds[fd].events != events:
|
||||||
|
var event = createEventStruct(events, fd)
|
||||||
|
|
||||||
|
s.fds[fd].events = events
|
||||||
|
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
||||||
|
OSError(OSLastError())
|
||||||
|
result = s.fds[fd]
|
||||||
|
|
||||||
|
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
||||||
|
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
|
||||||
|
let err = OSLastError()
|
||||||
|
if err.cint notin {ENOENT, EBADF}: # TODO: Why do we sometimes get an EBADF? Is this normal?
|
||||||
|
OSError(err)
|
||||||
|
result = s.fds[fd]
|
||||||
|
s.fds.del(fd)
|
||||||
|
|
||||||
|
proc close*(s: PSelector) =
|
||||||
|
if s.epollFD.close() != 0: OSError(OSLastError())
|
||||||
|
dealloc(addr s.events) # TODO: Test this
|
||||||
|
|
||||||
|
proc epollHasFd(s: PSelector, fd: TSocketHandle): bool =
|
||||||
|
result = true
|
||||||
|
var event = createEventStruct(s.fds[fd].events, fd)
|
||||||
|
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
|
||||||
|
let err = osLastError()
|
||||||
|
if err.cint in {ENOENT, EBADF}:
|
||||||
|
return false
|
||||||
OSError(OSLastError())
|
OSError(OSLastError())
|
||||||
# We could fill in the ``fds`` TTable to get the info, but that wouldn't
|
|
||||||
# be nice for our memory.
|
|
||||||
result = TSelectorKey(fd: fd, events: {}, data: nil)
|
|
||||||
|
|
||||||
proc esClose(s: PSelector) =
|
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
||||||
var es = PEpollSelector(s)
|
##
|
||||||
if es.epollFD.close() != 0: OSError(OSLastError())
|
## The ``events`` field of the returned ``key`` contains the original events
|
||||||
dealloc(addr es.events) # TODO: Test this
|
## for which the ``fd`` was bound. This is contrary to the ``events`` field
|
||||||
|
## of the ``TReadyInfo`` tuple which determines which events are ready
|
||||||
proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
|
## on the ``fd``.
|
||||||
result = @[]
|
result = @[]
|
||||||
var es = PEpollSelector(s)
|
let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
|
||||||
|
|
||||||
let evNum = epoll_wait(es.epollFD, es.events[0], 64.cint, timeout.cint)
|
|
||||||
if evNum < 0: OSError(OSLastError())
|
if evNum < 0: OSError(OSLastError())
|
||||||
if evNum == 0: return @[]
|
if evNum == 0: return @[]
|
||||||
for i in 0 .. <evNum:
|
for i in 0 .. <evNum:
|
||||||
var evSet: set[TEvent] = {}
|
let fd = s.events[i].data.fd.TSocketHandle
|
||||||
if (es.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
|
||||||
if (es.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
|
||||||
let dw = cast[ptr TDataWrapper](es.events[i].data.thePtr)
|
|
||||||
|
|
||||||
let selectorKey = TSelectorKey(fd: dw.fd, events: dw.boundEvents,
|
var evSet: set[TEvent] = {}
|
||||||
data: dw.data)
|
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
|
||||||
|
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
|
||||||
|
let selectorKey = s.fds[fd]
|
||||||
|
assert selectorKey != nil
|
||||||
result.add((selectorKey, evSet))
|
result.add((selectorKey, evSet))
|
||||||
|
|
||||||
proc newEpollSelector*(): PEpollSelector =
|
#echo("Epoll: ", result[i].key.fd, " ", result[i].events, " ", result[i].key.events)
|
||||||
|
|
||||||
|
proc newSelector*(): PSelector =
|
||||||
new result
|
new result
|
||||||
result.epollFD = epoll_create(64)
|
result.epollFD = epoll_create(64)
|
||||||
result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64))
|
result.events = cast[array[64, epoll_event]](alloc0(sizeof(epoll_event)*64))
|
||||||
|
result.fds = initTable[TSocketHandle, PSelectorKey]()
|
||||||
if result.epollFD < 0:
|
if result.epollFD < 0:
|
||||||
OSError(OSLastError())
|
OSError(OSLastError())
|
||||||
result.registerImpl = esRegister
|
|
||||||
result.unregisterImpl = esUnregister
|
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
||||||
result.closeImpl = esClose
|
## Determines whether selector contains a file descriptor.
|
||||||
result.selectImpl = esSelect
|
if s.fds.hasKey(fd):
|
||||||
|
# Ensure the underlying epoll instance still contains this fd.
|
||||||
|
result = epollHasFd(s, fd)
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc contains*(s: PSelector, key: PSelectorKey): bool =
|
||||||
|
## Determines whether selector contains this selector key. More accurate
|
||||||
|
## than checking if the file descriptor is in the selector because it
|
||||||
|
## ensures that the keys are equal. File descriptors may not always be
|
||||||
|
## unique especially when an fd is closed and then a new one is opened,
|
||||||
|
## the new one may have the same value.
|
||||||
|
return key.fd in s and s.fds[key.fd] == key
|
||||||
|
|
||||||
|
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
|
||||||
|
## Retrieves the selector key for ``fd``.
|
||||||
|
return s.fds[fd]
|
||||||
|
|
||||||
|
elif defined(windows):
|
||||||
|
type
|
||||||
|
PSelector* = ref object
|
||||||
|
fds: TTable[TSocketHandle, PSelectorKey]
|
||||||
|
|
||||||
|
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
|
||||||
|
data: PObject): PSelectorKey {.discardable.} =
|
||||||
|
if s.fds.hasKey(fd):
|
||||||
|
raise newException(EInvalidValue, "File descriptor already exists.")
|
||||||
|
var sk = PSelectorKey(fd: fd, events: events, data: data)
|
||||||
|
s.fds[fd] = sk
|
||||||
|
result = sk
|
||||||
|
|
||||||
|
proc update*(s: PSelector, fd: TSocketHandle,
|
||||||
|
events: set[TEvent]): PSelectorKey {.discardable.} =
|
||||||
|
## Updates the events which ``fd`` wants notifications for.
|
||||||
|
if not s.fds.hasKey(fd):
|
||||||
|
raise newException(EInvalidValue, "File descriptor not found.")
|
||||||
|
|
||||||
|
s.fds[fd].events = events
|
||||||
|
result = s.fds[fd]
|
||||||
|
|
||||||
|
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
|
||||||
|
result = s.fds[fd]
|
||||||
|
s.fds.del(fd)
|
||||||
|
|
||||||
|
proc close*(s: PSelector) = nil
|
||||||
|
|
||||||
|
proc timeValFromMilliseconds(timeout: int): TTimeVal =
|
||||||
|
if timeout != -1:
|
||||||
|
var seconds = timeout div 1000
|
||||||
|
result.tv_sec = seconds.int32
|
||||||
|
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
||||||
|
|
||||||
|
proc createFdSet(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey],
|
||||||
|
m: var int) =
|
||||||
|
FD_ZERO(rd); FD_ZERO(wr)
|
||||||
|
for k, v in pairs(fds):
|
||||||
|
if EvRead in v.events:
|
||||||
|
m = max(m, int(k))
|
||||||
|
FD_SET(k, rd)
|
||||||
|
if EvWrite in v.events:
|
||||||
|
m = max(m, int(k))
|
||||||
|
FD_SET(k, wr)
|
||||||
|
|
||||||
|
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey]):
|
||||||
|
seq[TReadyInfo] =
|
||||||
|
result = @[]
|
||||||
|
for k, v in pairs(fds):
|
||||||
|
var events: set[TEvent] = {}
|
||||||
|
if FD_ISSET(k, rd) != 0'i32:
|
||||||
|
events = events + {EvRead}
|
||||||
|
if FD_ISSET(k, wr) != 0'i32:
|
||||||
|
events = events + {EvWrite}
|
||||||
|
result.add((v, events))
|
||||||
|
|
||||||
|
proc select(fds: TTable[TSocketHandle, PSelectorKey], timeout = 500):
|
||||||
|
seq[TReadyInfo] =
|
||||||
|
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
|
||||||
|
|
||||||
|
var rd, wr: TFdSet
|
||||||
|
var m = 0
|
||||||
|
createFdSet(rd, wr, fds, m)
|
||||||
|
|
||||||
|
var retCode = 0
|
||||||
|
if timeout != -1:
|
||||||
|
retCode = int(select(TSocketHandle(m+1), addr(rd), addr(wr), nil, addr(tv)))
|
||||||
|
else:
|
||||||
|
retCode = int(select(TSocketHandle(m+1), addr(rd), addr(wr), nil, nil))
|
||||||
|
|
||||||
|
if retCode < 0:
|
||||||
|
OSError(OSLastError())
|
||||||
|
elif retCode == 0:
|
||||||
|
return @[]
|
||||||
|
else:
|
||||||
|
return getReadyFDs(rd, wr, fds)
|
||||||
|
|
||||||
|
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
|
||||||
|
result = select(s.fds, timeout)
|
||||||
|
|
||||||
|
proc newSelector*(): PSelector =
|
||||||
|
new result
|
||||||
|
result.fds = initTable[TSocketHandle, PSelectorKey]()
|
||||||
|
|
||||||
|
proc contains*(s: PSelector, fd: TSocketHandle): bool =
|
||||||
|
return s.fds.hasKey(fd)
|
||||||
|
|
||||||
|
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
|
||||||
|
return s.fds[fd]
|
||||||
|
|
||||||
|
elif defined(bsd) or defined(macosx):
|
||||||
|
# TODO: kqueue
|
||||||
|
{.error: "Sorry your platform is not supported yet.".}
|
||||||
|
else:
|
||||||
|
{.error: "Sorry your platform is not supported.".}
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
# Select()
|
# Select()
|
||||||
|
|
@ -224,11 +242,12 @@ when isMainModule:
|
||||||
sock: TSocket
|
sock: TSocket
|
||||||
|
|
||||||
var sock = socket()
|
var sock = socket()
|
||||||
|
sock.setBlocking(false)
|
||||||
sock.connect("irc.freenode.net", TPort(6667))
|
sock.connect("irc.freenode.net", TPort(6667))
|
||||||
|
|
||||||
var selector = newEpollSelector()
|
var selector = newSelector()
|
||||||
var data = PSockWrapper(sock: sock)
|
var data = PSockWrapper(sock: sock)
|
||||||
let key = selector.register(sock.getFD.cint, {EvRead}, data)
|
let key = selector.register(sock.getFD, {EvWrite}, data)
|
||||||
var i = 0
|
var i = 0
|
||||||
while true:
|
while true:
|
||||||
let ready = selector.select(1000)
|
let ready = selector.select(1000)
|
||||||
|
|
@ -236,6 +255,7 @@ when isMainModule:
|
||||||
if ready.len > 0: echo ready[0].events
|
if ready.len > 0: echo ready[0].events
|
||||||
i.inc
|
i.inc
|
||||||
if i == 6:
|
if i == 6:
|
||||||
|
assert selector.unregister(sock.getFD).fd == sock.getFD
|
||||||
selector.close()
|
selector.close()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,202 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# Nimrod's Runtime Library
|
|
||||||
# (c) Copyright 2014 Dominik Picheta
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## This module implements a low-level cross-platform sockets interface. Look
|
|
||||||
## at the ``net`` module for the higher-level version.
|
|
||||||
|
|
||||||
import unsigned, os
|
|
||||||
|
|
||||||
when hostos == "solaris":
|
|
||||||
{.passl: "-lsocket -lnsl".}
|
|
||||||
|
|
||||||
when defined(Windows):
|
|
||||||
import winlean
|
|
||||||
else:
|
|
||||||
import posix
|
|
||||||
|
|
||||||
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
|
|
||||||
inet_ntoa
|
|
||||||
|
|
||||||
type
|
|
||||||
|
|
||||||
TPort* = distinct uint16 ## port type
|
|
||||||
|
|
||||||
TDomain* = enum ## domain, which specifies the protocol family of the
|
|
||||||
## created socket. Other domains than those that are listed
|
|
||||||
## here are unsupported.
|
|
||||||
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
|
|
||||||
AF_INET = 2, ## for network protocol IPv4 or
|
|
||||||
AF_INET6 = 23 ## for network protocol IPv6.
|
|
||||||
|
|
||||||
TType* = enum ## second argument to `socket` proc
|
|
||||||
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
|
|
||||||
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
|
|
||||||
SOCK_RAW = 3, ## raw protocols atop the network layer.
|
|
||||||
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
|
|
||||||
|
|
||||||
TProtocol* = enum ## third argument to `socket` proc
|
|
||||||
IPPROTO_TCP = 6, ## Transmission control protocol.
|
|
||||||
IPPROTO_UDP = 17, ## User datagram protocol.
|
|
||||||
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
|
|
||||||
IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows.
|
|
||||||
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
|
|
||||||
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
|
|
||||||
|
|
||||||
TServent* {.pure, final.} = object ## information about a service
|
|
||||||
name*: string
|
|
||||||
aliases*: seq[string]
|
|
||||||
port*: TPort
|
|
||||||
proto*: string
|
|
||||||
|
|
||||||
Thostent* {.pure, final.} = object ## information about a given host
|
|
||||||
name*: string
|
|
||||||
aliases*: seq[string]
|
|
||||||
addrtype*: TDomain
|
|
||||||
length*: int
|
|
||||||
addrList*: seq[string]
|
|
||||||
|
|
||||||
when defined(windows):
|
|
||||||
let
|
|
||||||
OSInvalidSocket* = winlean.INVALID_SOCKET
|
|
||||||
else:
|
|
||||||
let
|
|
||||||
OSInvalidSocket* = posix.INVALID_SOCKET
|
|
||||||
|
|
||||||
proc `==`*(a, b: TPort): bool {.borrow.}
|
|
||||||
## ``==`` for ports.
|
|
||||||
|
|
||||||
proc `$`*(p: TPort): string {.borrow.}
|
|
||||||
## returns the port number as a string
|
|
||||||
|
|
||||||
proc toInt*(domain: TDomain): cint
|
|
||||||
## Converts the TDomain enum to a platform-dependent ``cint``.
|
|
||||||
|
|
||||||
proc toInt*(typ: TType): cint
|
|
||||||
## Converts the TType enum to a platform-dependent ``cint``.
|
|
||||||
|
|
||||||
proc toInt*(p: TProtocol): cint
|
|
||||||
## Converts the TProtocol enum to a platform-dependent ``cint``.
|
|
||||||
|
|
||||||
when defined(posix):
|
|
||||||
proc toInt(domain: TDomain): cint =
|
|
||||||
case domain
|
|
||||||
of AF_UNIX: result = posix.AF_UNIX
|
|
||||||
of AF_INET: result = posix.AF_INET
|
|
||||||
of AF_INET6: result = posix.AF_INET6
|
|
||||||
else: nil
|
|
||||||
|
|
||||||
proc toInt(typ: TType): cint =
|
|
||||||
case typ
|
|
||||||
of SOCK_STREAM: result = posix.SOCK_STREAM
|
|
||||||
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
|
||||||
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
|
|
||||||
of SOCK_RAW: result = posix.SOCK_RAW
|
|
||||||
else: nil
|
|
||||||
|
|
||||||
proc toInt(p: TProtocol): cint =
|
|
||||||
case p
|
|
||||||
of IPPROTO_TCP: result = posix.IPPROTO_TCP
|
|
||||||
of IPPROTO_UDP: result = posix.IPPROTO_UDP
|
|
||||||
of IPPROTO_IP: result = posix.IPPROTO_IP
|
|
||||||
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
|
|
||||||
of IPPROTO_RAW: result = posix.IPPROTO_RAW
|
|
||||||
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
|
|
||||||
else: nil
|
|
||||||
|
|
||||||
else:
|
|
||||||
proc toInt(domain: TDomain): cint =
|
|
||||||
result = toU16(ord(domain))
|
|
||||||
|
|
||||||
proc toInt(typ: TType): cint =
|
|
||||||
result = cint(ord(typ))
|
|
||||||
|
|
||||||
proc toInt(p: TProtocol): cint =
|
|
||||||
result = cint(ord(p))
|
|
||||||
|
|
||||||
|
|
||||||
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
|
||||||
protocol: TProtocol = IPPROTO_TCP): TSocketHandle =
|
|
||||||
## Creates a new socket; returns `InvalidSocket` if an error occurs.
|
|
||||||
|
|
||||||
# TODO: The function which will use this will raise EOS.
|
|
||||||
socket(toInt(domain), toInt(typ), toInt(protocol))
|
|
||||||
|
|
||||||
proc close*(socket: TSocketHandle) =
|
|
||||||
## closes a socket.
|
|
||||||
when defined(windows):
|
|
||||||
discard winlean.closeSocket(socket)
|
|
||||||
else:
|
|
||||||
discard posix.close(socket)
|
|
||||||
# TODO: These values should not be discarded. An EOS should be raised.
|
|
||||||
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
|
|
||||||
|
|
||||||
proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint =
|
|
||||||
result = bindSocket(socket, name, namelen)
|
|
||||||
|
|
||||||
proc listen*(socket: TSocketHandle, backlog = SOMAXCONN) {.tags: [FReadIO].} =
|
|
||||||
## Marks ``socket`` as accepting connections.
|
|
||||||
## ``Backlog`` specifies the maximum length of the
|
|
||||||
## queue of pending connections.
|
|
||||||
when defined(windows):
|
|
||||||
if winlean.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
|
|
||||||
else:
|
|
||||||
if posix.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
|
|
||||||
|
|
||||||
proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
|
||||||
prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo =
|
|
||||||
##
|
|
||||||
##
|
|
||||||
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
|
|
||||||
var hints: TAddrInfo
|
|
||||||
result = nil
|
|
||||||
hints.ai_family = toInt(af)
|
|
||||||
hints.ai_socktype = toInt(typ)
|
|
||||||
hints.ai_protocol = toInt(prot)
|
|
||||||
var gaiResult = getAddrInfo(address, $port, addr(hints), result)
|
|
||||||
if gaiResult != 0'i32:
|
|
||||||
when defined(windows):
|
|
||||||
OSError(OSLastError())
|
|
||||||
else:
|
|
||||||
raise newException(EOS, $gai_strerror(gaiResult))
|
|
||||||
|
|
||||||
proc dealloc*(ai: ptr TAddrInfo) =
|
|
||||||
freeaddrinfo(ai)
|
|
||||||
|
|
||||||
proc ntohl*(x: int32): int32 =
|
|
||||||
## Converts 32-bit integers from network to host byte order.
|
|
||||||
## On machines where the host byte order is the same as network byte order,
|
|
||||||
## this is a no-op; otherwise, it performs a 4-byte swap operation.
|
|
||||||
when cpuEndian == bigEndian: result = x
|
|
||||||
else: result = (x shr 24'i32) or
|
|
||||||
(x shr 8'i32 and 0xff00'i32) or
|
|
||||||
(x shl 8'i32 and 0xff0000'i32) or
|
|
||||||
(x shl 24'i32)
|
|
||||||
|
|
||||||
proc ntohs*(x: int16): int16 =
|
|
||||||
## Converts 16-bit integers from network to host byte order. On machines
|
|
||||||
## where the host byte order is the same as network byte order, this is
|
|
||||||
## a no-op; otherwise, it performs a 2-byte swap operation.
|
|
||||||
when cpuEndian == bigEndian: result = x
|
|
||||||
else: result = (x shr 8'i16) or (x shl 8'i16)
|
|
||||||
|
|
||||||
proc htonl*(x: int32): int32 =
|
|
||||||
## Converts 32-bit integers from host to network byte order. On machines
|
|
||||||
## where the host byte order is the same as network byte order, this is
|
|
||||||
## a no-op; otherwise, it performs a 4-byte swap operation.
|
|
||||||
result = sockets2.ntohl(x)
|
|
||||||
|
|
||||||
proc htons*(x: int16): int16 =
|
|
||||||
## Converts 16-bit positive integers from host to network byte order.
|
|
||||||
## On machines where the host byte order is the same as network byte
|
|
||||||
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
|
|
||||||
result = sockets2.ntohs(x)
|
|
||||||
|
|
||||||
when defined(Windows):
|
|
||||||
var wsa: TWSADATA
|
|
||||||
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())
|
|
||||||
|
|
@ -557,6 +557,119 @@ proc `$`*(m: TMonth): string =
|
||||||
"November", "December"]
|
"November", "December"]
|
||||||
return lookup[m]
|
return lookup[m]
|
||||||
|
|
||||||
|
proc format_token(info: TTimeInfo, token: string, buf: var string) =
|
||||||
|
## Helper of the format proc to parse individual tokens.
|
||||||
|
##
|
||||||
|
## Pass the found token in the user input string, and the buffer where the
|
||||||
|
## final string is being built. This has to be a var value because certain
|
||||||
|
## formatting tokens require modifying the previous characters.
|
||||||
|
case token
|
||||||
|
of "d":
|
||||||
|
buf.add($info.monthday)
|
||||||
|
of "dd":
|
||||||
|
if info.monthday < 10:
|
||||||
|
buf.add("0")
|
||||||
|
buf.add($info.monthday)
|
||||||
|
of "ddd":
|
||||||
|
buf.add(($info.weekday)[0 .. 2])
|
||||||
|
of "dddd":
|
||||||
|
buf.add($info.weekday)
|
||||||
|
of "h":
|
||||||
|
buf.add($(if info.hour > 12: info.hour - 12 else: info.hour))
|
||||||
|
of "hh":
|
||||||
|
let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
|
||||||
|
if amerHour < 10:
|
||||||
|
buf.add('0')
|
||||||
|
buf.add($amerHour)
|
||||||
|
of "H":
|
||||||
|
buf.add($info.hour)
|
||||||
|
of "HH":
|
||||||
|
if info.hour < 10:
|
||||||
|
buf.add('0')
|
||||||
|
buf.add($info.hour)
|
||||||
|
of "m":
|
||||||
|
buf.add($info.minute)
|
||||||
|
of "mm":
|
||||||
|
if info.minute < 10:
|
||||||
|
buf.add('0')
|
||||||
|
buf.add($info.minute)
|
||||||
|
of "M":
|
||||||
|
buf.add($(int(info.month)+1))
|
||||||
|
of "MM":
|
||||||
|
if info.month < mOct:
|
||||||
|
buf.add('0')
|
||||||
|
buf.add($(int(info.month)+1))
|
||||||
|
of "MMM":
|
||||||
|
buf.add(($info.month)[0..2])
|
||||||
|
of "MMMM":
|
||||||
|
buf.add($info.month)
|
||||||
|
of "s":
|
||||||
|
buf.add($info.second)
|
||||||
|
of "ss":
|
||||||
|
if info.second < 10:
|
||||||
|
buf.add('0')
|
||||||
|
buf.add($info.second)
|
||||||
|
of "t":
|
||||||
|
if info.hour >= 12:
|
||||||
|
buf.add('P')
|
||||||
|
else: buf.add('A')
|
||||||
|
of "tt":
|
||||||
|
if info.hour >= 12:
|
||||||
|
buf.add("PM")
|
||||||
|
else: buf.add("AM")
|
||||||
|
of "y":
|
||||||
|
var fr = ($info.year).len()-1
|
||||||
|
if fr < 0: fr = 0
|
||||||
|
buf.add(($info.year)[fr .. ($info.year).len()-1])
|
||||||
|
of "yy":
|
||||||
|
var fr = ($info.year).len()-2
|
||||||
|
if fr < 0: fr = 0
|
||||||
|
var fyear = ($info.year)[fr .. ($info.year).len()-1]
|
||||||
|
if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear
|
||||||
|
buf.add(fyear)
|
||||||
|
of "yyy":
|
||||||
|
var fr = ($info.year).len()-3
|
||||||
|
if fr < 0: fr = 0
|
||||||
|
var fyear = ($info.year)[fr .. ($info.year).len()-1]
|
||||||
|
if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear
|
||||||
|
buf.add(fyear)
|
||||||
|
of "yyyy":
|
||||||
|
var fr = ($info.year).len()-4
|
||||||
|
if fr < 0: fr = 0
|
||||||
|
var fyear = ($info.year)[fr .. ($info.year).len()-1]
|
||||||
|
if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear
|
||||||
|
buf.add(fyear)
|
||||||
|
of "yyyyy":
|
||||||
|
var fr = ($info.year).len()-5
|
||||||
|
if fr < 0: fr = 0
|
||||||
|
var fyear = ($info.year)[fr .. ($info.year).len()-1]
|
||||||
|
if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear
|
||||||
|
buf.add(fyear)
|
||||||
|
of "z":
|
||||||
|
let hrs = (info.timezone div 60) div 60
|
||||||
|
buf.add($hrs)
|
||||||
|
of "zz":
|
||||||
|
let hrs = (info.timezone div 60) div 60
|
||||||
|
|
||||||
|
buf.add($hrs)
|
||||||
|
if hrs.abs < 10:
|
||||||
|
var atIndex = buf.len-(($hrs).len-(if hrs < 0: 1 else: 0))
|
||||||
|
buf.insert("0", atIndex)
|
||||||
|
of "zzz":
|
||||||
|
let hrs = (info.timezone div 60) div 60
|
||||||
|
|
||||||
|
buf.add($hrs & ":00")
|
||||||
|
if hrs.abs < 10:
|
||||||
|
var atIndex = buf.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0))
|
||||||
|
buf.insert("0", atIndex)
|
||||||
|
of "ZZZ":
|
||||||
|
buf.add(info.tzname)
|
||||||
|
of "":
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
raise newException(EInvalidValue, "Invalid format string: " & token)
|
||||||
|
|
||||||
|
|
||||||
proc format*(info: TTimeInfo, f: string): string =
|
proc format*(info: TTimeInfo, f: string): string =
|
||||||
## This function formats `info` as specified by `f`. The following format
|
## This function formats `info` as specified by `f`. The following format
|
||||||
## specifiers are available:
|
## specifiers are available:
|
||||||
|
|
@ -591,8 +704,11 @@ proc format*(info: TTimeInfo, f: string): string =
|
||||||
## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST``
|
## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST``
|
||||||
## ========== ================================================================================= ================================================
|
## ========== ================================================================================= ================================================
|
||||||
##
|
##
|
||||||
## Other strings can be inserted by putting them in ``''``. For example ``hh'->'mm`` will give ``01->56``.
|
## Other strings can be inserted by putting them in ``''``. For example
|
||||||
## The following characters can be inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ``,``
|
## ``hh'->'mm`` will give ``01->56``. The following characters can be
|
||||||
|
## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]``
|
||||||
|
## ``,``. However you don't need to necessarily separate format specifiers, a
|
||||||
|
## unambiguous format string like ``yyyyMMddhhmmss`` is valid too.
|
||||||
|
|
||||||
result = ""
|
result = ""
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -600,111 +716,7 @@ proc format*(info: TTimeInfo, f: string): string =
|
||||||
while true:
|
while true:
|
||||||
case f[i]
|
case f[i]
|
||||||
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
|
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
|
||||||
case currentF
|
format_token(info, currentF, result)
|
||||||
of "d":
|
|
||||||
result.add($info.monthday)
|
|
||||||
of "dd":
|
|
||||||
if info.monthday < 10:
|
|
||||||
result.add("0")
|
|
||||||
result.add($info.monthday)
|
|
||||||
of "ddd":
|
|
||||||
result.add(($info.weekday)[0 .. 2])
|
|
||||||
of "dddd":
|
|
||||||
result.add($info.weekday)
|
|
||||||
of "h":
|
|
||||||
result.add($(if info.hour > 12: info.hour - 12 else: info.hour))
|
|
||||||
of "hh":
|
|
||||||
let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
|
|
||||||
if amerHour < 10:
|
|
||||||
result.add('0')
|
|
||||||
result.add($amerHour)
|
|
||||||
of "H":
|
|
||||||
result.add($info.hour)
|
|
||||||
of "HH":
|
|
||||||
if info.hour < 10:
|
|
||||||
result.add('0')
|
|
||||||
result.add($info.hour)
|
|
||||||
of "m":
|
|
||||||
result.add($info.minute)
|
|
||||||
of "mm":
|
|
||||||
if info.minute < 10:
|
|
||||||
result.add('0')
|
|
||||||
result.add($info.minute)
|
|
||||||
of "M":
|
|
||||||
result.add($(int(info.month)+1))
|
|
||||||
of "MM":
|
|
||||||
if info.month < mOct:
|
|
||||||
result.add('0')
|
|
||||||
result.add($(int(info.month)+1))
|
|
||||||
of "MMM":
|
|
||||||
result.add(($info.month)[0..2])
|
|
||||||
of "MMMM":
|
|
||||||
result.add($info.month)
|
|
||||||
of "s":
|
|
||||||
result.add($info.second)
|
|
||||||
of "ss":
|
|
||||||
if info.second < 10:
|
|
||||||
result.add('0')
|
|
||||||
result.add($info.second)
|
|
||||||
of "t":
|
|
||||||
if info.hour >= 12:
|
|
||||||
result.add('P')
|
|
||||||
else: result.add('A')
|
|
||||||
of "tt":
|
|
||||||
if info.hour >= 12:
|
|
||||||
result.add("PM")
|
|
||||||
else: result.add("AM")
|
|
||||||
of "y":
|
|
||||||
var fr = ($info.year).len()-1
|
|
||||||
if fr < 0: fr = 0
|
|
||||||
result.add(($info.year)[fr .. ($info.year).len()-1])
|
|
||||||
of "yy":
|
|
||||||
var fr = ($info.year).len()-2
|
|
||||||
if fr < 0: fr = 0
|
|
||||||
var fyear = ($info.year)[fr .. ($info.year).len()-1]
|
|
||||||
if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear
|
|
||||||
result.add(fyear)
|
|
||||||
of "yyy":
|
|
||||||
var fr = ($info.year).len()-3
|
|
||||||
if fr < 0: fr = 0
|
|
||||||
var fyear = ($info.year)[fr .. ($info.year).len()-1]
|
|
||||||
if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear
|
|
||||||
result.add(fyear)
|
|
||||||
of "yyyy":
|
|
||||||
var fr = ($info.year).len()-4
|
|
||||||
if fr < 0: fr = 0
|
|
||||||
var fyear = ($info.year)[fr .. ($info.year).len()-1]
|
|
||||||
if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear
|
|
||||||
result.add(fyear)
|
|
||||||
of "yyyyy":
|
|
||||||
var fr = ($info.year).len()-5
|
|
||||||
if fr < 0: fr = 0
|
|
||||||
var fyear = ($info.year)[fr .. ($info.year).len()-1]
|
|
||||||
if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear
|
|
||||||
result.add(fyear)
|
|
||||||
of "z":
|
|
||||||
let hrs = (info.timezone div 60) div 60
|
|
||||||
result.add($hrs)
|
|
||||||
of "zz":
|
|
||||||
let hrs = (info.timezone div 60) div 60
|
|
||||||
|
|
||||||
result.add($hrs)
|
|
||||||
if hrs.abs < 10:
|
|
||||||
var atIndex = result.len-(($hrs).len-(if hrs < 0: 1 else: 0))
|
|
||||||
result.insert("0", atIndex)
|
|
||||||
of "zzz":
|
|
||||||
let hrs = (info.timezone div 60) div 60
|
|
||||||
|
|
||||||
result.add($hrs & ":00")
|
|
||||||
if hrs.abs < 10:
|
|
||||||
var atIndex = result.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0))
|
|
||||||
result.insert("0", atIndex)
|
|
||||||
of "ZZZ":
|
|
||||||
result.add(info.tzname)
|
|
||||||
of "":
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
raise newException(EInvalidValue, "Invalid format string: " & currentF)
|
|
||||||
|
|
||||||
currentF = ""
|
currentF = ""
|
||||||
if f[i] == '\0': break
|
if f[i] == '\0': break
|
||||||
|
|
@ -716,7 +728,15 @@ proc format*(info: TTimeInfo, f: string): string =
|
||||||
inc(i)
|
inc(i)
|
||||||
else: result.add(f[i])
|
else: result.add(f[i])
|
||||||
|
|
||||||
else: currentF.add(f[i])
|
else:
|
||||||
|
# Check if the letter being added matches previous accumulated buffer.
|
||||||
|
if currentF.len < 1 or currentF[high(currentF)] == f[i]:
|
||||||
|
currentF.add(f[i])
|
||||||
|
else:
|
||||||
|
format_token(info, currentF, result)
|
||||||
|
dec(i) # Move position back to re-process the character separately.
|
||||||
|
currentF = ""
|
||||||
|
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
@ -727,12 +747,16 @@ when isMainModule:
|
||||||
|
|
||||||
var t = getGMTime(fromSeconds(2147483647))
|
var t = getGMTime(fromSeconds(2147483647))
|
||||||
echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy")
|
echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy")
|
||||||
|
echo t.format("ddd ddMMMhhmmssZZZyyyy")
|
||||||
assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
|
assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
|
||||||
|
assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038"
|
||||||
|
|
||||||
assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
||||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
||||||
"19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC"
|
"19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC"
|
||||||
|
|
||||||
|
assert t.format("yyyyMMddhhmmss") == "20380119031407"
|
||||||
|
|
||||||
var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975
|
var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975
|
||||||
assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
||||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
||||||
|
|
|
||||||
6
lib/stdlib.babel
Normal file
6
lib/stdlib.babel
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[Package]
|
||||||
|
name = "stdlib"
|
||||||
|
version = "0.9.0"
|
||||||
|
author = "Dominik Picheta"
|
||||||
|
description = "Nimrod's standard library."
|
||||||
|
license = "MIT"
|
||||||
142
lib/system.nim
142
lib/system.nim
|
|
@ -185,6 +185,8 @@ proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} =
|
||||||
|
|
||||||
when not defined(niminheritable):
|
when not defined(niminheritable):
|
||||||
{.pragma: inheritable.}
|
{.pragma: inheritable.}
|
||||||
|
when not defined(nimunion):
|
||||||
|
{.pragma: unchecked.}
|
||||||
|
|
||||||
const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \
|
const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \
|
||||||
## "fake variables" like 'var EBADF {.importc.}: cint'.
|
## "fake variables" like 'var EBADF {.importc.}: cint'.
|
||||||
|
|
@ -194,9 +196,10 @@ when not defined(JS):
|
||||||
TGenericSeq {.compilerproc, pure, inheritable.} = object
|
TGenericSeq {.compilerproc, pure, inheritable.} = object
|
||||||
len, reserved: int
|
len, reserved: int
|
||||||
PGenericSeq {.exportc.} = ptr TGenericSeq
|
PGenericSeq {.exportc.} = ptr TGenericSeq
|
||||||
|
UncheckedCharArray {.unchecked.} = array[0..100_000_000, char]
|
||||||
# len and space without counting the terminating zero:
|
# len and space without counting the terminating zero:
|
||||||
NimStringDesc {.compilerproc, final.} = object of TGenericSeq
|
NimStringDesc {.compilerproc, final.} = object of TGenericSeq
|
||||||
data: array[0..100_000_000, char]
|
data: UncheckedCharArray
|
||||||
NimString = ptr NimStringDesc
|
NimString = ptr NimStringDesc
|
||||||
|
|
||||||
when not defined(JS) and not defined(NimrodVM):
|
when not defined(JS) and not defined(NimrodVM):
|
||||||
|
|
@ -257,6 +260,7 @@ type
|
||||||
## system raises.
|
## system raises.
|
||||||
EIO* = object of ESystem ## raised if an IO error occured.
|
EIO* = object of ESystem ## raised if an IO error occured.
|
||||||
EOS* = object of ESystem ## raised if an operating system service failed.
|
EOS* = object of ESystem ## raised if an operating system service failed.
|
||||||
|
errorCode*: int32 ## OS-defined error code describing this error.
|
||||||
EInvalidLibrary* = object of EOS ## raised if a dynamic library
|
EInvalidLibrary* = object of EOS ## raised if a dynamic library
|
||||||
## could not be loaded.
|
## could not be loaded.
|
||||||
EResourceExhausted* = object of ESystem ## raised if a resource request
|
EResourceExhausted* = object of ESystem ## raised if a resource request
|
||||||
|
|
@ -1161,6 +1165,14 @@ when not defined(nimrodVM):
|
||||||
## from it before writing to it is undefined behaviour!
|
## from it before writing to it is undefined behaviour!
|
||||||
## The allocated memory belongs to its allocating thread!
|
## The allocated memory belongs to its allocating thread!
|
||||||
## Use `allocShared` to allocate from a shared heap.
|
## Use `allocShared` to allocate from a shared heap.
|
||||||
|
proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
|
||||||
|
## allocates a new memory block with at least ``T.sizeof * size``
|
||||||
|
## bytes. The block has to be freed with ``resize(block, 0)`` or
|
||||||
|
## ``free(block)``. The block is not initialized, so reading
|
||||||
|
## from it before writing to it is undefined behaviour!
|
||||||
|
## The allocated memory belongs to its allocating thread!
|
||||||
|
## Use `createSharedU` to allocate from a shared heap.
|
||||||
|
cast[ptr T](alloc(T.sizeof * size))
|
||||||
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
|
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
|
||||||
## allocates a new memory block with at least ``size`` bytes. The
|
## allocates a new memory block with at least ``size`` bytes. The
|
||||||
## block has to be freed with ``realloc(block, 0)`` or
|
## block has to be freed with ``realloc(block, 0)`` or
|
||||||
|
|
@ -1168,14 +1180,31 @@ when not defined(nimrodVM):
|
||||||
## containing zero, so it is somewhat safer than ``alloc``.
|
## containing zero, so it is somewhat safer than ``alloc``.
|
||||||
## The allocated memory belongs to its allocating thread!
|
## The allocated memory belongs to its allocating thread!
|
||||||
## Use `allocShared0` to allocate from a shared heap.
|
## Use `allocShared0` to allocate from a shared heap.
|
||||||
proc realloc*(p: pointer, newsize: int): pointer {.noconv, rtl, tags: [].}
|
proc create*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
|
||||||
|
## allocates a new memory block with at least ``T.sizeof * size``
|
||||||
|
## bytes. The block has to be freed with ``resize(block, 0)`` or
|
||||||
|
## ``free(block)``. The block is initialized with all bytes
|
||||||
|
## containing zero, so it is somewhat safer than ``createU``.
|
||||||
|
## The allocated memory belongs to its allocating thread!
|
||||||
|
## Use `createShared` to allocate from a shared heap.
|
||||||
|
cast[ptr T](alloc0(T.sizeof * size))
|
||||||
|
proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [].}
|
||||||
## grows or shrinks a given memory block. If p is **nil** then a new
|
## grows or shrinks a given memory block. If p is **nil** then a new
|
||||||
## memory block is returned. In either way the block has at least
|
## memory block is returned. In either way the block has at least
|
||||||
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
|
## ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
|
||||||
## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
|
## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
|
||||||
## be freed with ``dealloc``.
|
## be freed with ``dealloc``.
|
||||||
## The allocated memory belongs to its allocating thread!
|
## The allocated memory belongs to its allocating thread!
|
||||||
## Use `reallocShared` to reallocate from a shared heap.
|
## Use `reallocShared` to reallocate from a shared heap.
|
||||||
|
proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline.} =
|
||||||
|
## grows or shrinks a given memory block. If p is **nil** then a new
|
||||||
|
## memory block is returned. In either way the block has at least
|
||||||
|
## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not
|
||||||
|
## **nil** ``resize`` calls ``free(p)``. In other cases the block
|
||||||
|
## has to be freed with ``free``. The allocated memory belongs to
|
||||||
|
## its allocating thread!
|
||||||
|
## Use `resizeShared` to reallocate from a shared heap.
|
||||||
|
cast[ptr T](realloc(p, T.sizeof * newSize))
|
||||||
proc dealloc*(p: pointer) {.noconv, rtl, tags: [].}
|
proc dealloc*(p: pointer) {.noconv, rtl, tags: [].}
|
||||||
## frees the memory allocated with ``alloc``, ``alloc0`` or
|
## frees the memory allocated with ``alloc``, ``alloc0`` or
|
||||||
## ``realloc``. This procedure is dangerous! If one forgets to
|
## ``realloc``. This procedure is dangerous! If one forgets to
|
||||||
|
|
@ -1184,31 +1213,60 @@ when not defined(nimrodVM):
|
||||||
## or other memory may be corrupted.
|
## or other memory may be corrupted.
|
||||||
## The freed memory must belong to its allocating thread!
|
## The freed memory must belong to its allocating thread!
|
||||||
## Use `deallocShared` to deallocate from a shared heap.
|
## Use `deallocShared` to deallocate from a shared heap.
|
||||||
|
proc free*[T](p: ptr T) {.inline.} =
|
||||||
|
dealloc(p)
|
||||||
proc allocShared*(size: int): pointer {.noconv, rtl.}
|
proc allocShared*(size: int): pointer {.noconv, rtl.}
|
||||||
## allocates a new memory block on the shared heap with at
|
## allocates a new memory block on the shared heap with at
|
||||||
## least ``size`` bytes. The block has to be freed with
|
## least ``size`` bytes. The block has to be freed with
|
||||||
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
|
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
|
||||||
## is not initialized, so reading from it before writing to it is
|
## is not initialized, so reading from it before writing to it is
|
||||||
## undefined behaviour!
|
## undefined behaviour!
|
||||||
|
proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
|
||||||
|
## allocates a new memory block on the shared heap with at
|
||||||
|
## least ``T.sizeof * size`` bytes. The block has to be freed with
|
||||||
|
## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block
|
||||||
|
## is not initialized, so reading from it before writing to it is
|
||||||
|
## undefined behaviour!
|
||||||
|
cast[ptr T](allocShared(T.sizeof * size))
|
||||||
proc allocShared0*(size: int): pointer {.noconv, rtl.}
|
proc allocShared0*(size: int): pointer {.noconv, rtl.}
|
||||||
## allocates a new memory block on the shared heap with at
|
## allocates a new memory block on the shared heap with at
|
||||||
## least ``size`` bytes. The block has to be freed with
|
## least ``size`` bytes. The block has to be freed with
|
||||||
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
|
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
|
||||||
## The block is initialized with all bytes
|
## The block is initialized with all bytes
|
||||||
## containing zero, so it is somewhat safer than ``allocShared``.
|
## containing zero, so it is somewhat safer than ``allocShared``.
|
||||||
proc reallocShared*(p: pointer, newsize: int): pointer {.noconv, rtl.}
|
proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
|
||||||
|
## allocates a new memory block on the shared heap with at
|
||||||
|
## least ``T.sizeof * size`` bytes. The block has to be freed with
|
||||||
|
## ``resizeShared(block, 0)`` or ``freeShared(block)``.
|
||||||
|
## The block is initialized with all bytes
|
||||||
|
## containing zero, so it is somewhat safer than ``createSharedU``.
|
||||||
|
cast[ptr T](allocShared0(T.sizeof * size))
|
||||||
|
proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl.}
|
||||||
## grows or shrinks a given memory block on the heap. If p is **nil**
|
## grows or shrinks a given memory block on the heap. If p is **nil**
|
||||||
## then a new memory block is returned. In either way the block has at least
|
## then a new memory block is returned. In either way the block has at
|
||||||
## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
|
## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
|
||||||
## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
|
## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
|
||||||
## block has to be freed with ``deallocShared``.
|
## block has to be freed with ``deallocShared``.
|
||||||
|
proc resizeShared*[T](p: ptr T, newSize: Natural): ptr T {.inline.} =
|
||||||
|
## grows or shrinks a given memory block on the heap. If p is **nil**
|
||||||
|
## then a new memory block is returned. In either way the block has at
|
||||||
|
## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is
|
||||||
|
## not **nil** ``resizeShared`` calls ``freeShared(p)``. In other
|
||||||
|
## cases the block has to be freed with ``freeShared``.
|
||||||
|
cast[ptr T](reallocShared(p, T.sizeof * newSize))
|
||||||
proc deallocShared*(p: pointer) {.noconv, rtl.}
|
proc deallocShared*(p: pointer) {.noconv, rtl.}
|
||||||
## frees the memory allocated with ``allocShared``, ``allocShared0`` or
|
## frees the memory allocated with ``allocShared``, ``allocShared0`` or
|
||||||
## ``reallocShared``. This procedure is dangerous! If one forgets to
|
## ``reallocShared``. This procedure is dangerous! If one forgets to
|
||||||
## free the memory a leak occurs; if one tries to access freed
|
## free the memory a leak occurs; if one tries to access freed
|
||||||
## memory (or just freeing it twice!) a core dump may happen
|
## memory (or just freeing it twice!) a core dump may happen
|
||||||
## or other memory may be corrupted.
|
## or other memory may be corrupted.
|
||||||
|
proc freeShared*[T](p: ptr T) {.inline.} =
|
||||||
|
## frees the memory allocated with ``createShared``, ``createSharedU`` or
|
||||||
|
## ``resizeShared``. This procedure is dangerous! If one forgets to
|
||||||
|
## free the memory a leak occurs; if one tries to access freed
|
||||||
|
## memory (or just freeing it twice!) a core dump may happen
|
||||||
|
## or other memory may be corrupted.
|
||||||
|
deallocShared(p)
|
||||||
|
|
||||||
proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
|
proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
|
||||||
## swaps the values `a` and `b`. This is often more efficient than
|
## swaps the values `a` and `b`. This is often more efficient than
|
||||||
|
|
@ -1399,20 +1457,6 @@ iterator items*[IX, T](a: array[IX, T]): T {.inline.} =
|
||||||
if i >= high(IX): break
|
if i >= high(IX): break
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
iterator items*[T](a: seq[T]): T {.inline.} =
|
|
||||||
## iterates over each item of `a`.
|
|
||||||
var i = 0
|
|
||||||
while i < len(a):
|
|
||||||
yield a[i]
|
|
||||||
inc(i)
|
|
||||||
|
|
||||||
iterator items*(a: string): char {.inline.} =
|
|
||||||
## iterates over each item of `a`.
|
|
||||||
var i = 0
|
|
||||||
while i < len(a):
|
|
||||||
yield a[i]
|
|
||||||
inc(i)
|
|
||||||
|
|
||||||
iterator items*[T](a: set[T]): T {.inline.} =
|
iterator items*[T](a: set[T]): T {.inline.} =
|
||||||
## iterates over each element of `a`. `items` iterates only over the
|
## iterates over each element of `a`. `items` iterates only over the
|
||||||
## elements that are really in the set (and not over the ones the set is
|
## elements that are really in the set (and not over the ones the set is
|
||||||
|
|
@ -1514,7 +1558,7 @@ when not defined(NimrodVM):
|
||||||
proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
|
proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
|
||||||
result = cast[pointer](x)
|
result = cast[pointer](x)
|
||||||
else:
|
else:
|
||||||
proc seqToPtr[T](x: seq[T]): pointer {.noStackFrame, nosideeffect.} =
|
proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} =
|
||||||
asm """return `x`"""
|
asm """return `x`"""
|
||||||
|
|
||||||
proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
|
proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
|
||||||
|
|
@ -1802,7 +1846,7 @@ type
|
||||||
len*: int ## length of the inspectable slots
|
len*: int ## length of the inspectable slots
|
||||||
|
|
||||||
when defined(JS):
|
when defined(JS):
|
||||||
proc add*(x: var string, y: cstring) {.noStackFrame.} =
|
proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
var len = `x`[0].length-1;
|
var len = `x`[0].length-1;
|
||||||
for (var i = 0; i < `y`.length; ++i) {
|
for (var i = 0; i < `y`.length; ++i) {
|
||||||
|
|
@ -2019,8 +2063,10 @@ when not defined(JS): #and not defined(NimrodVM):
|
||||||
## Flushes `f`'s buffer.
|
## Flushes `f`'s buffer.
|
||||||
|
|
||||||
proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].}
|
proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].}
|
||||||
## Reads all data from the stream `file`. Raises an IO exception
|
## Reads all data from the stream `file`.
|
||||||
## in case of an error
|
##
|
||||||
|
## Raises an IO exception in case of an error. It is an error if the
|
||||||
|
## current file position is not at the beginning of the file.
|
||||||
|
|
||||||
proc readFile*(filename: string): TaintedString {.tags: [FReadIO].}
|
proc readFile*(filename: string): TaintedString {.tags: [FReadIO].}
|
||||||
## Opens a file named `filename` for reading. Then calls `readAll`
|
## Opens a file named `filename` for reading. Then calls `readAll`
|
||||||
|
|
@ -2309,12 +2355,32 @@ when not defined(JS): #and not defined(NimrodVM):
|
||||||
|
|
||||||
when not defined(NimrodVM):
|
when not defined(NimrodVM):
|
||||||
proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
|
proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
|
||||||
## can be used to mark a condition to be likely. This is a hint for the
|
## Hints the optimizer that `val` is likely going to be true.
|
||||||
## optimizer.
|
##
|
||||||
|
## You can use this proc to decorate a branch condition. On certain
|
||||||
|
## platforms this can help the processor predict better which branch is
|
||||||
|
## going to be run. Example:
|
||||||
|
##
|
||||||
|
## .. code-block:: nimrod
|
||||||
|
## for value in inputValues:
|
||||||
|
## if likely(value <= 100):
|
||||||
|
## process(value)
|
||||||
|
## else:
|
||||||
|
## echo "Value too big!"
|
||||||
|
|
||||||
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
|
proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
|
||||||
## can be used to mark a condition to be unlikely. This is a hint for the
|
## Hints the optimizer that `val` is likely going to be false.
|
||||||
## optimizer.
|
##
|
||||||
|
## You can use this proc to decorate a branch condition. On certain
|
||||||
|
## platforms this can help the processor predict better which branch is
|
||||||
|
## going to be run. Example:
|
||||||
|
##
|
||||||
|
## .. code-block:: nimrod
|
||||||
|
## for value in inputValues:
|
||||||
|
## if unlikely(value > 100):
|
||||||
|
## echo "Value too big!"
|
||||||
|
## else:
|
||||||
|
## process(value)
|
||||||
|
|
||||||
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
|
proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
|
||||||
## retrieves the raw proc pointer of the closure `x`. This is
|
## retrieves the raw proc pointer of the closure `x`. This is
|
||||||
|
|
@ -2612,6 +2678,24 @@ template doAssert*(cond: bool, msg = "") =
|
||||||
if not cond:
|
if not cond:
|
||||||
raiseAssert(astToStr(cond) & ' ' & msg)
|
raiseAssert(astToStr(cond) & ' ' & msg)
|
||||||
|
|
||||||
|
iterator items*[T](a: seq[T]): T {.inline.} =
|
||||||
|
## iterates over each item of `a`.
|
||||||
|
var i = 0
|
||||||
|
let L = len(a)
|
||||||
|
while i < L:
|
||||||
|
yield a[i]
|
||||||
|
inc(i)
|
||||||
|
assert(len(a) == L, "seq modified while iterating over it")
|
||||||
|
|
||||||
|
iterator items*(a: string): char {.inline.} =
|
||||||
|
## iterates over each item of `a`.
|
||||||
|
var i = 0
|
||||||
|
let L = len(a)
|
||||||
|
while i < L:
|
||||||
|
yield a[i]
|
||||||
|
inc(i)
|
||||||
|
assert(len(a) == L, "string modified while iterating over it")
|
||||||
|
|
||||||
when not defined(nimhygiene):
|
when not defined(nimhygiene):
|
||||||
{.pragma: inject.}
|
{.pragma: inject.}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ const
|
||||||
when asmVersion and not defined(gcc) and not defined(llvm_gcc):
|
when asmVersion and not defined(gcc) and not defined(llvm_gcc):
|
||||||
# assembler optimized versions for compilers that
|
# assembler optimized versions for compilers that
|
||||||
# have an intel syntax assembler:
|
# have an intel syntax assembler:
|
||||||
proc addInt(a, b: int): int {.compilerProc, noStackFrame.} =
|
proc addInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
|
||||||
# a in eax, and b in edx
|
# a in eax, and b in edx
|
||||||
asm """
|
asm """
|
||||||
mov eax, `a`
|
mov eax, `a`
|
||||||
|
|
@ -121,7 +121,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
|
||||||
theEnd:
|
theEnd:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc subInt(a, b: int): int {.compilerProc, noStackFrame.} =
|
proc subInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
mov eax, `a`
|
mov eax, `a`
|
||||||
sub eax, `b`
|
sub eax, `b`
|
||||||
|
|
@ -130,7 +130,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
|
||||||
theEnd:
|
theEnd:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc negInt(a: int): int {.compilerProc, noStackFrame.} =
|
proc negInt(a: int): int {.compilerProc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
mov eax, `a`
|
mov eax, `a`
|
||||||
neg eax
|
neg eax
|
||||||
|
|
@ -139,7 +139,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
|
||||||
theEnd:
|
theEnd:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc divInt(a, b: int): int {.compilerProc, noStackFrame.} =
|
proc divInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
mov eax, `a`
|
mov eax, `a`
|
||||||
mov ecx, `b`
|
mov ecx, `b`
|
||||||
|
|
@ -150,7 +150,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
|
||||||
theEnd:
|
theEnd:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc modInt(a, b: int): int {.compilerProc, noStackFrame.} =
|
proc modInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
mov eax, `a`
|
mov eax, `a`
|
||||||
mov ecx, `b`
|
mov ecx, `b`
|
||||||
|
|
@ -162,7 +162,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
|
||||||
mov eax, edx
|
mov eax, edx
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc mulInt(a, b: int): int {.compilerProc, noStackFrame.} =
|
proc mulInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
mov eax, `a`
|
mov eax, `a`
|
||||||
mov ecx, `b`
|
mov ecx, `b`
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ proc scanAndAppendWord(src: cstring, a: var TStaticStr, start: int): int =
|
||||||
while True:
|
while True:
|
||||||
case src[result]
|
case src[result]
|
||||||
of 'a'..'z', '0'..'9': add(a, src[result])
|
of 'a'..'z', '0'..'9': add(a, src[result])
|
||||||
of '_': nil # just skip it
|
of '_': discard # just skip it
|
||||||
of 'A'..'Z': add(a, chr(ord(src[result]) - ord('A') + ord('a')))
|
of 'A'..'Z': add(a, chr(ord(src[result]) - ord('A') + ord('a')))
|
||||||
else: break
|
else: break
|
||||||
inc(result)
|
inc(result)
|
||||||
|
|
@ -203,7 +203,7 @@ proc scanNumber(src: cstring, a: var int, start: int): int =
|
||||||
while true:
|
while true:
|
||||||
case src[result]
|
case src[result]
|
||||||
of '0'..'9': a = a * 10 + ord(src[result]) - ord('0')
|
of '0'..'9': a = a * 10 + ord(src[result]) - ord('0')
|
||||||
of '_': nil # skip underscores (nice for long line numbers)
|
of '_': discard # skip underscores (nice for long line numbers)
|
||||||
else: break
|
else: break
|
||||||
inc(result)
|
inc(result)
|
||||||
|
|
||||||
|
|
@ -524,7 +524,7 @@ proc lineHookImpl() {.nimcall.} =
|
||||||
of dbBreakpoints:
|
of dbBreakpoints:
|
||||||
# debugger is only interested in breakpoints
|
# debugger is only interested in breakpoints
|
||||||
checkForBreakpoint()
|
checkForBreakpoint()
|
||||||
else: nil
|
else: discard
|
||||||
|
|
||||||
proc watchpointHookImpl(name: cstring) {.nimcall.} =
|
proc watchpointHookImpl(name: cstring) {.nimcall.} =
|
||||||
dbgWriteStackTrace(framePtr)
|
dbgWriteStackTrace(framePtr)
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ type
|
||||||
PCallFrame = ptr TCallFrame
|
PCallFrame = ptr TCallFrame
|
||||||
TCallFrame {.importc, nodecl, final.} = object
|
TCallFrame {.importc, nodecl, final.} = object
|
||||||
prev: PCallFrame
|
prev: PCallFrame
|
||||||
procname: CString
|
procname: cstring
|
||||||
line: int # current line number
|
line: int # current line number
|
||||||
filename: CString
|
filename: cstring
|
||||||
|
|
||||||
var
|
var
|
||||||
framePtr {.importc, nodecl, volatile.}: PCallFrame
|
framePtr {.importc, nodecl, volatile.}: PCallFrame
|
||||||
|
|
@ -48,7 +48,7 @@ proc getCurrentExceptionMsg*(): string =
|
||||||
|
|
||||||
proc auxWriteStackTrace(f: PCallFrame): string =
|
proc auxWriteStackTrace(f: PCallFrame): string =
|
||||||
type
|
type
|
||||||
TTempFrame = tuple[procname: CString, line: int]
|
TTempFrame = tuple[procname: cstring, line: int]
|
||||||
var
|
var
|
||||||
it = f
|
it = f
|
||||||
i = 0
|
i = 0
|
||||||
|
|
@ -84,7 +84,7 @@ proc rawWriteStackTrace(): string =
|
||||||
framePtr = nil
|
framePtr = nil
|
||||||
|
|
||||||
proc raiseException(e: ref E_Base, ename: cstring) {.
|
proc raiseException(e: ref E_Base, ename: cstring) {.
|
||||||
compilerproc, noStackFrame.} =
|
compilerproc, asmNoStackFrame.} =
|
||||||
e.name = ename
|
e.name = ename
|
||||||
if excHandler != nil:
|
if excHandler != nil:
|
||||||
excHandler.exc = e
|
excHandler.exc = e
|
||||||
|
|
@ -104,7 +104,7 @@ proc raiseException(e: ref E_Base, ename: cstring) {.
|
||||||
alert(buf)
|
alert(buf)
|
||||||
asm """throw `e`;"""
|
asm """throw `e`;"""
|
||||||
|
|
||||||
proc reraiseException() {.compilerproc, noStackFrame.} =
|
proc reraiseException() {.compilerproc, asmNoStackFrame.} =
|
||||||
if excHandler == nil:
|
if excHandler == nil:
|
||||||
raise newException(ENoExceptionToReraise, "no exception to reraise")
|
raise newException(ENoExceptionToReraise, "no exception to reraise")
|
||||||
else:
|
else:
|
||||||
|
|
@ -125,7 +125,7 @@ proc raiseIndexError() {.compilerproc, noreturn.} =
|
||||||
proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
|
proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
|
||||||
raise newException(EInvalidField, f & " is not accessible")
|
raise newException(EInvalidField, f & " is not accessible")
|
||||||
|
|
||||||
proc SetConstr() {.varargs, noStackFrame, compilerproc.} =
|
proc SetConstr() {.varargs, asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = {};
|
var result = {};
|
||||||
for (var i = 0; i < arguments.length; ++i) {
|
for (var i = 0; i < arguments.length; ++i) {
|
||||||
|
|
@ -141,7 +141,7 @@ proc SetConstr() {.varargs, noStackFrame, compilerproc.} =
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} =
|
proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = [];
|
var result = [];
|
||||||
for (var i = 0; i < `c`.length; ++i) {
|
for (var i = 0; i < `c`.length; ++i) {
|
||||||
|
|
@ -151,7 +151,7 @@ proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} =
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} =
|
proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var len = `s`.length-1;
|
var len = `s`.length-1;
|
||||||
var result = new Array(len);
|
var result = new Array(len);
|
||||||
|
|
@ -162,7 +162,7 @@ proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} =
|
||||||
return result.join("");
|
return result.join("");
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc mnewString(len: int): string {.noStackFrame, compilerproc.} =
|
proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = new Array(`len`+1);
|
var result = new Array(`len`+1);
|
||||||
result[0] = 0;
|
result[0] = 0;
|
||||||
|
|
@ -170,7 +170,7 @@ proc mnewString(len: int): string {.noStackFrame, compilerproc.} =
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc SetCard(a: int): int {.compilerproc, noStackFrame.} =
|
proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} =
|
||||||
# argument type is a fake
|
# argument type is a fake
|
||||||
asm """
|
asm """
|
||||||
var result = 0;
|
var result = 0;
|
||||||
|
|
@ -178,14 +178,14 @@ proc SetCard(a: int): int {.compilerproc, noStackFrame.} =
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc SetEq(a, b: int): bool {.compilerproc, noStackFrame.} =
|
proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
for (var elem in `a`) { if (!`b`[elem]) return false; }
|
for (var elem in `a`) { if (!`b`[elem]) return false; }
|
||||||
for (var elem in `b`) { if (!`a`[elem]) return false; }
|
for (var elem in `b`) { if (!`a`[elem]) return false; }
|
||||||
return true;
|
return true;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc SetLe(a, b: int): bool {.compilerproc, noStackFrame.} =
|
proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
for (var elem in `a`) { if (!`b`[elem]) return false; }
|
for (var elem in `a`) { if (!`b`[elem]) return false; }
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -194,7 +194,7 @@ proc SetLe(a, b: int): bool {.compilerproc, noStackFrame.} =
|
||||||
proc SetLt(a, b: int): bool {.compilerproc.} =
|
proc SetLt(a, b: int): bool {.compilerproc.} =
|
||||||
result = SetLe(a, b) and not SetEq(a, b)
|
result = SetLe(a, b) and not SetEq(a, b)
|
||||||
|
|
||||||
proc SetMul(a, b: int): int {.compilerproc, noStackFrame.} =
|
proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
var result = {};
|
var result = {};
|
||||||
for (var elem in `a`) {
|
for (var elem in `a`) {
|
||||||
|
|
@ -203,7 +203,7 @@ proc SetMul(a, b: int): int {.compilerproc, noStackFrame.} =
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc SetPlus(a, b: int): int {.compilerproc, noStackFrame.} =
|
proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
var result = {};
|
var result = {};
|
||||||
for (var elem in `a`) { result[elem] = true; }
|
for (var elem in `a`) { result[elem] = true; }
|
||||||
|
|
@ -211,7 +211,7 @@ proc SetPlus(a, b: int): int {.compilerproc, noStackFrame.} =
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc SetMinus(a, b: int): int {.compilerproc, noStackFrame.} =
|
proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
var result = {};
|
var result = {};
|
||||||
for (var elem in `a`) {
|
for (var elem in `a`) {
|
||||||
|
|
@ -220,7 +220,7 @@ proc SetMinus(a, b: int): int {.compilerproc, noStackFrame.} =
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc cmpStrings(a, b: string): int {.noStackFrame, compilerProc.} =
|
proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} =
|
||||||
asm """
|
asm """
|
||||||
if (`a` == `b`) return 0;
|
if (`a` == `b`) return 0;
|
||||||
if (!`a`) return -1;
|
if (!`a`) return -1;
|
||||||
|
|
@ -234,7 +234,7 @@ proc cmpStrings(a, b: string): int {.noStackFrame, compilerProc.} =
|
||||||
|
|
||||||
proc cmp(x, y: string): int = return cmpStrings(x, y)
|
proc cmp(x, y: string): int = return cmpStrings(x, y)
|
||||||
|
|
||||||
proc eqStrings(a, b: string): bool {.noStackFrame, compilerProc.} =
|
proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
|
||||||
asm """
|
asm """
|
||||||
if (`a` == `b`) return true;
|
if (`a` == `b`) return true;
|
||||||
if ((!`a`) || (!`b`)) return false;
|
if ((!`a`) || (!`b`)) return false;
|
||||||
|
|
@ -300,7 +300,7 @@ type
|
||||||
setAttributeNode*: proc (attr: ref TNode) {.nimcall.}
|
setAttributeNode*: proc (attr: ref TNode) {.nimcall.}
|
||||||
|
|
||||||
when defined(kwin):
|
when defined(kwin):
|
||||||
proc rawEcho {.compilerproc, nostackframe.} =
|
proc rawEcho {.compilerproc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
var buf = "";
|
var buf = "";
|
||||||
for (var i = 0; i < arguments.length; ++i) {
|
for (var i = 0; i < arguments.length; ++i) {
|
||||||
|
|
@ -312,7 +312,7 @@ when defined(kwin):
|
||||||
elif defined(nodejs):
|
elif defined(nodejs):
|
||||||
proc ewriteln(x: cstring) = log(x)
|
proc ewriteln(x: cstring) = log(x)
|
||||||
|
|
||||||
proc rawEcho {.compilerproc, nostackframe.} =
|
proc rawEcho {.compilerproc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
var buf = "";
|
var buf = "";
|
||||||
for (var i = 0; i < arguments.length; ++i) {
|
for (var i = 0; i < arguments.length; ++i) {
|
||||||
|
|
@ -345,42 +345,42 @@ else:
|
||||||
node.appendChild(document.createElement("br"))
|
node.appendChild(document.createElement("br"))
|
||||||
|
|
||||||
# Arithmetic:
|
# Arithmetic:
|
||||||
proc addInt(a, b: int): int {.noStackFrame, compilerproc.} =
|
proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` + `b`;
|
var result = `a` + `b`;
|
||||||
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
|
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc subInt(a, b: int): int {.noStackFrame, compilerproc.} =
|
proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` - `b`;
|
var result = `a` - `b`;
|
||||||
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
|
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc mulInt(a, b: int): int {.noStackFrame, compilerproc.} =
|
proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` * `b`;
|
var result = `a` * `b`;
|
||||||
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
|
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc divInt(a, b: int): int {.noStackFrame, compilerproc.} =
|
proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
if (`b` == 0) `raiseDivByZero`();
|
if (`b` == 0) `raiseDivByZero`();
|
||||||
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
|
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
|
||||||
return Math.floor(`a` / `b`);
|
return Math.floor(`a` / `b`);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc modInt(a, b: int): int {.noStackFrame, compilerproc.} =
|
proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
if (`b` == 0) `raiseDivByZero`();
|
if (`b` == 0) `raiseDivByZero`();
|
||||||
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
|
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
|
||||||
return Math.floor(`a` % `b`);
|
return Math.floor(`a` % `b`);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc addInt64(a, b: int): int {.noStackFrame, compilerproc.} =
|
proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` + `b`;
|
var result = `a` + `b`;
|
||||||
if (result > 9223372036854775807
|
if (result > 9223372036854775807
|
||||||
|
|
@ -388,7 +388,7 @@ proc addInt64(a, b: int): int {.noStackFrame, compilerproc.} =
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc subInt64(a, b: int): int {.noStackFrame, compilerproc.} =
|
proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` - `b`;
|
var result = `a` - `b`;
|
||||||
if (result > 9223372036854775807
|
if (result > 9223372036854775807
|
||||||
|
|
@ -396,7 +396,7 @@ proc subInt64(a, b: int): int {.noStackFrame, compilerproc.} =
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc mulInt64(a, b: int): int {.noStackFrame, compilerproc.} =
|
proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
var result = `a` * `b`;
|
var result = `a` * `b`;
|
||||||
if (result > 9223372036854775807
|
if (result > 9223372036854775807
|
||||||
|
|
@ -404,90 +404,89 @@ proc mulInt64(a, b: int): int {.noStackFrame, compilerproc.} =
|
||||||
return result;
|
return result;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc divInt64(a, b: int): int {.noStackFrame, compilerproc.} =
|
proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
if (`b` == 0) `raiseDivByZero`();
|
if (`b` == 0) `raiseDivByZero`();
|
||||||
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
|
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
|
||||||
return Math.floor(`a` / `b`);
|
return Math.floor(`a` / `b`);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc modInt64(a, b: int): int {.noStackFrame, compilerproc.} =
|
proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
if (`b` == 0) `raiseDivByZero`();
|
if (`b` == 0) `raiseDivByZero`();
|
||||||
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
|
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
|
||||||
return Math.floor(`a` % `b`);
|
return Math.floor(`a` % `b`);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc NegInt(a: int): int {.compilerproc.} =
|
proc negInt(a: int): int {.compilerproc.} =
|
||||||
result = a*(-1)
|
result = a*(-1)
|
||||||
|
|
||||||
proc NegInt64(a: int64): int64 {.compilerproc.} =
|
proc negInt64(a: int64): int64 {.compilerproc.} =
|
||||||
result = a*(-1)
|
result = a*(-1)
|
||||||
|
|
||||||
proc AbsInt(a: int): int {.compilerproc.} =
|
proc absInt(a: int): int {.compilerproc.} =
|
||||||
result = if a < 0: a*(-1) else: a
|
result = if a < 0: a*(-1) else: a
|
||||||
|
|
||||||
proc AbsInt64(a: int64): int64 {.compilerproc.} =
|
proc absInt64(a: int64): int64 {.compilerproc.} =
|
||||||
result = if a < 0: a*(-1) else: a
|
result = if a < 0: a*(-1) else: a
|
||||||
|
|
||||||
proc LeU(a, b: int): bool {.compilerproc.} =
|
proc leU(a, b: int): bool {.compilerproc.} =
|
||||||
result = abs(a) <= abs(b)
|
result = abs(a) <= abs(b)
|
||||||
|
|
||||||
proc LtU(a, b: int): bool {.compilerproc.} =
|
proc ltU(a, b: int): bool {.compilerproc.} =
|
||||||
result = abs(a) < abs(b)
|
result = abs(a) < abs(b)
|
||||||
|
|
||||||
proc LeU64(a, b: int64): bool {.compilerproc.} =
|
proc leU64(a, b: int64): bool {.compilerproc.} =
|
||||||
result = abs(a) <= abs(b)
|
result = abs(a) <= abs(b)
|
||||||
|
proc ltU64(a, b: int64): bool {.compilerproc.} =
|
||||||
proc LtU64(a, b: int64): bool {.compilerproc.} =
|
|
||||||
result = abs(a) < abs(b)
|
result = abs(a) < abs(b)
|
||||||
|
|
||||||
proc AddU(a, b: int): int {.compilerproc.} =
|
proc addU(a, b: int): int {.compilerproc.} =
|
||||||
result = abs(a) + abs(b)
|
result = abs(a) + abs(b)
|
||||||
proc AddU64(a, b: int64): int64 {.compilerproc.} =
|
proc addU64(a, b: int64): int64 {.compilerproc.} =
|
||||||
result = abs(a) + abs(b)
|
result = abs(a) + abs(b)
|
||||||
|
|
||||||
proc SubU(a, b: int): int {.compilerproc.} =
|
proc subU(a, b: int): int {.compilerproc.} =
|
||||||
result = abs(a) - abs(b)
|
result = abs(a) - abs(b)
|
||||||
proc SubU64(a, b: int64): int64 {.compilerproc.} =
|
proc subU64(a, b: int64): int64 {.compilerproc.} =
|
||||||
result = abs(a) - abs(b)
|
result = abs(a) - abs(b)
|
||||||
|
|
||||||
proc MulU(a, b: int): int {.compilerproc.} =
|
proc mulU(a, b: int): int {.compilerproc.} =
|
||||||
result = abs(a) * abs(b)
|
result = abs(a) * abs(b)
|
||||||
proc MulU64(a, b: int64): int64 {.compilerproc.} =
|
proc mulU64(a, b: int64): int64 {.compilerproc.} =
|
||||||
result = abs(a) * abs(b)
|
result = abs(a) * abs(b)
|
||||||
|
|
||||||
proc DivU(a, b: int): int {.compilerproc.} =
|
proc divU(a, b: int): int {.compilerproc.} =
|
||||||
result = abs(a) div abs(b)
|
result = abs(a) div abs(b)
|
||||||
proc DivU64(a, b: int64): int64 {.compilerproc.} =
|
proc divU64(a, b: int64): int64 {.compilerproc.} =
|
||||||
result = abs(a) div abs(b)
|
result = abs(a) div abs(b)
|
||||||
|
|
||||||
proc ModU(a, b: int): int {.compilerproc.} =
|
proc modU(a, b: int): int {.compilerproc.} =
|
||||||
result = abs(a) mod abs(b)
|
result = abs(a) mod abs(b)
|
||||||
proc ModU64(a, b: int64): int64 {.compilerproc.} =
|
proc modU64(a, b: int64): int64 {.compilerproc.} =
|
||||||
result = abs(a) mod abs(b)
|
result = abs(a) mod abs(b)
|
||||||
|
|
||||||
proc Ze(a: int): int {.compilerproc.} =
|
proc ze*(a: int): int {.compilerproc.} =
|
||||||
result = a
|
|
||||||
proc Ze64(a: int64): int64 {.compilerproc.} =
|
|
||||||
result = a
|
result = a
|
||||||
|
|
||||||
proc ToU8(a: int): int8 {.noStackFrame, compilerproc.} =
|
proc ze64*(a: int64): int64 {.compilerproc.} =
|
||||||
|
result = a
|
||||||
|
|
||||||
|
proc ToU8(a: int): int8 {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
return `a`;
|
return `a`;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc ToU16(a: int): int16 {.noStackFrame, compilerproc.} =
|
proc ToU16(a: int): int16 {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
return `a`;
|
return `a`;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc ToU32(a: int): int32 {.noStackFrame, compilerproc.} =
|
proc ToU32(a: int): int32 {.asmNoStackFrame, compilerproc.} =
|
||||||
asm """
|
asm """
|
||||||
return `a`;
|
return `a`;
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
|
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
|
||||||
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
|
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
|
||||||
|
|
||||||
|
|
@ -500,9 +499,9 @@ proc isFatPointer(ti: PNimType): bool =
|
||||||
tyArray, tyArrayConstr, tyTuple,
|
tyArray, tyArrayConstr, tyTuple,
|
||||||
tyOpenArray, tySet, tyVar, tyRef, tyPtr}
|
tyOpenArray, tySet, tyVar, tyRef, tyPtr}
|
||||||
|
|
||||||
proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
|
proc nimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
|
||||||
|
|
||||||
proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
|
proc nimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkNone: sysAssert(false, "NimCopyAux")
|
of nkNone: sysAssert(false, "NimCopyAux")
|
||||||
of nkSlot:
|
of nkSlot:
|
||||||
|
|
@ -518,7 +517,7 @@ proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
proc NimCopy(x: pointer, ti: PNimType): pointer =
|
proc nimCopy(x: pointer, ti: PNimType): pointer =
|
||||||
case ti.kind
|
case ti.kind
|
||||||
of tyPtr, tyRef, tyVar, tyNil:
|
of tyPtr, tyRef, tyVar, tyNil:
|
||||||
if not isFatPointer(ti):
|
if not isFatPointer(ti):
|
||||||
|
|
@ -586,7 +585,7 @@ proc genericReset(x: Pointer, ti: PNimType): pointer {.compilerproc.} =
|
||||||
result = nil
|
result = nil
|
||||||
|
|
||||||
proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {.
|
proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {.
|
||||||
noStackFrame, compilerproc.} =
|
asmNoStackFrame, compilerproc.} =
|
||||||
# types are fake
|
# types are fake
|
||||||
asm """
|
asm """
|
||||||
var result = new Array(`len`);
|
var result = new Array(`len`);
|
||||||
|
|
@ -620,7 +619,7 @@ proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
|
||||||
x = x.base
|
x = x.base
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc addChar(x: string, c: char) {.compilerproc, noStackFrame.} =
|
proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} =
|
||||||
asm """
|
asm """
|
||||||
`x`[`x`.length-1] = `c`; `x`.push(0);
|
`x`[`x`.length-1] = `c`; `x`.push(0);
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,11 @@ proc reprChar(x: char): string {.compilerRtl.} =
|
||||||
|
|
||||||
proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
|
proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
|
||||||
# we read an 'int' but this may have been too large, so mask the other bits:
|
# we read an 'int' but this may have been too large, so mask the other bits:
|
||||||
let e = e and (1 shl (typ.size*8)-1)
|
let e = if typ.size == 1: e and 0xff
|
||||||
|
elif typ.size == 2: e and 0xffff
|
||||||
|
else: e
|
||||||
|
# XXX we need a proper narrowing based on signedness here
|
||||||
|
#e and ((1 shl (typ.size*8)) - 1)
|
||||||
if ntfEnumHole notin typ.flags:
|
if ntfEnumHole notin typ.flags:
|
||||||
if e <% typ.node.len:
|
if e <% typ.node.len:
|
||||||
return $typ.node.sons[e].name
|
return $typ.node.sons[e].name
|
||||||
|
|
|
||||||
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