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

Provide fastparse helpers for parser errors. #195

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 40 additions & 51 deletions core/src/main/scala-3/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,32 +74,28 @@ object Parser {
a
}

def optionlessSeq[A](option: Option[Seq[A]]): Seq[A] = option match {
case None => Seq[A]()
case Some(x) => x
}
extension [T](inline p: P[T])

/** Make the parser optional, parsing defaults if otherwise failing.
*
* @todo:
* Figure out dark implicit magic to figure out magically that the
* default default is "T()".
*
* @param default
* The default value to use if the parser fails.
* @return
* An optional parser, defaulting to default.
*/
inline def orElse[$: P](default: T): P[T] = P(
p.?.map(_.getOrElse(default))
)

/** Higher-order optional parser.
*
* Takes a parser p, and parses it optionally, defaulting the parsed value to
* the passed default.
*
* @todo:
* Figure out dark implicit magic to figure out magically that the default
* is "T()".
*
* @tparam T
* The optionally parsed type.
* @param p
* The parser to use optionally
* @param default
* The default value to use if the parser fails.
* @return
* A parser optionally parsing p, defaulting to default.
*/
inline def Optional[$: P, T](p: => P[T], default: T): P[T] = P(
p.?.map(_.getOrElse(default))
)
inline def ensure[$: P](check: T => Boolean, msg: T => String): P[T] = P(
p.flatMapX(parsed =>
if (check(parsed)) Pass(parsed) else Fail(msg(parsed))
)
)

/*≡==--==≡≡≡==--=≡≡*\
|| SCOPE ||
Expand Down Expand Up @@ -374,7 +370,7 @@ object Parser {
def FloatLiteral[$: P] = P(
CharIn("\\-\\+").? ~~ (Digit.repX(1) ~~ "." ~~ Digit.repX(1)).!
~~ (CharIn("eE")
~~ (CharIn("\\-\\+").? ~~ Digit.repX(1)).!).?.map(_.getOrElse("0"))
~~ (CharIn("\\-\\+").? ~~ Digit.repX(1)).!).orElse("0")
).map(parseFloatNum(_)) // substituted [0-9]* with [0-9]+

def notExcluded[$: P] = P(
Expand Down Expand Up @@ -521,7 +517,9 @@ object Parser {
def DialectNamespace[$: P] = P(DialectBareId)

def PrettyDialectReferenceName[$: P] = P(
(DialectNamespace ~ "." ~ PrettyDialectTypeOrAttReferenceName)
(DialectNamespace ~ "." ~ PrettyDialectTypeOrAttReferenceName).map(
(dialect, op) => f"$dialect.$op"
)
)

def OpaqueDialectReferenceName[$: P] = P(
Expand Down Expand Up @@ -713,9 +711,7 @@ class Parser(val context: MLContext, val args: Args = Args())
P(OperationPat.rep(at_least_this_many).map(_.to(ListType)))

def OperationPat[$: P]: P[Operation] = P(
OpResultList.?./.map(
optionlessSeq
).flatMap(Op(_)) ~/ TrailingLocation.?
OpResultList.orElse(Seq()).flatMap(Op(_)) ~/ TrailingLocation.?
)

def Op[$: P](resNames: Seq[String]) = P(
Expand All @@ -732,11 +728,11 @@ class Parser(val context: MLContext, val args: Args = Args())
})

def GenericOperation[$: P](resNames: Seq[String]) = P(
StringLiteral ~/ "(" ~ ValueUseList.?.map(optionlessSeq) ~ ")"
~/ SuccessorList.?.map(optionlessSeq)
~/ Optional(DictionaryProperties, DictType.empty)
~/ RegionList.?.map(optionlessSeq)
~/ Optional(DictionaryAttribute, DictType.empty) ~/ ":" ~/ FunctionType
StringLiteral ~/ "(" ~ ValueUseList.orElse(Seq()) ~ ")"
~/ SuccessorList.orElse(Seq())
~/ DictionaryProperties.orElse(DictType.empty)
~/ RegionList.orElse(Seq())
~/ (DictionaryAttribute).orElse(DictType.empty) ~/ ":" ~/ FunctionType
).map(
(
(
Expand All @@ -762,16 +758,9 @@ class Parser(val context: MLContext, val args: Args = Args())
)

def CustomOperation[$: P](resNames: Seq[String]) = P(
PrettyDialectReferenceName.flatMap { (x: String, y: String) =>
ctx.getOperation(s"${x}.${y}") match {
case Some(y) =>
y.parse(this)
case None =>
throw new Exception(
s"Operation ${x}.${y} is not defined in any supported Dialect."
)
}
}
PrettyDialectReferenceName./.map(name => (name, ctx.getOperation(name)))
.ensure(_._2 != None, (name, _) => f"Unregistered op ${name}")
.flatMap(_._2.get.parse(this))
)

def RegionList[$: P] =
Expand Down Expand Up @@ -802,7 +791,7 @@ class Parser(val context: MLContext, val args: Args = Args())
P(BlockLabel ~ Operations(0)).map(createBlock)

def BlockLabel[$: P] = P(
BlockId ~ BlockArgList.?.map(optionlessSeq) ~ ":"
BlockId ~ BlockArgList.orElse(Seq()) ~ ":"
)

/*≡==--==≡≡≡≡≡==--=≡≡*\
Expand Down Expand Up @@ -874,10 +863,10 @@ class Parser(val context: MLContext, val args: Args = Args())
def ValueIdAndType[$: P] = P(ValueId ~ ":" ~ Type)

def ValueIdAndTypeList[$: P] =
P(ValueIdAndType.rep(sep = ","))
P(ValueIdAndType.rep(sep = ",")).orElse(Seq())

def BlockArgList[$: P] =
P("(" ~ ValueIdAndTypeList.? ~ ")").map(optionlessSeq)
P("(" ~ ValueIdAndTypeList ~ ")")

// [x] dictionary-properties ::= `<` dictionary-attribute `>`
// [x] dictionary-attribute ::= `{` (attribute-entry (`,` attribute-entry)*)? `}`
Expand Down Expand Up @@ -910,7 +899,7 @@ class Parser(val context: MLContext, val args: Args = Args())
* present.
*/
inline def OptionalProperties[$: P]() =
Optional(DictionaryProperties, DictType.empty)
(DictionaryProperties).orElse(DictType.empty)

