while-continue with labels and docs/tests

This commit is contained in:
Sergey Chernov 2025-05-20 16:47:57 +04:00
parent 22fbd5584b
commit ebeed385e9
2 changed files with 85 additions and 28 deletions

View File

@ -164,33 +164,6 @@ to call it:
If you need to create _unnamed_ function, use alternative syntax (TBD, like { -> } ?) If you need to create _unnamed_ function, use alternative syntax (TBD, like { -> } ?)
# Integral data types
| type | description | literal samples |
|--------|---------------------------------|---------------------|
| Int | 64 bit signed | `1` `-22` `0x1FF` |
| Real | 64 bit double | `1.0`, `2e-11` |
| Bool | boolean | `true` `false` |
| String | unicode string, no limits | "hello" (see below) |
| Void | no value could exist, singleton | void |
| Null | missing value, singleton | null |
| Fn | callable type | |
## String details
### String operations
Concatenation is a `+`: `"hello " + name` works as expected. No confusion.
### Literals
String literal could be multiline:
"Hello
World"
though multiline literals is yet work in progress.
# Flow control operators # Flow control operators
## if-then-else ## if-then-else
@ -248,7 +221,7 @@ Why `void`? Because `break` drops out without the chute, not providing anything
} }
>>> too much >>> too much
## Breaking nested loops ### Breaking nested loops
If you have several loops and want to exit not the inner one, use labels: If you have several loops and want to exit not the inner one, use labels:
@ -268,6 +241,41 @@ If you have several loops and want to exit not the inner one, use labels:
} }
>>> 5/2 situation >>> 5/2 situation
### and continue
We can skip the rest of the loop and restart it, as usual, with `continue` operator.
var count = 0
var countEven = 0
while( count < 10 ) {
count = count + 1
if( count % 2 == 1) continue
countEven = countEven + 1
}
"found even numbers: " + countEven
>>> found even numbers: 5
`continue` can't "return" anything: it just restarts the loop. It can use labeled loops to restart outer ones:
var count = 0
var total = 0
// notice the label:
outerLoop@ while( count < 5 ) {
count = count + 1
var innerCount = 0
while( innerCount < 10 ) {
innerCount = innerCount + 1
if( innerCount == 10 )
continue@outerLoop
}
// we don't reach it because continue above restarts our loop
total = total + 1
}
total
>>> 0
Notice that `total` remains 0 as the end of the outerLoop@ is not reachable: `continue` is always called and always make Ling to skip it.
The label can be any valid identifier, even a keyword, labels exist in their own, isolated world, so no risk of occasional clash. Labels are also scoped to their context and do not exist outside it. The label can be any valid identifier, even a keyword, labels exist in their own, isolated world, so no risk of occasional clash. Labels are also scoped to their context and do not exist outside it.
# Comments # Comments
@ -276,4 +284,31 @@ The label can be any valid identifier, even a keyword, labels exist in their own
var result = null // here we will store the result var result = null // here we will store the result
>>> void >>> void
# Integral data types
| type | description | literal samples |
|--------|---------------------------------|---------------------|
| Int | 64 bit signed | `1` `-22` `0x1FF` |
| Real | 64 bit double | `1.0`, `2e-11` |
| Bool | boolean | `true` `false` |
| String | unicode string, no limits | "hello" (see below) |
| Void | no value could exist, singleton | void |
| Null | missing value, singleton | null |
| Fn | callable type | |
## String details
### String operations
Concatenation is a `+`: `"hello " + name` works as expected. No confusion.
### Literals
String literal could be multiline:
"Hello
World"
though multiline literals is yet work in progress.

View File

@ -305,6 +305,7 @@ class Compiler {
"var" -> parseVarDeclaration(id.value, true, cc) "var" -> parseVarDeclaration(id.value, true, cc)
"while" -> parseWhileStatement(cc) "while" -> parseWhileStatement(cc)
"break" -> parseBreakStatement(id.pos, cc) "break" -> parseBreakStatement(id.pos, cc)
"continue" -> parseContinueStatement(id.pos, cc)
"fn", "fun" -> parseFunctionDeclaration(cc) "fn", "fun" -> parseFunctionDeclaration(cc)
"if" -> parseIfStatement(cc) "if" -> parseIfStatement(cc)
else -> null else -> null
@ -388,6 +389,27 @@ class Compiler {
} }
} }
private fun parseContinueStatement(start: Pos, cc: CompilerContext): Statement {
val t = cc.next()
val label = if (t.pos.line != start.line || t.type != Token.Type.ATLABEL) {
cc.previous()
null
} else {
t.value
}?.also {
// check that label is defined
cc.ensureLabelIsValid(start, it)
}
return statement(start) {
throw LoopBreakContinueException(
doContinue = true,
label = label,
)
}
}
private fun ensureRparen(tokens: CompilerContext): Pos { private fun ensureRparen(tokens: CompilerContext): Pos {
val t = tokens.next() val t = tokens.next()
if (t.type != Token.Type.RPAREN) if (t.type != Token.Type.RPAREN)