A nice parser combinator library for Kotlin

Overview

better-parse

Maven Central Gradle build

A nice parser combinator library for Kotlin JVM, JS, and Multiplatform projects

val booleanGrammar = object : Grammar<BooleanExpression>() {
    val id by regexToken("\\w+")
    val not by literalToken("!")
    val and by literalToken("&")
    val or by literalToken("|")
    val ws by regexToken("\\s+", ignore = true)
    val lpar by literalToken("(")
    val rpar by literalToken(")")

    val term by 
        (id use { Variable(text) }) or
        (-not * parser(this::term) map { Not(it) }) or
        (-lpar * parser(this::rootParser) * -rpar)

    val andChain by leftAssociative(term, and) { l, _, r -> And(l, r) }
    override val rootParser by leftAssociative(andChain, or) { l, _, r -> Or(l, r) }
}

val ast = booleanGrammar.parseToEnd("a & !b | b & (!a | c)")

Using with Gradle

dependencies {
   implementation("com.github.h0tk3y.betterParse:better-parse:0.4.3")
}

With multiplatform projects, it's OK to add the dependency just to the commonMain source set, or some other source set if you want it for specific parts of the code.

Tokens

As many other language recognition tools, better-parse abstracts away from raw character input by pre-processing it with a Tokenizer, that can match Tokens (with regular expressions, literal values or arbitrary against an input character sequence.

There are several kinds of supported Tokens:

  • a regexToken("(?:my)?(?:regex)) is matched as a regular expression;
  • a literalToken("foo") is matched literally, character to character;
  • a token { (charSequence, from) -> ... } is matched using the passed function.

A Tokenizer tokenizes an input sequence such as InputStream or a String into a Sequence<TokenMatch>, providing each with a position in the input.

One way to create a Tokenizer is to first define the Tokens to be matched:

val id = regexToken("\\w+")
val cm = literalToken(",")
val ws = regexToken("\\s+", ignore = true)

A Token can be ignored by setting its ignore = true. An ignored token can still be matched explicitly, but if another token is expected, the ignored one is just dropped from the sequence.

val tokenizer = DefaultTokenizer(listOf(id, cm, ws))

Note: the tokens order matters in some cases, because the tokenizer tries to match them in exactly this order. For instance, if literalToken("a") is listed before literalToken("aa"), the latter will never be matched. Be careful with keyword tokens! If you match them with regexes, a word boundary \b in the end may help against ambiguity.

val tokenMatches: Sequence<TokenMatch> = tokenizer.tokenize("hello, world")

A more convenient way of defining tokens is described in the Grammar section.

It is possible to provide a custom implementation of a Tokenizer.

Parser

A Parser<T> is an object that accepts an input sequence (TokenMatchesSequence) and tries to convert some (from none to all) of its items into a T. In better-parse, parsers are also the building blocks used to create new parsers by combining them.

When a parser tries to process the input, there are two possible outcomes:

  • If it succeeds, it returns Parsed<T> containing the T result and the nextPosition: Int that points to what it left unprocessed. The latter can then be, and often is, passed to another parser.

  • If it fails, it reports the failure returning an ErrorResult, which provides detailed information about the failure.

A very basic parser to start with is a Token itself: given an input TokenMatchesSequence and a position in it, it succeeds if the sequence starts with the match of this token itself (possibly, skipping some ignored tokens) and returns that TokenMatch, pointing at the next token with the nextPosition.

val a = regexToken("a+")
val b = regexToken("b+")
val tokenMatches = DefaultTokenizer(listOf(a, b)).tokenize("aabbaaa")
val result = a.tryParse(tokenMatches, 0) // contains the match for "aa" and the next index is 1 for the match of b

Combinators

Simpler parsers can be combined to build a more complex parser, from tokens to terms and to the whole language. There are several kinds of combinators included in better-parse:

  • map, use, asJust

    The map combinator takes a successful input of another parser and applies a transforming function to it. The error results are returned unchanged.

    val id = regexToken("\\w+")
    val aText = a map { it.text } // Parser<String>, returns the matched text from the input sequence

    A parser for objects of a custom type can be created with map:

    val variable = a map { JavaVariable(name = it.text) } // Parser<JavaVariable>.
    • someParser use { ... } is a map equivalent that takes a function with receiver instead. Example: id use { text }.

    • foo asJust bar can be used to map a parser to some constant value.

  • optional(...)

    Given a Parser<T>, tries to parse the sequence with it, but returns a null result if the parser failed, and thus never fails itself:

    val p: Parser<T> = ...
    val o = optional(p) // Parser<T?>    
  • and, and skip(...)

    The tuple combinator arranges the parsers in a sequence, so that the remainder of the first one goes to the second one and so on. If all the parsers succeed, their results are merged into a Tuple. If either parser failes, its ErrorResult is returned by the combinator.

    val a: Parser<A> = ...
    val b: Parser<B> = ...
    val aAndB = a and b                 // This is a `Parser<Tuple2<A, B>>`
    val bAndBAndA = b and b and a       // This is a `Parser<Tuple3<B, B, A>>`

    You can skip(...) components in a tuple combinator: the parsers will be called just as well, but their results won't be included in the resulting tuple:

    val bbWithoutA = skip(a) and b and skip(a) and b and skip(a)  // Parser<Tuple2<B, B>>

    If all the components in an and chain are skipped except for one Parser<T>, the resulting parser is Parser<T>, not Parser<Tuple1<T>>.

    To process the resulting Tuple, use the aforementioned map and use. These parsers are equivalent:

    • val fCall = id and skip(lpar) and id and skip(rpar) map { (fName, arg) -> FunctionCall(fName, arg) }

    • val fCall = id and lpar and id and rpar map { (fName, _, arg, _) -> FunctionCall(fName, arg) }

    • val fCall = id and lpar and id and rpar use { FunctionCall(t1, t3) }

    • val fCall = id * -lpar * id * -rpar use { FunctionCall(t1, t2) } (see operators below)

    There are Tuple classes up to Tuple16 and the corresponding and overloads.

    Operators

    There are operator overloads for more compact and chains definition:

    • a * b is equivalent to a and b.

    • -a is equivalent to skip(a).

    With these operators, the parser a and skip(b) and skip(c) and d can also be defined as a * -b * -c * d.

  • or

    The alternative combinator tries to parse the sequence with the parsers it combines one by one until one succeeds. If all the parsers fail, the returned ErrorResult is an AlternativesFailure instance that contains all the failures from the parsers.

    The result type for the combined parsers is the least common supertype (which is possibly Any).

    val expr = const or variable or fCall
  • zeroOrMore(...), oneOrMore(...), N times, N timesOrMore, N..M times

    These combinators transform a Parser<T> into a Parser<List<T>>, invokng the parser several times and failing if there was not enough matches.

    val modifiers = zeroOrMore(functionModifier)
    val rectangleParser = 4 times number map { (a, b, c, d) -> Rect(a, b, c, d) }
  • separated(term, separator), separatedTerms(term, separator), leftAssociative(...), rightAssociative(...)

    Combines the two parsers, invoking them in turn and thus parsing a sequence of term matches separated by separator matches.

    The result is a Separated<T, S> which provides the matches of both parsers (note that terms are one more than separators) and can also be reduced in either direction.

    val number: Parser<Int> = ...
    val sumParser = separated(number, plus) use { reduce { a, _, b -> a + b } }

    The leftAssociative and rightAssociative combinators do exactly this, but they take the reducing operation as they are built:

    val term: Parser<Term>
    val andChain = leftAssociative(term, andOperator) { l, _, r -> And(l, r) }

Grammar

As a convenient way of defining a grammar of a language, there is an abstract class Grammar, that collects the by-delegated properties into a Tokenizer automatically, and also behaves as a composition of the Tokenizer and the rootParser.

Note: a Grammar also collects by-delegated Parser<T> properties so that they can be accessed as declaredParsers along with the tokens. As a good style, declare the parsers inside a Grammar by delegation as well.

interface Item
class Number(val value: Int) : Item
class Variable(val name: String) : Item

class ItemsParser : Grammar<List<Item>>() {
    val num by regexToken("\\d+")
    val word by regexToken("[A-Za-z]+")
    val comma by regexToken(",\\s+")

    val numParser by num use { Number(text.toInt()) }
    val varParser by word use { Variable(text) }

    override val rootParser by separatedTerms(numParser or varParser, comma)
}

val result: List<Item> = ItemsParser().parseToEnd("one, 2, three, 4, five")

To use a parser that has not been constructed yet, reference it with parser { someParser } or parser(this::someParser):

val term by
    constParser or 
    variableParser or 
    (-lpar and parser(this::term) and -rpar)

A Grammar implementation can override the tokenizer property to provide a custom implementation of Tokenizer.

Syntax trees

A Parser<T> can be converted to another Parser<SyntaxTree<T>>, where a SyntaxTree<T>, along with the parsed T contains the children syntax trees, the reference to the parser and the positions in the input sequence. This can be done with parser.liftToSyntaxTreeParser().

This can be used for syntax highlighting and inspecting the resulting tree in case the parsed result does not contain the full syntactic structure.

For convenience, a Grammar can also be lifted to that parsing a SyntaxTree with grammar.liftToSyntaxTreeGrammar().

val treeGrammar = booleanGrammar.liftToSyntaxTreeGrammar()
val tree = treeGrammar.parseToEnd("a & !b | c -> d")
assertTrue(tree.parser == booleanGrammar.implChain)
val firstChild = tree.children.first()
assertTrue(firstChild.parser == booleanGrammar.orChain)
assertTrue(firstChild.range == 0..9)

There are optional arguments for customizing the transformation:

  • LiftToSyntaxTreeOptions

    • retainSkipped — whether the resulting syntax tree should include skipped and components;
    • retainSeparators — whether the Separated combinator parsed separators should be included;
  • structureParsers — defines the parsers that are retained in the syntax tree; the nodes with parsers that are not in this set are flattened so that their children are attached to their parents in their place.

    For Parser<T>, the default is null, which means no nodes are flattened.

    In case of Grammar<T>, structureParsers defaults to the grammar's declaredParsers.

  • transformer — a strategy to transform non-built-in parsers. If you define your own combinators and want them to be lifted to syntax tree parsers, pass a LiftToSyntaxTreeTransformer that will be called on the parsers. When a custom combinator nests another parser, a transformer implementation should call default.transform(...) on that parser.

See SyntaxTreeDemo.kt for an example of working with syntax trees.

Examples

Benchmarks

See the benchmarks repository h0tk3y/better-parse-benchmark and feel free to contribute.

Comments
  • Fix InvalidMutabilityException in RegexToken

    Fix InvalidMutabilityException in RegexToken

    Mutating relativeInput makes better-parse throw InvalidMutabilityException on iosX64 (probably on other Kotlin/Native platforms as well). This PR turns relativeInput into a local immutable object.

    opened by itegulov 5
  • Token patterns with flags are broken

    Token patterns with flags are broken

    Since 0e6eb411b, Tokens defined by a Pattern or RegEx object are converted to a Token defined by a pattern String.

    This breaks use-cases where the pattern object was created with flags set (e.g. token("^--".toRegex(RegexOption.MULTILINE)))

    Currently, this can only be worked around by specifying the flags inside the pattern string (e.g. token("(?m)^--"))

    opened by oxc 4
  • Support kotlin 1.4.0

    Support kotlin 1.4.0

    Could you provide a version that compiles against kotlin 1.4.0 release? The 1.4-M2 version is rejected by the compiler as "compiled by a pre-release version". Thanks!

    opened by oxc 3
  • Rules dependent on each other

    Rules dependent on each other

    Is it possible to define the following grammar?

    val singleton by lbrace cmd rbrace; // singleton -> cmd
    val expr by number or funccall or ... or singleton; // expr -> singleton
    val cmd by expr semicol; // cmd -> expr
    // the dependency is a cycle: cmd -> expr -> singleton -> cmd
    

    so that this grammar accepts

    { 1; }
    

    as an expression. I can't write the grammar in the naive way, because Kotlin rejects this code complaining "Variable cmd must be initialized". I tried lazy but the properties seem to be NULL, not properly initialized.

    edit: I added lazy to every parser but it ended up infinite recursion.

    opened by ksqsf 2
  • How should Grammar objects be composed?

    How should Grammar objects be composed?

    The goal

    I have a file that looks like this:

    Header
    Line
    Line
    Line
    Footer
    

    And I would like to parse it into the following container:

    data class File(
      val header: Header,
      val lines: List<Line>,
      val footer: Footer
    )
    

    The problem

    I have written a grammar for each of the different variants, i.e Grammar<Header, Grammar<Line>, and Grammar<Footer>. There is some similarity in how these different lines look. How can I compose these grammars together? The variants are split on a newline \n character.

    Any help would be much appreciated.

    opened by KushalP 2
  • Looks like bug in description

    Looks like bug in description

    interface Item
    class Number(val value: Int) : Item
    class Variable(val name: String) : Item
    
    object ItemsParser : Grammar<Item>() {
        val num by token("\\d+")
        val word by token("[A-Za-z]")
        val comma by token(",\\s+")
    
        val numParser = num use { Number(text.toInt()) }
        val varParser = word use { Variable(text) }
    
        override val <!PROPERTY_TYPE_MISMATCH_ON_OVERRIDE!>rootParser<!> = separatedTerms(numParser or varParser, comma)
    }
    
    val result: List<Item> = ItemsParser.<!TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>parseToEnd("one, 2, three, 4, five")<!>
    

    Some errors reported on code copied from README

    opened by semoro 2
  • [BUG] literal token with DefaultTokenizer can't find a match - 'noneMatched'

    [BUG] literal token with DefaultTokenizer can't find a match - 'noneMatched'

    I a trying to figure out how this library works but I simply can't get passed the simple literal token example. Here is my test code:

        @Test
        fun `should match foo`() {
            val literal = literalToken("foo")
            val tokenizer = DefaultTokenizer(listOf(literal))
            val sequence: TokenMatchesSequence = tokenizer.tokenize("my foo")
    
            assertThat(sequence.toList()).isNotEmpty() // success
            assertThat(sequence[0]!!.input).isEqualTo("my foo") // success
            assertThat(sequence[0]!!.type).isNotEqualTo(noneMatched) // failure
            assertThat(sequence[0]!!.text).isEqualTo("foo") // never reached
        }
    

    This fails with assertThat(sequence[0]!!.type).isNotEqualTo(noneMatched) since there is no match for the simple input of my foo and the literal token looking for foo.

    Any idea what I am doing wrong?

    Infos:

    implementation("com.github.h1tk3y.betterParse:better-parse:0.4.4")
    

    Greetings!

    opened by xetra11 1
  • fix preprocessRegex function (fixes h0tk3y/better-parse#51)

    fix preprocessRegex function (fixes h0tk3y/better-parse#51)

    Kotlins internal name for the nativePattern seems to have changed. This fix changes preprocessRegex to handle multiple names. It also replaces the js(...) call with regular Kotlin code

    opened by InfectedBytes 1
  • Fix reported range when an optional parser is lifted to an AST parser

    Fix reported range when an optional parser is lifted to an AST parser

    The range added to the SyntaxTree for an optional parser that is skipped, is reported as 0..0, as per this logic. This will affect the reported range of parent parsers as well, e.g. if the optional parser is the first in an "and combinator", the reported range of the "and combinator" as a whole will start at 0. This PR changes the fallback behavior to an empty range starting at the current token's offset. I think it can be debated if this is fully correct, but I like it because it's pragmatic.

    Thanks for considering this PR. Let me know if you have any comments or things I should improve!

    opened by Thomvis 1
  • Misreported range in SyntaxTree for optional parser

    Misreported range in SyntaxTree for optional parser

    When an optional parser parses nothing, it's range becomes 0..0, because of the fallback here. This error will bubble up, e.g. when the optional is inside an "and" parser.

    Example:

    val integerExpression by (optional(MINUS) * INT)
    

    If there is no - to parse, the start of the range of the SyntaxTree corresponding to a match of integerExpression will always start at 0, regardless of where the match happens. So if the input where Hello World! 5 (and imagine integerExpression is part of a larger parser that can also parse words), the actual range would be 0..14, while the expected range would be 13..14.

    Do you have any pointers how this could be best resolved?

    opened by Thomvis 1
  • Note that this library is not thread safe

    Note that this library is not thread safe

    In grammars on the JVM, RegexTokens (at least) use stateful matchers, and therefore aren't thread-safe. It would be nice if the library either noted this, or was thread-safe (by creating a new matcher as needed- at a glance this doesn't seem to be an expensive JVM operation, I think that's the creation of the Pattern).

    opened by ScottPeterJohnson 1
  • Negative lookahead?

    Negative lookahead?

    Hi there, I have a grammar with some negative lookahead rules. From what I can tell, some other PEG parsers have a ! operator that can help with this, but it appears this library doesn't. Is this something that would be easy to implement "in userspace"? I'm not a parsing expert. Thanks!

    opened by acruise 9
  • RegexToken in Kotlin 1.7.0 JS IR compiler broken due to minification of member names

    RegexToken in Kotlin 1.7.0 JS IR compiler broken due to minification of member names

    When the Kotlin/JS IR compiler is enabled in Kotlin 1.7.0, minification of member names is turned on by default which breaks preprocessRegex.

    As a workaround, disabling minification as described in the 1.7.0 change notes fixes the issue.

    opened by qwewqa 0
  • Indentation and Dedentation

    Indentation and Dedentation

    Hi, I want to make a grammar with Python-style indentation. In Python:

    def stuff():
        if True:
            if True:
                for i in range(2):
                    print(i)
        print("YAY")
    

    How would I make INDENT and DEDENT tokens using better-parse? I couldn't find any better-parse examples or tests showcasing how to do this.

    opened by Cormanz 0
  • reuse parser result in other delegate

    reuse parser result in other delegate

    Say I have a synthetic situation: number n says how many symbols x will be after this number. After that, I want to drop all other xs.

    For example:

    val digit by regexToken("\\d")
    val n by literalToken("n")
    val found by digit map {it.toInt()}
    val result by found and (found times n) and -zeroOrMore(n)
    //                       ^ can't do
    
    opened by asm0dey 0
  • Using Grammars as Parsers seems to fail.

    Using Grammars as Parsers seems to fail.

    Seeing that Grammar<T> extends Parser<T>, I figured I should be able to delegate to a Grammar<T>, such as:

    val exp: Parser<Exp> by ExpGrammar() // where ExpGrammar is a Grammar<Exp>
    

    However, it doesn't seem to behave as expected. The following is a small SSCCE to demonstrate:

    package com.example
    
    import com.github.h0tk3y.betterParse.combinators.and
    import com.github.h0tk3y.betterParse.combinators.map
    import com.github.h0tk3y.betterParse.combinators.separatedTerms
    import com.github.h0tk3y.betterParse.combinators.skip
    import com.github.h0tk3y.betterParse.grammar.Grammar
    import com.github.h0tk3y.betterParse.grammar.parseToEnd
    import com.github.h0tk3y.betterParse.lexer.TokenMatch
    import com.github.h0tk3y.betterParse.lexer.literalToken
    import com.github.h0tk3y.betterParse.lexer.regexToken
    import com.github.h0tk3y.betterParse.parser.Parser
    
    data class Inner(
        val names: List<String>,
    )
    
    data class Outer(
        val name: String,
        val inner: Inner,
    )
    
    abstract class TestGrammarBase<T> : Grammar<T>()
    {
        val idToken by regexToken("\\w+")
    
        val spaceToken by regexToken("\\s*", true)
    
        val commaToken by literalToken(",")
    
        val lBraceToken by literalToken("{")
    
        val rBraceToken by literalToken("}")
    }
    
    class InnerTestGrammar : TestGrammarBase<Inner>()
    {
        override val rootParser: Parser<Inner> by separatedTerms(idToken, commaToken, true) map inner@{ tokenMatches ->
            return@inner Inner(
                names = tokenMatches.map(TokenMatch::text),
            )
        }
    }
    
    class OuterTestGrammar : TestGrammarBase<Outer>()
    {
        val innerTestParser by InnerTestGrammar()
    
        override val rootParser: Parser<Outer> by idToken and skip(lBraceToken) and innerTestParser and skip(rBraceToken) map outer@{ (tokenMatch, inner) ->
            return@outer Outer(
                name = tokenMatch.text,
                inner = inner,
            )
        }
    }
    
    fun main()
    {
        val innerTest1 = "X, Y, Z"
        val outerTest1 = "A { }"
        val outerTest2 = "A { X, Y, Z }"
    
        val innerTestGrammar = InnerTestGrammar()
        val outerTestGrammar = OuterTestGrammar()
    
        innerTestGrammar.parseToEnd(innerTest1).also(::println)
        outerTestGrammar.parseToEnd(outerTest1).also(::println)
        outerTestGrammar.parseToEnd(outerTest2).also(::println)
    }
    

    And the output:

    Inner(names=[X, Y, Z])
    Outer(name=A, inner=Inner(names=[]))
    Exception in thread "main" com.github.h0tk3y.betterParse.parser.ParseException: Could not parse input: MismatchedToken(expected=rBraceToken (}), found=idToken@5 for "X" at 4 (1:5))
    	at com.github.h0tk3y.betterParse.parser.ParserKt.toParsedOrThrow(Parser.kt:92)
    	at com.github.h0tk3y.betterParse.parser.ParserKt.parseToEnd(Parser.kt:29)
    	at com.github.h0tk3y.betterParse.grammar.GrammarKt.parseToEnd(Grammar.kt:70)
    	at com.example.__langKt.main(__lang.kt:68)
    	at com.example.__langKt.main(__lang.kt)
    

    As you can see, the third attempt to parse the grammar-combined input of A { X, Y, Z } errors out. InnerTestGrammar and OuterTestGrammar, having extended from TestGrammarBase can see the shared member tokens/parsers, but seem to get confused (or perhaps I'm confused).

    Is this not an intended use of Grammar?

    opened by dan-lugg 6
Releases(v0.4.4-1)
  • v0.4.4-1(Apr 21, 2022)

  • v0.4.3(Oct 31, 2021)

    • Fix reported range when an optional parser is lifted to an AST parser (thanks @Thomvis!)
    • Fix InvalidMutabilityException in RegexToken (thanks @itegulov!)
    • Use Kotlin 1.5.31
    Source code(tar.gz)
    Source code(zip)
  • v0.4.2(Apr 20, 2021)

  • 0.4.1(Jan 9, 2021)

    • Updated to Kotlin 1.4.21 and new Multiplatform library publishing layout (no need to depend on platform modules or -metadata part)
    • Fixed issue #28
    • better-parse is now available on Maven Central
    Source code(tar.gz)
    Source code(zip)
  • 0.4.0(Jul 1, 2020)

    • better-parse is now a multiplatform library! All Kotlin targets are supported;
    • Optimize tokenization and parsing, thanks @orangy for his help!
    • Replace token with literalToken and regexToken
    Source code(tar.gz)
    Source code(zip)
  • 0.3.2(Dec 2, 2017)

  • 0.3.1(Oct 29, 2017)

    What's new:

    • Introducing syntax tree parsers that store the tree structure of the parsed input including the start/end positions of each tree node. Your parsers can automatically be transformed from Parser<T> to Parser<SyntaxTree<T>>, see the Syntax trees section;

      image

    • Tokenizer interface to customize the default tokenizing behavior; To support tokenizer implementations that do not use Java regexes, Token now stores a raw String instead of a Pattern. The default tokenizer implementation is now named DefaultTokenizer.

    • Parser<T> properties now can be delegated inside Grammar, which will store them into the the new declaredParsers property;

    • Optimizations of built-in combinators;

    • Regex parameters of Token factory functions annoteted for IntelliJ IDEA regex language injection;

    • Names for Tokens are no more necessary.

    Migration notes:

    • If you used Lexer explicitly, use DefaultTokenizer instead;

    • If you used Token::pattern, change to using the raw pattern string;

    • It is also good to change the val p = someParser declarations inside the Grammars to val p by someParser.

    Source code(tar.gz)
    Source code(zip)
    better-parse-0.3.1-sources.jar(18.38 KB)
    better-parse-0.3.1.jar(201.53 KB)
  • 0.2.1(Aug 1, 2017)

  • 0.2(Jul 12, 2017)

    • Change the semantics of and chains: when there's only one non-skipped operand Parser<T>, the whole chain is now typed as Parser<T>, not Parser<Tuple1<T>>

    • Add operator equivalents: a * b for a and b, and -a for skip(a).

    Source code(tar.gz)
    Source code(zip)
Owner
Sergey Igushkin
Kotlin/Java Programmer
Sergey Igushkin
Kotlin parser library with an easy-to-use DSL

Pratt Library for parsing expressions and a beautiful Kotlin DSL Just define your operators and operands with the Kotlin DSL and the parser is ready!

furetur 9 Oct 17, 2022
A TOML 1.0 parser library for Kotlin

4koma A small, stand-alone, easy to use TOML parser library for Kotlin. 4koma supports an array of convenient features, such as full TOML 1.0 complian

Anton Ekblad 53 Dec 12, 2022
A nice weather that helps you get all information including: current weather, hourly weather and also forecasts for 16 days

WeatherForecast This is an ongoing project where I fetch all the weather data using Retrofit and Kotlin Coroutines over two APIs containing both curre

null 2 Jul 26, 2022
Run Kotlin/JS libraries in Kotlin/JVM and Kotlin/Native programs

Zipline This library streamlines using Kotlin/JS libraries from Kotlin/JVM and Kotlin/Native programs. It makes it possible to do continuous deploymen

Cash App 1.5k Dec 30, 2022
A somewhat copy past of Jetbrain's code from the kotlin plugin repo to make it humanly possible to test Intellij IDEA kotlin plugins that work on kotlin

A somewhat copy past of Jetbrain's code from the kotlin plugin repo to make it humanly possible to test Intellij IDEA kotlin plugins that work on kotlin

common sense OSS 0 Jan 20, 2022
Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in Kotlin with Jetpack Compose and a backed in Kotlin hosted on AppEngine.

Conferences4Hall Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in K

Gérard Paligot 98 Dec 15, 2022
[Android Library] A SharedPreferences helper library to save and fetch the values easily.

Preference Helper A SharedPreferences helper library to save and fetch the values easily. Featured in Use in your project Add this to your module's bu

Naveen T P 13 Apr 4, 2020
Kotlin library for Android

KAndroid Kotlin library for Android providing useful extensions to eliminate boilerplate code in Android SDK and focus on productivity. Download Downl

Paweł Gajda 890 Nov 13, 2022
A Kotlin DSL wrapper around the mikepenz/MaterialDrawer library.

MaterialDrawerKt Create navigation drawers in your Activities and Fragments without having to write any XML, in pure Kotlin code, with access to all t

Márton Braun 517 Nov 19, 2022
🔓 Kotlin version of the popular google/easypermissions wrapper library to simplify basic system permissions logic on Android M or higher.

EasyPermissions-ktx Kotlin version of the popular googlesample/easypermissions wrapper library to simplify basic system permissions logic on Android M

Madalin Valceleanu 326 Dec 23, 2022
Android Spinner Dialog Library supported on both Java and Kotlin, Use for single or multi selection of choice

SpinnerDialog Android Spinner Dialog Library, Use for single or multi selection of choice Android UI Download To include SpinnerDialog in your project

Hamza Khan 55 Sep 15, 2022
A Kotlin library for reactive and boilerplate-free SharedPreferences in Android

KPreferences A Kotlin library for reactive and boilerplate-free Shared Preferences in Android. With KPreferences you can use Kotlin's marvelous delega

Mohamad Amin Mohamadi 19 Dec 16, 2020
AbstractMvp 0.8 0.0 Kotlin is a library that provides abstract components for MVP architecture realization, with problems solutions that are exist in classic MVP.

MinSDK 14+ AbstractMvp AbstractMvp is a library that provides abstract components for MVP architecture realization, with problems solutions that are e

Robert 12 Apr 5, 2022
The most complete and powerful data-binding library and persistence infra for Kotlin 1.3, Android & Splitties Views DSL, JavaFX & TornadoFX, JSON, JDBC & SQLite, SharedPreferences.

Lychee (ex. reactive-properties) Lychee is a library to rule all the data. ToC Approach to declaring data Properties Other data-binding libraries Prop

Mike 112 Dec 9, 2022
A Bluetooth kotlin multiplatform "Cross-Platform" library for iOS and Android

Blue-Falcon A Bluetooth "Cross Platform" Kotlin Multiplatform library for iOS, Android, MacOS, Raspberry Pi and Javascript. Bluetooth in general has t

Andrew Reed 220 Dec 28, 2022
A lightweight cache library written in Kotlin

[NEW] Released to Maven Central: 'com.github.yundom:kache:1.x.x' Kache A runtime in-memory cache. Installation Put this in your build.gradle implemen

Dennis 22 Nov 19, 2022
A Kotlin Android library for content provider queries with reactive streams and coroutines.

Pickpocket An Android library for content provider queries with reactive streams and coroutines. Calendar Contacts SMS MMS Files/Media Call Log Bookma

Chris Basinger 27 Nov 14, 2022
A Kotlin Android library for heuristics evasion that prevents your code from being tested.

EvadeMe An Android library for heuristics evasion that prevents your code from being tested. User Instructions Add the maven repository to your projec

Chris Basinger 29 Dec 26, 2022
Easy lightweight SharedPreferences library for Android in Kotlin using delegated properties

Easy lightweight SharedPreferences library for Android in Kotlin using delegated properties Idea Delegated properties in Kotlin allow you to execute a

null 25 Dec 27, 2022