breaking change: 'concept' is now a keyword and used instead of 'generic'
This commit is contained in:
parent
210fab10b2
commit
f6ff01572e
15 changed files with 462 additions and 475 deletions
|
|
@ -35,7 +35,7 @@ type
|
||||||
tkSymbol, # keywords:
|
tkSymbol, # keywords:
|
||||||
tkAddr, tkAnd, tkAs, tkAsm, tkAtomic,
|
tkAddr, tkAnd, tkAs, tkAsm, tkAtomic,
|
||||||
tkBind, tkBlock, tkBreak, tkCase, tkCast,
|
tkBind, tkBlock, tkBreak, tkCase, tkCast,
|
||||||
tkConst, tkContinue, tkConverter,
|
tkConcept, tkConst, tkContinue, tkConverter,
|
||||||
tkDefer, tkDiscard, tkDistinct, tkDiv, tkDo,
|
tkDefer, tkDiscard, tkDistinct, tkDiv, tkDo,
|
||||||
tkElif, tkElse, tkEnd, tkEnum, tkExcept, tkExport,
|
tkElif, tkElse, tkEnd, tkEnum, tkExcept, tkExport,
|
||||||
tkFinally, tkFor, tkFrom, tkFunc,
|
tkFinally, tkFor, tkFrom, tkFunc,
|
||||||
|
|
@ -72,7 +72,7 @@ const
|
||||||
"tkSymbol",
|
"tkSymbol",
|
||||||
"addr", "and", "as", "asm", "atomic",
|
"addr", "and", "as", "asm", "atomic",
|
||||||
"bind", "block", "break", "case", "cast",
|
"bind", "block", "break", "case", "cast",
|
||||||
"const", "continue", "converter",
|
"concept", "const", "continue", "converter",
|
||||||
"defer", "discard", "distinct", "div", "do",
|
"defer", "discard", "distinct", "div", "do",
|
||||||
"elif", "else", "end", "enum", "except", "export",
|
"elif", "else", "end", "enum", "except", "export",
|
||||||
"finally", "for", "from", "func", "generic", "if",
|
"finally", "for", "from", "func", "generic", "if",
|
||||||
|
|
|
||||||
|
|
@ -1073,8 +1073,10 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
|
||||||
else:
|
else:
|
||||||
result = newNodeP(nkObjectTy, p)
|
result = newNodeP(nkObjectTy, p)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
of tkGeneric:
|
of tkGeneric, tkConcept:
|
||||||
if mode == pmTypeDef:
|
if mode == pmTypeDef:
|
||||||
|
if p.tok.tokType == tkGeneric:
|
||||||
|
parMessage(p, warnDeprecated, "use 'concept' instead; 'generic'")
|
||||||
result = parseTypeClass(p)
|
result = parseTypeClass(p)
|
||||||
else:
|
else:
|
||||||
parMessage(p, errInvalidToken, p.tok)
|
parMessage(p, errInvalidToken, p.tok)
|
||||||
|
|
@ -1107,7 +1109,7 @@ proc parseTypeDesc(p: var TParser): PNode =
|
||||||
|
|
||||||
proc parseTypeDefAux(p: var TParser): PNode =
|
proc parseTypeDefAux(p: var TParser): PNode =
|
||||||
#| typeDefAux = simpleExpr
|
#| typeDefAux = simpleExpr
|
||||||
#| | 'generic' typeClass
|
#| | 'concept' typeClass
|
||||||
result = simpleExpr(p, pmTypeDef)
|
result = simpleExpr(p, pmTypeDef)
|
||||||
|
|
||||||
proc makeCall(n: PNode): PNode =
|
proc makeCall(n: PNode): PNode =
|
||||||
|
|
|
||||||
|
|
@ -725,7 +725,7 @@ proc gproc(g: var TSrcGen, n: PNode) =
|
||||||
proc gTypeClassTy(g: var TSrcGen, n: PNode) =
|
proc gTypeClassTy(g: var TSrcGen, n: PNode) =
|
||||||
var c: TContext
|
var c: TContext
|
||||||
initContext(c)
|
initContext(c)
|
||||||
putWithSpace(g, tkGeneric, "generic")
|
putWithSpace(g, tkConcept, "concept")
|
||||||
gsons(g, n[0], c) # arglist
|
gsons(g, n[0], c) # arglist
|
||||||
gsub(g, n[1]) # pragmas
|
gsub(g, n[1]) # pragmas
|
||||||
gsub(g, n[2]) # of
|
gsub(g, n[2]) # of
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ type
|
||||||
wInvalid,
|
wInvalid,
|
||||||
|
|
||||||
wAddr, wAnd, wAs, wAsm, wAtomic,
|
wAddr, wAnd, wAs, wAsm, wAtomic,
|
||||||
wBind, wBlock, wBreak, wCase, wCast, wConst,
|
wBind, wBlock, wBreak, wCase, wCast, wConcept, wConst,
|
||||||
wContinue, wConverter, wDefer, wDiscard, wDistinct, wDiv, wDo,
|
wContinue, wConverter, wDefer, wDiscard, wDistinct, wDiv, wDo,
|
||||||
wElif, wElse, wEnd, wEnum, wExcept, wExport,
|
wElif, wElse, wEnd, wEnum, wExcept, wExport,
|
||||||
wFinally, wFor, wFrom, wFunc, wGeneric, wIf, wImport, wIn,
|
wFinally, wFor, wFrom, wFunc, wGeneric, wIf, wImport, wIn,
|
||||||
|
|
@ -103,7 +103,7 @@ const
|
||||||
|
|
||||||
"addr", "and", "as", "asm", "atomic",
|
"addr", "and", "as", "asm", "atomic",
|
||||||
"bind", "block", "break", "case", "cast",
|
"bind", "block", "break", "case", "cast",
|
||||||
"const", "continue", "converter",
|
"concept", "const", "continue", "converter",
|
||||||
"defer", "discard", "distinct", "div", "do",
|
"defer", "discard", "distinct", "div", "do",
|
||||||
"elif", "else", "end", "enum", "except", "export",
|
"elif", "else", "end", "enum", "except", "export",
|
||||||
"finally", "for", "from", "func", "generic", "if",
|
"finally", "for", "from", "func", "generic", "if",
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
|
||||||
| '.' optInd symbol generalizedLit?
|
| '.' optInd symbol generalizedLit?
|
||||||
| '[' optInd indexExprList optPar ']'
|
| '[' optInd indexExprList optPar ']'
|
||||||
| '{' optInd indexExprList optPar '}'
|
| '{' optInd indexExprList optPar '}'
|
||||||
| &( '`'|IDENT|literal|'cast') expr # command syntax
|
| &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax
|
||||||
condExpr = expr colcom expr optInd
|
condExpr = expr colcom expr optInd
|
||||||
('elif' expr colcom expr optInd)*
|
('elif' expr colcom expr optInd)*
|
||||||
'else' colcom expr
|
'else' colcom expr
|
||||||
|
|
@ -94,7 +94,7 @@ primary = typeKeyw typeDescK
|
||||||
/ 'bind' primary
|
/ 'bind' primary
|
||||||
typeDesc = simpleExpr
|
typeDesc = simpleExpr
|
||||||
typeDefAux = simpleExpr
|
typeDefAux = simpleExpr
|
||||||
| 'generic' typeClass
|
| 'concept' typeClass
|
||||||
macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt
|
macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt
|
||||||
| IND{=} 'elif' expr ':' stmt
|
| IND{=} 'elif' expr ':' stmt
|
||||||
| IND{=} 'except' exprList ':' stmt
|
| IND{=} 'except' exprList ':' stmt
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
addr and as asm atomic
|
addr and as asm atomic
|
||||||
bind block break
|
bind block break
|
||||||
case cast const continue converter
|
case cast concept const continue converter
|
||||||
defer discard distinct div do
|
defer discard distinct div do
|
||||||
elif else end enum except export
|
elif else end enum except export
|
||||||
finally for from func
|
finally for from func
|
||||||
|
|
|
||||||
|
|
@ -203,34 +203,34 @@ be inferred to have the equivalent of the `any` type class and thus they will
|
||||||
match anything without discrimination.
|
match anything without discrimination.
|
||||||
|
|
||||||
|
|
||||||
User defined type classes
|
Concepts
|
||||||
-------------------------
|
--------
|
||||||
|
|
||||||
**Note**: User defined type classes are still in development.
|
**Note**: Concepts are still in development.
|
||||||
|
|
||||||
The user-defined type classes are available in two flavours - declarative and
|
Concepts, also known as "user-defined type classes", are available in two
|
||||||
imperative. Both are used to specify an arbitrary set of requirements that the
|
flavours - declarative and imperative. Both are used to specify an arbitrary
|
||||||
matched type must satisfy.
|
set of requirements that the matched type must satisfy.
|
||||||
|
|
||||||
Declarative type classes are written in the following form:
|
Declarative type classes are written in the following form:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
Comparable = generic x, y
|
Comparable = concept x, y
|
||||||
(x < y) is bool
|
(x < y) is bool
|
||||||
|
|
||||||
Container[T] = generic c
|
Container[T] = concept c
|
||||||
c.len is ordinal
|
c.len is ordinal
|
||||||
items(c) is iterator
|
items(c) is iterator
|
||||||
for value in c:
|
for value in c:
|
||||||
type(value) is T
|
type(value) is T
|
||||||
|
|
||||||
The type class will be matched if:
|
The concept will be matched if:
|
||||||
|
|
||||||
a) all of the expressions within the body can be compiled for the tested type
|
a) all of the expressions within the body can be compiled for the tested type
|
||||||
b) all statically evaluatable boolean expressions in the body must be true
|
b) all statically evaluatable boolean expressions in the body must be true
|
||||||
|
|
||||||
The identifiers following the `generic` keyword represent instances of the
|
The identifiers following the ``concept`` keyword represent instances of the
|
||||||
currently matched type. These instances can act both as variables of the type,
|
currently matched type. These instances can act both as variables of the type,
|
||||||
when used in contexts where a value is expected, and as the type itself when
|
when used in contexts where a value is expected, and as the type itself when
|
||||||
used in contexts where a type is expected.
|
used in contexts where a type is expected.
|
||||||
|
|
@ -240,18 +240,18 @@ type signatures of the required operations, but since type inference and
|
||||||
default parameters are still applied in the provided block, it's also possible
|
default parameters are still applied in the provided block, it's also possible
|
||||||
to encode usage protocols that do not reveal implementation details.
|
to encode usage protocols that do not reveal implementation details.
|
||||||
|
|
||||||
As a special rule providing further convenience when writing type classes, any
|
As a special rule providing further convenience when writing concepts, any
|
||||||
type value appearing in a callable expression will be treated as a variable of
|
type value appearing in a callable expression will be treated as a variable of
|
||||||
the designated type for overload resolution purposes, unless the type value was
|
the designated type for overload resolution purposes, unless the type value was
|
||||||
passed in its explicit ``typedesc[T]`` form:
|
passed in its explicit ``typedesc[T]`` form:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
OutputStream = generic S
|
OutputStream = concept S
|
||||||
write(var S, string)
|
write(var S, string)
|
||||||
|
|
||||||
Much like generics, the user defined type classes will be instantiated exactly
|
Much like generics, concepts are instantiated exactly
|
||||||
once for each tested type and any static code included within them will also be
|
once for each tested type and any static code included within them is also
|
||||||
executed once.
|
executed once.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ static[T]
|
||||||
|
|
||||||
**Note**: static[T] is still in development.
|
**Note**: static[T] is still in development.
|
||||||
|
|
||||||
As their name suggests, static params must be known at compile-time:
|
As their name suggests, static parameters must be known at compile-time:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
|
|
@ -25,22 +25,6 @@ For the purposes of code generation, all static params are treated as
|
||||||
generic params - the proc will be compiled separately for each unique
|
generic params - the proc will be compiled separately for each unique
|
||||||
supplied value (or combination of values).
|
supplied value (or combination of values).
|
||||||
|
|
||||||
Furthermore, the system module defines a `semistatic[T]` type that can be
|
|
||||||
used to declare procs accepting both static and run-time values, which can
|
|
||||||
optimize their body according to the supplied param using the `isStatic(p)`
|
|
||||||
predicate:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
|
||||||
|
|
||||||
# The following proc will be compiled once for each unique static
|
|
||||||
# value and also once for the case handling all run-time values:
|
|
||||||
|
|
||||||
proc re(pattern: semistatic[string]): RegEx =
|
|
||||||
when isStatic(pattern):
|
|
||||||
result = precompiledRegex(pattern)
|
|
||||||
else:
|
|
||||||
result = compile(pattern)
|
|
||||||
|
|
||||||
Static params can also appear in the signatures of generic types:
|
Static params can also appear in the signatures of generic types:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ const
|
||||||
# The following list comes from doc/keywords.txt, make sure it is
|
# The following list comes from doc/keywords.txt, make sure it is
|
||||||
# synchronized with this array by running the module itself as a test case.
|
# synchronized with this array by running the module itself as a test case.
|
||||||
nimKeywords = ["addr", "and", "as", "asm", "atomic", "bind", "block",
|
nimKeywords = ["addr", "and", "as", "asm", "atomic", "bind", "block",
|
||||||
"break", "case", "cast", "const", "continue", "converter",
|
"break", "case", "cast", "concept", "const", "continue", "converter",
|
||||||
"defer", "discard", "distinct", "div", "do",
|
"defer", "discard", "distinct", "div", "do",
|
||||||
"elif", "else", "end", "enum", "except", "export",
|
"elif", "else", "end", "enum", "except", "export",
|
||||||
"finally", "for", "from", "func",
|
"finally", "for", "from", "func",
|
||||||
|
|
@ -554,10 +554,12 @@ when isMainModule:
|
||||||
var keywords: seq[string]
|
var keywords: seq[string]
|
||||||
# Try to work running in both the subdir or at the root.
|
# Try to work running in both the subdir or at the root.
|
||||||
for filename in ["doc/keywords.txt", "../../../doc/keywords.txt"]:
|
for filename in ["doc/keywords.txt", "../../../doc/keywords.txt"]:
|
||||||
except: echo filename, " not found"
|
try:
|
||||||
let input = string(readFile(filename))
|
let input = string(readFile(filename))
|
||||||
keywords = input.split()
|
keywords = input.split()
|
||||||
break
|
break
|
||||||
|
except:
|
||||||
|
echo filename, " not found"
|
||||||
doAssert(not keywords.isNil, "Couldn't read any keywords.txt file!")
|
doAssert(not keywords.isNil, "Couldn't read any keywords.txt file!")
|
||||||
doAssert keywords.len == nimKeywords.len, "No matching lengths"
|
doAssert keywords.len == nimKeywords.len, "No matching lengths"
|
||||||
for i in 0..keywords.len-1:
|
for i in 0..keywords.len-1:
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ template reject(e: expr) =
|
||||||
static: assert(not compiles(e))
|
static: assert(not compiles(e))
|
||||||
|
|
||||||
type
|
type
|
||||||
Container[T] = generic C
|
Container[T] = concept C
|
||||||
C.len is Ordinal
|
C.len is Ordinal
|
||||||
items(c) is iterator
|
items(c) is iterator
|
||||||
for value in C:
|
for value in C:
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ proc isSwizzle(s: string): bool {.compileTime.} =
|
||||||
return false
|
return false
|
||||||
|
|
||||||
type
|
type
|
||||||
StringIsSwizzle = generic value
|
StringIsSwizzle = concept value
|
||||||
value.isSwizzle
|
value.isSwizzle
|
||||||
|
|
||||||
SwizzleStr = static[string] and StringIsSwizzle
|
SwizzleStr = static[string] and StringIsSwizzle
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@ type
|
||||||
TObj = object
|
TObj = object
|
||||||
x: int
|
x: int
|
||||||
|
|
||||||
Sortable = generic x, y
|
Sortable = concept x, y
|
||||||
(x < y) is bool
|
(x < y) is bool
|
||||||
|
|
||||||
ObjectContainer = generic C
|
ObjectContainer = concept C
|
||||||
C.len is Ordinal
|
C.len is Ordinal
|
||||||
for v in items(C):
|
for v in items(C):
|
||||||
v.type is tuple|object
|
v.type is tuple|object
|
||||||
|
|
@ -38,7 +38,7 @@ proc intval(x: int): int = 10
|
||||||
|
|
||||||
# check real and virtual fields
|
# check real and virtual fields
|
||||||
type
|
type
|
||||||
TFoo = generic T
|
TFoo = concept T
|
||||||
T.x
|
T.x
|
||||||
y(T)
|
y(T)
|
||||||
intval T.y
|
intval T.y
|
||||||
|
|
@ -50,7 +50,7 @@ proc testFoo(x: TFoo) = discard
|
||||||
testFoo(TObj(x: 10))
|
testFoo(TObj(x: 10))
|
||||||
|
|
||||||
type
|
type
|
||||||
Matrix[Rows, Cols: static[int]; T] = generic M
|
Matrix[Rows, Cols: static[int]; T] = concept M
|
||||||
M.M == Rows
|
M.M == Rows
|
||||||
M.N == Cols
|
M.N == Cols
|
||||||
M.T is T
|
M.T is T
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
type
|
type
|
||||||
hasFieldX = generic z
|
hasFieldX = concept z
|
||||||
z.x is int
|
z.x is int
|
||||||
|
|
||||||
obj_x = object
|
obj_x = object
|
||||||
|
|
|
||||||
3
todo.txt
3
todo.txt
|
|
@ -5,6 +5,7 @@ version 0.10.4
|
||||||
- improve GC-unsafety warnings
|
- improve GC-unsafety warnings
|
||||||
- make 'nil' work for 'add' and 'len'
|
- make 'nil' work for 'add' and 'len'
|
||||||
- add "all threads are blocked" detection to 'spawn'
|
- add "all threads are blocked" detection to 'spawn'
|
||||||
|
- overloading of '='
|
||||||
|
|
||||||
|
|
||||||
version 1.0
|
version 1.0
|
||||||
|
|
@ -17,8 +18,6 @@ version 1.0
|
||||||
- The bitwise 'not' operator will be renamed to 'bnot' to
|
- The bitwise 'not' operator will be renamed to 'bnot' to
|
||||||
prevent 'not 4 == 5' from compiling. -> requires 'mixin' annotation for procs!
|
prevent 'not 4 == 5' from compiling. -> requires 'mixin' annotation for procs!
|
||||||
- iterators always require a return type
|
- iterators always require a return type
|
||||||
- overloading of '='
|
|
||||||
|
|
||||||
- make nimble part of the distribution
|
- make nimble part of the distribution
|
||||||
- split docgen into separate tool
|
- split docgen into separate tool
|
||||||
- special rule for ``[]=``, items, pairs
|
- special rule for ``[]=``, items, pairs
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ News
|
||||||
and not as ``type((x).name)``. Note that this also affects the AST
|
and not as ``type((x).name)``. Note that this also affects the AST
|
||||||
structure; for immediate macro parameters ``nkCall('addr', 'x')`` is
|
structure; for immediate macro parameters ``nkCall('addr', 'x')`` is
|
||||||
produced instead of ``nkAddr('x')``.
|
produced instead of ``nkAddr('x')``.
|
||||||
|
- ``concept`` is now a keyword and is used instead of ``generic``.
|
||||||
|
|
||||||
|
|
||||||
Language Additions
|
Language Additions
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue