Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test-cases & Documentation: Annotations #336

Merged
merged 10 commits into from
Dec 11, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.SoftAST

private object AnnotationParser {

/**
* Parses a single annotation and its trailing space.
*
* The configuration within an `Annotation` syntax allows for various expressions [[SoftAST.ExpressionAST]].
* For example, the following will parse successfully:
*
* {{{
* @using(a = functionCall(), b = object.getConfig(param), 1 + 1, blah)
* }}}
*
* @return Returns a [[SoftAST.Annotation]] representation the annotation and its documentation.
*/
def parseOrFail[Unknown: P]: P[SoftAST.Annotation] =
P {
Index ~
Expand All @@ -32,16 +44,18 @@ private object AnnotationParser {
identifier ~
spaceOrFail.? ~
TupleParser.parseOrFail.? ~
spaceOrFail.? ~
Index
} map {
case (from, at, preIdentifierSpace, identifier, postIdentifierSpace, tuple, to) =>
case (from, at, preIdentifierSpace, identifier, postIdentifierSpace, tuple, postTupleSpace, to) =>
SoftAST.Annotation(
index = range(from, to),
at = at,
preIdentifierSpace = preIdentifierSpace,
identifier = identifier,
postIdentifierSpace = postIdentifierSpace,
tuple = tuple
tuple = tuple,
postTupleSpace = postTupleSpace
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,25 @@ private object CommonParser {
SoftAST.IdentifierExpected(point(from))
}

/**
* Parses identifiers as long as they are not reserved tokens [[Token.Reserved]].
*
* For example, the following code will result in an [[SoftAST.IdentifierExpected]] error
* because `let` is a reserved token [[Token.Let]]:
*
* {{{
* fn let() -> ()
* }}}
*
* TODO: Handle cases such as `fn letter() -> ()`
*
* @return A successfully parsed identifier instance [[SoftAST.Identifier]] or a parser error.
*/
def identifierOrFail[Unknown: P]: P[SoftAST.Identifier] =
P {
Index ~
CommentParser.parseOrFail.? ~
!TokenParser.Reserved ~
toCodeOrFail(isLetterDigitOrUnderscore.!) ~
Index
} map {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,16 @@ import org.alephium.ralph.lsp.access.compiler.parser.soft.ast.SoftAST

private object FunctionParser {

/**
* Parses a function.
*
* @return A parsed [[SoftAST.Function]] containing all function information,
* such as its documentation, name, parameters, return type and block expressions.
*/
def parseOrFail[Unknown: P]: P[SoftAST.Function] =
P {
Index ~
AnnotationParser.parseOrFail.? ~
AnnotationParser.parseOrFail.rep ~
spaceOrFail.? ~
AccessModifierParser.parseOrFail.? ~
TokenParser.FnOrFail ~
Expand All @@ -40,7 +46,7 @@ private object FunctionParser {
case (from, annotation, postAnnotationSpace, pub, fnDeceleration, headSpace, signature, tailSpace, block, to) =>
SoftAST.Function(
index = range(from, to),
annotation = annotation,
annotations = annotation,
postAnnotationSpace = postAnnotationSpace,
pub = pub,
fn = fnDeceleration,
Expand All @@ -51,6 +57,12 @@ private object FunctionParser {
)
}

/**
* Parses a function's signature.
*
* @return A parsed [[SoftAST.FunctionSignature]] containing the details of the function signature,
* such as its name, parameters and return type.
*/
private def signature[Unknown: P]: P[SoftAST.FunctionSignature] =
P(Index ~ identifier ~ spaceOrFail.? ~ ParameterParser.parse ~ spaceOrFail.? ~ returnSignature ~ Index) map {
case (from, fnName, headSpace, params, tailSpace, returns, to) =>
Expand All @@ -64,6 +76,12 @@ private object FunctionParser {
)
}

/**
* Parses a function's return type.
*
* @return The parsed [[SoftAST.FunctionReturnAST]], either containing the return
* type or an error indicating that the return type is expected.
*/
private def returnSignature[Unknown: P]: P[SoftAST.FunctionReturnAST] =
P(Index ~ (TokenParser.ForwardArrow ~ spaceOrFail.? ~ TypeParser.parse).? ~ Index) map {
case (from, Some((forwardArrow, space, tpe)), to) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,31 @@ private object TokenParser {
)
}

/**
* Parses all reserved tokens defined in [[Token.reserved]] and returns the first match.
*/
def Reserved[Unknown: P]: P[Token.Reserved] = {
val it =
Token.reserved.iterator

val head =
P(it.next().lexeme) map {
_ =>
Token.reserved.head
}

it.foldLeft(head) {
case (parser, keyword) =>
def nextParser =
P(keyword.lexeme) map {
_ =>
keyword
}

parser | nextParser
}
}

def OperatorOrFail[Unknown: P]: P[SoftAST.Operator] =
P {
buildOperatorParser(Token.Or) |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,17 @@ object SoftAST {
code: Code)
extends TokenDocumentedAST

object At {

def apply(code: Code): At =
At(
index = code.index,
documentation = None,
code = code
)

}

case class At(
index: SourceIndex,
documentation: Option[Comments],
Expand Down Expand Up @@ -501,7 +512,7 @@ object SoftAST {

case class Function(
index: SourceIndex,
annotation: Option[Annotation],
annotations: Seq[Annotation],
postAnnotationSpace: Option[Space],
pub: Option[AccessModifier],
fn: Fn,
Expand Down Expand Up @@ -729,7 +740,8 @@ object SoftAST {
preIdentifierSpace: Option[Space],
identifier: IdentifierAST,
postIdentifierSpace: Option[Space],
tuple: Option[Tuple])
tuple: Option[Tuple],
postTupleSpace: Option[Space])
extends ExpressionAST

sealed trait SpaceAST extends SoftAST
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.alephium.ralph.lsp.access.compiler.parser.soft.ast

import org.alephium.macros.EnumerationMacros

sealed trait Token { self =>

def lexeme: String
Expand All @@ -24,96 +26,125 @@ sealed trait Token { self =>

object Token {

/**
* Represents tokens that are specifically reserved for use in Ralph's grammar.
*
* These tokens cannot be used as identifier [[SoftAST.Identifier]].
*/
sealed trait Reserved extends Token

sealed abstract class Operator(override val lexeme: String) extends Token
case object Minus extends Operator("-")
case object Plus extends Operator("+")
case object Asterisk extends Operator("*")
case object ForwardSlash extends Operator("/")
case object GreaterThan extends Operator(">")
case object LessThan extends Operator("<")
case object Equality extends Operator("==")
case object GreaterThanOrEqual extends Operator(">=")
case object PlusEquals extends Operator("+=")
case object MinusEquals extends Operator("-=")
case object LessThanOrEqual extends Operator("<=")
case object Equal extends Operator("=")
case object NotEqual extends Operator("!=")
case object Exclamation extends Operator("!")
case object Bar extends Operator("|")
case object Ampersand extends Operator("&")
case object Caret extends Operator("^")
case object Percent extends Operator("%")
case object ForwardArrow extends Operator("->")
case object Or extends Operator("||")
case object And extends Operator("&&")
case object Equality extends Operator("==") with Reserved
case object GreaterThanOrEqual extends Operator(">=") with Reserved
case object PlusEquals extends Operator("+=") with Reserved
case object MinusEquals extends Operator("-=") with Reserved
case object LessThanOrEqual extends Operator("<=") with Reserved
case object NotEqual extends Operator("!=") with Reserved
case object ForwardArrow extends Operator("->") with Reserved
case object Or extends Operator("||") with Reserved
case object And extends Operator("&&") with Reserved
case object Minus extends Operator("-") with Reserved
case object Plus extends Operator("+") with Reserved
case object Asterisk extends Operator("*") with Reserved
case object ForwardSlash extends Operator("/") with Reserved
case object GreaterThan extends Operator(">") with Reserved
case object LessThan extends Operator("<") with Reserved
case object Equal extends Operator("=") with Reserved
case object Exclamation extends Operator("!") with Reserved
case object Bar extends Operator("|") with Reserved
case object Ampersand extends Operator("&") with Reserved
case object Caret extends Operator("^") with Reserved
case object Percent extends Operator("%") with Reserved

sealed abstract class Delimiter(override val lexeme: String) extends Token
case object OpenParen extends Delimiter("(")
case object CloseParen extends Delimiter(")")
case object OpenCurly extends Delimiter("{")
case object CloseCurly extends Delimiter("}")
case object OpenBracket extends Delimiter("[")
case object BlockBracket extends Delimiter("]")
case object Comma extends Delimiter(",")
case object Dot extends Delimiter(".")
case object Colon extends Delimiter(":")
case object Semicolon extends Delimiter(";")
case object OpenParen extends Delimiter("(") with Reserved
case object CloseParen extends Delimiter(")") with Reserved
case object OpenCurly extends Delimiter("{") with Reserved
case object CloseCurly extends Delimiter("}") with Reserved
case object OpenBracket extends Delimiter("[") with Reserved
case object BlockBracket extends Delimiter("]") with Reserved
case object Comma extends Delimiter(",") with Reserved
case object Dot extends Delimiter(".") with Reserved
case object Colon extends Delimiter(":") with Reserved
case object Semicolon extends Delimiter(";") with Reserved
case object Newline extends Delimiter(System.lineSeparator())
case object Space extends Delimiter(" ")
case object DoubleForwardSlash extends Delimiter("//")
case object DoubleForwardSlash extends Delimiter("//") with Reserved

sealed abstract class Punctuator(override val lexeme: String) extends Token
case object Hash extends Punctuator("#")
case object Hash extends Punctuator("#") with Reserved
case object Underscore extends Punctuator("_")
case object At extends Punctuator("@")
case object Tick extends Punctuator("`")
case object Quote extends Punctuator("\"")
case object At extends Punctuator("@") with Reserved
case object Tick extends Punctuator("`") with Reserved
case object Quote extends Punctuator("\"") with Reserved

sealed abstract class Data(override val lexeme: String) extends Token
case object Let extends Data("let")
case object Mut extends Data("mut")
case object Struct extends Data("struct")
case object Const extends Data("const")
case object Enum extends Data("enum")
case object Event extends Data("event")
case object Let extends Data("let") with Reserved
case object Mut extends Data("mut") with Reserved
case object Struct extends Data("struct") with Reserved
case object Const extends Data("const") with Reserved
case object Enum extends Data("enum") with Reserved
case object Event extends Data("event") with Reserved

sealed abstract class Control(override val lexeme: String) extends Token
case object If extends Control("if")
case object Else extends Control("else")
case object While extends Control("while")
case object Return extends Control("return")
case object For extends Control("for")
case object If extends Control("if") with Reserved
case object Else extends Control("else") with Reserved
case object While extends Control("while") with Reserved
case object Return extends Control("return") with Reserved
case object For extends Control("for") with Reserved

sealed abstract class AccessModifier(override val lexeme: String) extends Token
case object Pub extends AccessModifier("pub")
case object Pub extends AccessModifier("pub") with Reserved

sealed abstract class Definition(override val lexeme: String) extends Token
case object Fn extends Definition("fn")
case object Import extends Definition("import")
case object Contract extends Definition("Contract")
case object Abstract extends Definition("Abstract")
case object TxScript extends Definition("TxScript")
case object AssetScript extends Definition("AssetScript")
case object Interface extends Definition("Interface")
case object Fn extends Definition("fn") with Reserved
case object Import extends Definition("import") with Reserved
case object Contract extends Definition("Contract") with Reserved
case object Abstract extends Definition("Abstract") with Reserved
case object TxScript extends Definition("TxScript") with Reserved
case object AssetScript extends Definition("AssetScript") with Reserved
case object Interface extends Definition("Interface") with Reserved

sealed abstract class Inheritance(override val lexeme: String) extends Token
case object Extends extends Inheritance("extends")
case object Implements extends Inheritance("implements")
case object Extends extends Inheritance("extends") with Reserved
case object Implements extends Inheritance("implements") with Reserved

sealed abstract class Native(override val lexeme: String) extends Token
case object Using extends Native("using")
case object Emit extends Native("emit")
case object Embeds extends Native("embeds")

sealed abstract class Primitive(override val lexeme: String) extends Token
case object True extends Primitive("true")
case object False extends Primitive("false")
case object Alph_Small extends Primitive("alph")
case object Alph_Big extends Primitive("ALPH")
case object True extends Primitive("true") with Reserved
case object False extends Primitive("false") with Reserved
case object Alph_Small extends Primitive("alph") with Reserved
case object Alph_Big extends Primitive("ALPH") with Reserved

sealed trait Term extends Token
case class Name(lexeme: String) extends Term
case class NumberLiteral(lexeme: String) extends Term
case class StringLiteral(lexeme: String) extends Term

/**
* Order such that tokens such as:
* - `||` come before `|`
* - `+=` come before `+` and `=`
*/
implicit val reservedDescendingOrdering: Ordering[Reserved] = {
case (x: Reserved, y: Reserved) =>
val lengthCompare =
y.lexeme.length compare x.lexeme.length

if (lengthCompare == 0)
y.lexeme compare x.lexeme
else
lengthCompare
}

val reserved: Array[Reserved] =
EnumerationMacros
.sealedInstancesOf[Reserved]
.toArray
.sorted

}
Loading
Loading