/** Parses an optional attributes dictionary from the input.
*
Expand All @@ -919,7 +908,7 @@ class Parser(val context: MLContext, val args: Args = Args())
* present.
*/
inline def OptionalAttributes[$: P] =
Optional(DictionaryAttribute, DictType.empty)
(DictionaryAttribute).orElse(DictType.empty)

/** Parses an optional attributes dictionary from the input, preceded by the
* `attributes` keyword.
Expand All @@ -928,7 +917,7 @@ class Parser(val context: MLContext, val args: Args = Args())
* An optional dictionary of attributes - empty if no keyword is present.
*/
inline def OptionalKeywordAttributes[$: P] =
Optional("attributes" ~ DictionaryAttribute, DictType.empty)
("attributes" ~ DictionaryAttribute).orElse(DictType.empty)

/*≡==--==≡≡≡≡==--=≡≡*\
|| PARSE FUNCTION ||
Expand Down
25 changes: 12 additions & 13 deletions core/src/main/scala-3/builtin/AttrParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,29 @@
class AttrParser(val ctx: MLContext) {

def DialectAttribute[$: P]: P[Attribute] = P(
"#" ~ PrettyDialectReferenceName.flatMap { (x: String, y: String) =>
ctx.getAttribute(s"${x}.${y}") match {
"#" ~ PrettyDialectReferenceName.flatMap { (name: String) =>
ctx.getAttribute(name) match {
case Some(y) =>
y.parse(this)
case None =>
throw new Exception(
s"Type ${x} is not defined in any supported Dialect."
s"Type $name is not defined in any supported Dialect."
)
}
}
)

def DialectType[$: P]: P[Attribute] = P(
"!" ~ PrettyDialectReferenceName.flatMap { (x: String, y: String) =>
ctx.getAttribute(s"${x}.${y}") match {
"!" ~ PrettyDialectReferenceName.flatMap { (name: String) =>
ctx.getAttribute(name) match {
case Some(y) =>
y.parse(this)
case None =>
println(
s"Type ${x} is not defined in any supported Dialect."
s"Type $name is not defined in any supported Dialect."

Check warning on line 50 in core/src/main/scala-3/builtin/AttrParser.scala

View check run for this annotation

Codecov / codecov/patch

core/src/main/scala-3/builtin/AttrParser.scala#L50

Added line #L50 was not covered by tests
)
throw new Exception(
s"Type ${x} is not defined in any supported Dialect."
s"Type $name is not defined in any supported Dialect."

Check warning on line 53 in core/src/main/scala-3/builtin/AttrParser.scala

View check run for this annotation

Codecov / codecov/patch

core/src/main/scala-3/builtin/AttrParser.scala#L53

Added line #L53 was not covered by tests
)
}
}
Expand Down Expand Up @@ -184,14 +184,13 @@
// dense-array-attribute ::= `array` `<` (integer-type | float-type) (`:` tensor-literal)? `>`

def DenseArrayAttributeP[$: P]: P[DenseArrayAttr] = P(
"array<" ~ (((IntegerTypeP) ~ (":" ~ IntDataP.rep(sep = ",")).?.map(
_.getOrElse(Seq())
"array<" ~ (((IntegerTypeP) ~ (":" ~ IntDataP.rep(sep = ",")).orElse(
Seq()
)).map((typ: IntegerType, x: Seq[IntData]) =>
DenseArrayAttr(typ, x.map(IntegerAttr(_, typ)))
) | ((FloatTypeP) ~ (":" ~ FloatDataP.rep(sep = ",")).?.map(
_.getOrElse(Seq())
)).map((typ: FloatType, x: Seq[FloatData]) =>
DenseArrayAttr(typ, x.map(FloatAttr(_, typ)))
) | ((FloatTypeP) ~ (":" ~ FloatDataP.rep(sep = ",")).orElse(Seq())).map(
(typ: FloatType, x: Seq[FloatData]) =>
DenseArrayAttr(typ, x.map(FloatAttr(_, typ)))
)) ~ ">"
)

Expand Down
24 changes: 9 additions & 15 deletions dialects/src/main/scala-3/lingodb/RelAlgOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import scair.Parser
import scair.Parser.BareId
import scair.Parser.E
import scair.Parser.ValueId
import scair.Parser.optionlessSeq
import scair.Parser.orElse
import scair.Parser.whitespace
import scair.dialects.LingoDB.SubOperatorOps.*
import scair.dialects.LingoDB.TupleStream.*
Expand Down Expand Up @@ -136,13 +136,13 @@ case class SortSpecificationAttr(

private def DialectRegion[$: P](parser: Parser) = P(
E({ parser.enterLocalRegion })
~ (parser.BlockArgList.?.map(optionlessSeq).map(
(x: Seq[(String, Attribute)]) => {
~ (parser.BlockArgList
.orElse(Seq())
.map((x: Seq[(String, Attribute)]) => {
val b = new Block(ListType.empty, ListType.from(x.map(_._2)))
parser.currentScope.defineValues(x.map(_._1) zip b.arguments)
b
}
)
})
~ "{"
~ parser.Operations(1) ~ "}").map((b: Block, y: ListType[Operation]) => {
b.operations ++= y
Expand Down Expand Up @@ -244,8 +244,8 @@ object SelectionOp extends OperationObject {
override def parse[$: P](
parser: Parser
): P[Operation] = P(
ValueId ~ DialectRegion(parser)
~ "attributes" ~ parser.OptionalAttributes
ValueId ~ DialectRegion(parser) ~
parser.OptionalKeywordAttributes
)
.map(
(
Expand Down Expand Up @@ -325,10 +325,7 @@ object MapOp extends OperationObject {
~ "computes" ~ ":"
~ "[" ~ ColumnDefAttr.parse(parser).rep.map(ArrayAttribute(_)) ~ "]"
~ DialectRegion(parser)
~ Parser.Optional(
"attributes" ~ parser.DictionaryAttribute,
DictType.empty
)
~ parser.OptionalKeywordAttributes
).map(
(
x: String,
Expand Down Expand Up @@ -430,10 +427,7 @@ object AggregationOp extends OperationObject {
.rep(sep = ",")
.map(ArrayAttribute(_)) ~ "]"
~ DialectRegion(parser)
~ Parser.Optional(
"attributes" ~ parser.DictionaryAttribute,
DictType.empty
)
~ parser.OptionalKeywordAttributes
).map(
(
x: String,
Expand Down
3 changes: 2 additions & 1 deletion dialects/src/main/scala-3/lingodb/TupleStream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import fastparse.*
import scair.AttrParser
import scair.Parser
import scair.Parser.ValueId
import scair.Parser.orElse
import scair.Parser.whitespace
import scair.dialects.builtin.*
import scair.ir.*
Expand Down Expand Up @@ -142,7 +143,7 @@ object ReturnOp extends OperationObject {
): P[Operation] = P(
parser.OptionalAttributes ~ (ValueId.rep(sep = ",")
~ ":" ~
parser.Type.rep(sep = ",")).?.map(makeResults)
parser.Type.rep(sep = ",")).orElse((Seq(), Seq()))
).map((x: DictType[String, Attribute], y: (Seq[String], Seq[Attribute])) =>
parser.generateOperation(
opName = name,
Expand Down