Razvan
04/22/2021, 8:01 PMRazvan
04/22/2021, 8:07 PMdave
05/01/2021, 1:20 PMdave08
05/09/2021, 2:07 PMfilter(predicate: () -> Boolean, error: () -> E)
just like filterNotNull
in Result4k... my use case is that in the case my result is an empty list, it should return a domain error. What do you guys think?dave08
05/09/2021, 2:10 PMinline fun <T, R> resultFrom(block: T.() -> R): Result<R, Exception>
might also be nice (makes chaining a bit cleaner), like in kotlin.Result.Razvan
05/13/2021, 7:25 PMvalue class
?dave
05/13/2021, 7:30 PMRazvan
05/14/2021, 11:53 AMvalue class
?dave
05/14/2021, 11:55 AMRazvan
05/14/2021, 12:18 PMdave
11/17/2021, 10:39 AMdave
12/20/2021, 7:27 PMdave
08/22/2023, 8:34 PMAndrew O'Hara
08/26/2023, 10:26 PMREMOVE foo
, but not REMOVE foo bar
? I thought the oneOrMore
in Remove
would catch multiple space-separated tokens, but I get this error:
parser4k.InputIsNotConsumed:
REMOVE foo bar
^
object DynamoDbUpdateGrammar {
private val cache = OutputCache<Expr>()
fun parse(expression: String): Expr = expression.parseWith(expr)
private val expr: Parser<Expr> = oneOf(
//set
Remove(::expr).with(cache),
// add
// delete
).reset(cache)
}
private val UpdateAttributeName : ExprFactory = {
Tokens.identifier.map { name -> Expr { AttributeName.of(name) } }
}
private val Remove: ExprFactory = { parser ->
inOrder(
token("REMOVE"),
oneOrMore(
UpdateAttributeName(parser),
)
).skipFirst().map { names ->
Expr { item ->
val attributeNames = names.map { it.eval(item) }
item.item.filterKeys { it !in attributeNames }
}
}
}
Dmitry Kandalov
08/27/2023, 10:07 AM.joinedWith(" ")
, where ” ” is the separator between REMOVE arguments.
I wasn’t able to run your example directly, so I simplified it a bit 🙈 Not sure why you needed ExprFactory
🤔
Given “REMOVE foo bar”, token("REMOVE")
matches “REMOVE ” (including whitespace), oneOrMore(identifier).joinedWith(" ")
matches “foo” and “bar”.
import parser4k.OutputCache
import parser4k.Parser
import parser4k.commonparsers.Tokens.identifier
import parser4k.commonparsers.joinedWith
import parser4k.commonparsers.token
import parser4k.inOrder
import parser4k.map
import parser4k.oneOf
import parser4k.oneOrMore
import parser4k.parseWith
import parser4k.reset
import parser4k.skipFirst
import parser4k.with
object DynamoDbUpdateGrammar {
private val cache = OutputCache<Expr>()
fun parse(expression: String): Expr = expression.parseWith(expr)
private val expr: Parser<Expr> = oneOf(
Remove.with(cache),
).reset(cache)
}
data class Expr(val id: String)
private val Remove =
inOrder(
token("REMOVE"),
oneOrMore(identifier).joinedWith(" ")
).skipFirst().map { names ->
Expr(names.joinToString())
}
fun main() {
println(DynamoDbUpdateGrammar.parse("REMOVE foo bar"))
}
Dmitry Kandalov
08/27/2023, 2:26 PMoneOrMore
parser does capture multiple space-separated tokens, but it expects them to be directly adjacent to one another. In other words, it’s expecting input like REMOVE foofoofoo
instead of REMOVE foo bar
because there’s no parser defined to handle the space between the tokens. To solve this, you can explicitly specify that you’re expecting space-separated tokens. Here’s how you can modify the Remove
parser to handle multiple space-separated tokens:
private val Remove: ExprFactory = { parser ->
inOrder(
token("REMOVE"),
oneOrMore(
inOrder(
Tokens.space.optional(),
UpdateAttributeName(parser)
).skipFirst()
)
).skipFirst().map { names ->
Expr { item ->
val attributeNames = names.map { it.eval(item) }
item.item.filterKeys { it !in attributeNames }
}
}
}
Here’s a breakdown of the changes:
1. Inside the oneOrMore
parser, I wrapped the UpdateAttributeName(parser)
with another inOrder
parser.
2. This new inOrder
parser starts with Tokens.space.optional()
, which tries to parse a space but doesn’t fail if there isn’t one.
3. I used .skipFirst()
to make sure the optional space doesn’t get included in the parsed result.
Now, Remove
should be able to parse inputs like REMOVE foo bar
.Andrew O'Hara
08/27/2023, 3:03 PMoptional(Tokens.whitespace)
rather than
Tokens.space.optional()
Is that an artifact of ChatGpt? Or just a pseudocode approximation? I did just make a extension function that provides the .optional()
, so maybe I'll make a small PR for that.Andrew O'Hara
08/27/2023, 3:08 PMExprFactory
isn't necessary; I was following David's lead from other examples, but I've now been able to remove it from this new parser.Dmitry Kandalov
08/27/2023, 3:40 PMTokens.space.optional()
is what ChatGPT generated. As usual, it casually makes things up 🤷 (Technically speaking this is all it does.)
Not sure about .optional()
, if there is an extension function for it, then nonRecursive, repeat, zeroOrMore and oneOrMore should probably also have one. On the other hand, don’t remember why/how I decided that ref
deserves an extension function 🤔Dmitry Kandalov
08/27/2023, 3:41 PMAndrew O'Hara
08/27/2023, 3:43 PMDmitry Kandalov
09/11/2023, 3:24 PMdev.forkhandles.result4k.Result4k
should be the default 🤔 Because dev.forkhandles.result4k.Result
will inevitably clash with kotlin.Result
. And since IntelliJ doesn’t keep typealiases when refactoring, the codebase ends up as a mix of Result4k
and Result
.Andrew O'Hara
12/06/2023, 8:54 PMdave
01/05/2024, 5:21 PMdave
03/24/2024, 1:35 PMnatpryce
04/08/2024, 9:35 PMfun maybeReturnsATuple(): Tuple2<String,Int>? = ...
val (a,b) = maybeReturnsATuple()
Where a
has type String?
and b
has type Int?
.natpryce
04/08/2024, 9:36 PMAndrew O'Hara
03/18/2025, 1:41 PMtime4k
over java.time.Clock
on the JVM. I'm not even certain the TimeSource
class will load correctly outside the JVM 🤔 . The best theory I have is that it's simpler to implement a custom TimeSource
than a Clock
.dave
03/18/2025, 3:25 PMdave
03/18/2025, 3:26 PM