-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(didparser): Adding amtlr4 grammar did parser with specification …
…and tests (#10) * Add dependencies to manage antlr grammar in multiplatform * Generate antlr4 grammar parsers and lexers with gradle task * Remove intermediate interp and tokens amtlr grammar files * Remove auto generated files but keep tests working. * few changes to fix Gradle - Gradle task should not be a dependency on all tasks * add another exclude Co-authored-by: Ahmed Moussa <[email protected]>
- Loading branch information
1 parent
1e4faf8
commit 0d7dac1
Showing
9 changed files
with
340 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
grammar DIDAbnf; | ||
|
||
did | ||
: SCHEMA ':' method_name ':' method_specific_id EOF | ||
; | ||
|
||
method_name | ||
: (ALPHA | DIGIT)* | ||
; | ||
|
||
method_specific_id | ||
: idchar* ( ':' idchar+ )? | ||
; | ||
|
||
idchar | ||
: ( ALPHA | DIGIT | PERIOD | DASH | UNDERSCORE | PCT_ENCODED ) | ||
; | ||
|
||
fragment D : ('d' | 'D'); | ||
fragment I : ('i' | 'I'); | ||
SCHEMA : D I D; | ||
|
||
fragment LOWERCASE : [a-z]; | ||
fragment UPPERCASE : [A-Z]; | ||
ALPHA : ( LOWERCASE | UPPERCASE ); | ||
|
||
fragment HEX : [0-9a-fA-F]; | ||
DIGIT : [0-9]; | ||
PCT_ENCODED : PERCENT HEX HEX; | ||
PERCENT : '%'; | ||
DASH : '-'; | ||
PERIOD : '.'; | ||
COLON : ':'; | ||
UNDERSCORE : '_'; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
grammar DIDUrlAbnf; | ||
|
||
did_url | ||
: did path? query? frag? EOF | ||
; | ||
|
||
did | ||
: SCHEMA ':' method_name ':' method_specific_id | ||
; | ||
|
||
method_name | ||
: string | ||
; | ||
|
||
method_specific_id | ||
: ( string ':'? )* string | ||
; | ||
|
||
path | ||
: ('/' string)* '/'? | ||
; | ||
|
||
query | ||
: '?' search | ||
; | ||
|
||
frag | ||
: '#' (string | DIGIT) | ||
; | ||
|
||
search | ||
: searchparameter ('&' searchparameter)* | ||
; | ||
|
||
searchparameter | ||
: string ('=' (string | DIGIT | HEX))? | ||
; | ||
|
||
string | ||
: STRING | ||
| DIGIT | ||
; | ||
|
||
fragment D : ('d' | 'D'); | ||
fragment I : ('i' | 'I'); | ||
SCHEMA : D I D; | ||
|
||
fragment LOWERCASE : [a-z]; | ||
fragment UPPERCASE : [A-Z]; | ||
ALPHA : ( LOWERCASE | UPPERCASE ); | ||
|
||
DIGIT : [0-9]; | ||
PCT_ENCODED : PERCENT HEX HEX; | ||
PERCENT : '%'; | ||
DASH : '-'; | ||
PERIOD : '.'; | ||
COLON : ':'; | ||
UNDERSCORE : '_'; | ||
HEX : ('%' [a-fA-F0-9] [a-fA-F0-9])+; | ||
|
||
STRING: ([a-zA-Z~0-9] | HEX) ([a-zA-Z0-9.+-] | HEX)*; |
57 changes: 57 additions & 0 deletions
57
castor/src/commonMain/kotlin/io/iohk/atala/prism/castor/DIDParser.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package io.iohk.atala.prism.castor | ||
|
||
import io.iohk.atala.prism.castor.antlrgrammar.DIDAbnfLexer | ||
import io.iohk.atala.prism.castor.antlrgrammar.DIDAbnfParser | ||
import io.iohk.atala.prism.domain.models.DID | ||
import org.antlr.v4.kotlinruntime.CharStreams | ||
import org.antlr.v4.kotlinruntime.CommonTokenStream | ||
import org.antlr.v4.kotlinruntime.DefaultErrorStrategy | ||
import org.antlr.v4.kotlinruntime.Parser | ||
import org.antlr.v4.kotlinruntime.ParserRuleContext | ||
import org.antlr.v4.kotlinruntime.RecognitionException | ||
import org.antlr.v4.kotlinruntime.Token | ||
import org.antlr.v4.kotlinruntime.tree.ParseTree | ||
import org.antlr.v4.kotlinruntime.tree.ParseTreeWalker | ||
|
||
class BailErrorStrategy : DefaultErrorStrategy() { | ||
override fun recover(recognizer: Parser, e: RecognitionException) { | ||
var context = recognizer.context | ||
while (context != null) { | ||
context.exception = e | ||
context = context.readParent() as ParserRuleContext? | ||
} | ||
|
||
throw e | ||
} | ||
|
||
override fun recoverInline(recognizer: Parser): Token { | ||
var context = recognizer.context | ||
while (context != null) { | ||
context = context.readParent() as ParserRuleContext? | ||
} | ||
throw InvalidDIDStringError("Invalid Did char found at [line ${recognizer.currentToken?.line}, col ${recognizer.currentToken?.charPositionInLine}] \"${recognizer.currentToken?.text}\"") | ||
} | ||
|
||
override fun sync(recognizer: Parser) {} | ||
} | ||
|
||
class DIDParser(private var didString: String) { | ||
fun parse(): DID { | ||
var inputStream = CharStreams.fromString(didString) | ||
val lexer = DIDAbnfLexer(inputStream) | ||
val tokenStream = CommonTokenStream(lexer) | ||
val parser = DIDAbnfParser(tokenStream) | ||
|
||
parser.errorHandler = BailErrorStrategy() | ||
|
||
val context = parser.did() | ||
val listener = DIDParserListener() | ||
ParseTreeWalker().walk(listener, context as ParseTree) | ||
|
||
val scheme = listener.scheme ?: throw InvalidDIDStringError("Invalid DID string, missing scheme") | ||
val methodName = listener.methodName ?: throw InvalidDIDStringError("Invalid DID string, missing method name") | ||
val methodId = listener.methodId ?: throw InvalidDIDStringError("Invalid DID string, missing method ID") | ||
|
||
return DID(scheme, methodName, methodId) | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
castor/src/commonMain/kotlin/io/iohk/atala/prism/castor/DIDParserListener.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package io.iohk.atala.prism.castor | ||
|
||
import io.iohk.atala.prism.castor.antlrgrammar.DIDAbnfBaseListener | ||
import io.iohk.atala.prism.castor.antlrgrammar.DIDAbnfParser | ||
|
||
class DIDParserListener : DIDAbnfBaseListener() { | ||
|
||
var scheme: String? = null | ||
var methodName: String? = null | ||
var methodId: String? = null | ||
|
||
override fun exitDid(ctx: DIDAbnfParser.DidContext) { | ||
ctx.SCHEMA()?.let { | ||
scheme = it.text | ||
} | ||
} | ||
|
||
override fun exitMethod_name(ctx: DIDAbnfParser.Method_nameContext) { | ||
methodName = ctx.text | ||
} | ||
|
||
override fun exitMethod_specific_id(ctx: DIDAbnfParser.Method_specific_idContext) { | ||
methodId = ctx.text | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
castor/src/commonMain/kotlin/io/iohk/atala/prism/castor/InvalidDIDStringError.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package io.iohk.atala.prism.castor | ||
|
||
class InvalidDIDStringError(override val message: String) : Exception(message) { | ||
val code: String = "InvalidDIDStringError" | ||
} |
84 changes: 84 additions & 0 deletions
84
castor/src/commonTest/kotlin/io/iohk/atala/prism/castor/DIDParserTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package io.iohk.atala.prism.castor | ||
|
||
import kotlin.test.Test | ||
import kotlin.test.assertEquals | ||
import kotlin.test.assertFailsWith | ||
|
||
class DIDParserTest { | ||
|
||
@Test | ||
fun it_should_test_valid_DIDs() { | ||
var didExample1 = "did:aaaaaa:aa:aaa" | ||
var didExample2 = "did:prism01:b2.-_%11:b4._-%11" | ||
var didExample3 = | ||
"did:prism:b6c0c33d701ac1b9a262a14454d1bbde3d127d697a76950963c5fd930605:Cj8KPRI7CgdtYXN0ZXIwEAFKLgoJc2VmsxEiECSTjyV7sUfCr_ArpN9rvCwR9fRMAhcsr_S7ZRiJk4p5k" | ||
|
||
var parsedDID1 = DIDParser(didExample1).parse() | ||
var parsedDID2 = DIDParser(didExample2).parse() | ||
var parsedDID3 = DIDParser(didExample3).parse() | ||
|
||
assertEquals(parsedDID1.schema, "did") | ||
assertEquals(parsedDID1.method, "aaaaaa") | ||
assertEquals(parsedDID1.methodId, "aa:aaa") | ||
|
||
assertEquals(parsedDID2.schema, "did") | ||
assertEquals(parsedDID2.method, "prism01") | ||
assertEquals(parsedDID2.methodId, "b2.-_%11:b4._-%11") | ||
|
||
assertEquals(parsedDID3.schema, "did") | ||
assertEquals(parsedDID3.method, "prism") | ||
assertEquals( | ||
parsedDID3.methodId, | ||
"b6c0c33d701ac1b9a262a14454d1bbde3d127d697a76950963c5fd930605:Cj8KPRI7CgdtYXN0ZXIwEAFKLgoJc2VmsxEiECSTjyV7sUfCr_ArpN9rvCwR9fRMAhcsr_S7ZRiJk4p5k" | ||
) | ||
} | ||
|
||
@Test | ||
fun it_should_test_invalid_DIDs() { | ||
var didExample1 = "idi:aaaaaa:aa:aaa" | ||
var didExample2 = "did:-prism-:aaaaa:aaaa" | ||
var didExample3 = "did:prism:aaaaaaaaaaa::" | ||
var didExample4 = "did::prism:aaaaaaaaaaa:aaaa" | ||
var didExample5 = "did:prism::aaaaaaaaaaa:bbbb" | ||
|
||
val exception = assertFailsWith( | ||
exceptionClass = InvalidDIDStringError::class, | ||
block = { | ||
DIDParser(didExample1).parse() | ||
} | ||
) | ||
assertEquals(exception.code, "InvalidDIDStringError") | ||
|
||
val exception2 = assertFailsWith( | ||
exceptionClass = InvalidDIDStringError::class, | ||
block = { | ||
DIDParser(didExample2).parse() | ||
} | ||
) | ||
assertEquals(exception2.code, "InvalidDIDStringError") | ||
|
||
val exception3 = assertFailsWith( | ||
exceptionClass = InvalidDIDStringError::class, | ||
block = { | ||
DIDParser(didExample3).parse() | ||
} | ||
) | ||
assertEquals(exception3.code, "InvalidDIDStringError") | ||
|
||
val exception4 = assertFailsWith( | ||
exceptionClass = InvalidDIDStringError::class, | ||
block = { | ||
DIDParser(didExample4).parse() | ||
} | ||
) | ||
assertEquals(exception4.code, "InvalidDIDStringError") | ||
|
||
val exception5 = assertFailsWith( | ||
exceptionClass = InvalidDIDStringError::class, | ||
block = { | ||
DIDParser(didExample5).parse() | ||
} | ||
) | ||
assertEquals(exception5.code, "InvalidDIDStringError") | ||
} | ||
} |
Oops, something went wrong.