mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 08:54:25 +00:00
generics (in the compiler written in Finch)
fn max[T](T a, T b) -> T struct Stack[T] { []T items } fn Stack[T].push(T x)
The type is worked out from the arguments (max(3, 4), first([1, 2])) and written out
where it cannot be: max[float](1, 2.5), Stack[int](), Pair[str, int](...). Each use makes
its own copy, so a generic costs nothing at run time. Several parameters
(Pair[A, B]), nesting (Stack[Pair[int, str]]), methods, and printing (Stack[int](items: ...))
work. An error inside a generic says which version it was building.
boot.sh: 37 passed, 0 failed (stage 2 and 3 identical).
This commit is contained in:
parent
609ceffe58
commit
04eb0cce55
10 changed files with 575 additions and 7 deletions
105
boot/parser.fch
105
boot/parser.fch
|
|
@ -125,10 +125,80 @@ fn parseType(ptr[Parser] p) -> ast.Type {
|
|||
t.module = name
|
||||
t.name = next(p).text
|
||||
}
|
||||
// Stack[int], Pair[str, int]: the type arguments of a generic struct
|
||||
if at(p, "[") && !cur(p).nl && peekKind(p, 1) != "]" {
|
||||
next(p)
|
||||
while true {
|
||||
t.elem.push(parseType(p))
|
||||
if !accept(p, ",") {
|
||||
break
|
||||
}
|
||||
}
|
||||
expect(p, "]", "']' after the type arguments")
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Stack[int] name: a generic type, then a name on the same line. (a[i] by itself is an index.)
|
||||
fn genericDeclAhead(ptr[Parser] p) -> bool {
|
||||
if !at(p, "ident") || peekKind(p, 1) != "[" || peekKind(p, 2) == "]" {
|
||||
return false
|
||||
}
|
||||
depth := 0
|
||||
i := p.i + 1
|
||||
while i < p.toks.len {
|
||||
k := p.toks[i].kind
|
||||
if k == "[" {
|
||||
depth += 1
|
||||
} else if k == "]" {
|
||||
depth -= 1
|
||||
if depth == 0 {
|
||||
return i + 1 < p.toks.len && p.toks[i + 1].kind == "ident" && !p.toks[i + 1].nl
|
||||
}
|
||||
} else if k == "eof" || p.toks[i].nl && depth == 0 {
|
||||
return false
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// The same, when the name was just taken and the parser stands on the `[`.
|
||||
fn genericCallAheadFrom(ptr[Parser] p) -> bool {
|
||||
p.i -= 1
|
||||
r := genericCallAhead(p)
|
||||
p.i += 1
|
||||
return r
|
||||
}
|
||||
|
||||
// Is the `[` after this name followed, once it closes, by `(`? Then it is max[int](...) and not an index.
|
||||
fn genericCallAhead(ptr[Parser] p) -> bool {
|
||||
if peekKind(p, 1) != "[" || p.toks[p.i + 1].nl || peekKind(p, 2) == "]" {
|
||||
return false
|
||||
}
|
||||
depth := 0
|
||||
i := p.i + 1
|
||||
while i < p.toks.len {
|
||||
k := p.toks[i].kind
|
||||
if k == "[" {
|
||||
depth += 1
|
||||
} else if k == "]" {
|
||||
depth -= 1
|
||||
if depth == 0 {
|
||||
return i + 1 < p.toks.len && p.toks[i + 1].kind == "(" && !p.toks[i + 1].nl
|
||||
}
|
||||
} else if k == "eof" || p.toks[i].nl {
|
||||
return false
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn atDeclaration(ptr[Parser] p) -> bool {
|
||||
if genericDeclAhead(p) {
|
||||
return true
|
||||
}
|
||||
if at(p, "[") {
|
||||
return peekKind(p, 1) == "]"
|
||||
}
|
||||
|
|
@ -262,10 +332,27 @@ fn parseMatch(ptr[Parser] p, bool asExpr) -> ast.Node {
|
|||
return m
|
||||
}
|
||||
|
||||
// [T, U]
|
||||
fn parseTParams(ptr[Parser] p) -> []str {
|
||||
[]str names
|
||||
expect(p, "[", "'['")
|
||||
while true {
|
||||
names.push(expect(p, "ident", "a type parameter name, like T").text)
|
||||
if !accept(p, ",") {
|
||||
break
|
||||
}
|
||||
}
|
||||
expect(p, "]", "']' after the type parameters")
|
||||
return names
|
||||
}
|
||||
|
||||
fn parseStruct(ptr[Parser] p) -> ast.Node {
|
||||
s := node(p, "struct")
|
||||
expect(p, "struct", "'struct'")
|
||||
s.name = expect(p, "ident", "a struct name").text
|
||||
if at(p, "[") {
|
||||
s.tparams = parseTParams(p)
|
||||
}
|
||||
expect(p, "{", "'{'")
|
||||
while !at(p, "}") {
|
||||
if at(p, "eof") {
|
||||
|
|
@ -291,6 +378,9 @@ fn parseFn(ptr[Parser] p) -> ast.Node {
|
|||
f := node(p, "fn")
|
||||
expect(p, "fn", "'fn', 'struct' or 'import'")
|
||||
f.name = expect(p, "ident", "a function name").text
|
||||
if at(p, "[") {
|
||||
f.tparams = parseTParams(p) // fn max[T](...) or the struct's, in fn Stack[T].push(...)
|
||||
}
|
||||
if accept(p, ".") {
|
||||
f.recv = f.name
|
||||
f.name = expect(p, "ident", "a method name").text
|
||||
|
|
@ -648,6 +738,21 @@ fn parsePrimary(ptr[Parser] p) -> ast.Node {
|
|||
if k == "ident" {
|
||||
next(p)
|
||||
e.name = t.text
|
||||
if at(p, "[") && p.toks[p.i - 1].kind == "ident" && genericCallAheadFrom(p) {
|
||||
// max[int](3, 4), Stack[int]()
|
||||
next(p)
|
||||
e.ty = ast.Type(kind: "targs")
|
||||
while true {
|
||||
e.ty.elem.push(parseType(p))
|
||||
if !accept(p, ",") {
|
||||
break
|
||||
}
|
||||
}
|
||||
expect(p, "]", "']' after the type arguments")
|
||||
e.kind = "call"
|
||||
parseArgs(p, addr(e))
|
||||
return e
|
||||
}
|
||||
if at(p, "(") && !cur(p).nl {
|
||||
e.kind = "call"
|
||||
parseArgs(p, addr(e))
|
||||
|
|
|
|||
Loading…
Reference in a new issue