/
Identifier.scala
134 lines (105 loc) · 3.75 KB
/
Identifier.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package org.bykn.bosatsu
import cats.Order
import cats.parse.{Parser0 => P0, Parser => P}
import org.typelevel.paiges.{Doc, Document}
import Parser.{lowerIdent, upperIdent}
import cats.implicits._
sealed abstract class Identifier {
def asString: String
def sourceCodeRepr: String =
Identifier.document.document(this).renderWideStream.mkString
override def equals(that: Any): Boolean =
that match {
case ident: Identifier =>
asString == ident.asString
case _ => false
}
override val hashCode: Int = asString.hashCode
def toBindable: Option[Identifier.Bindable] =
this match {
case b: Identifier.Bindable => Some(b)
case _ => None
}
def toConstructor: Option[Identifier.Constructor] =
this match {
case c: Identifier.Constructor => Some(c)
case _ => None
}
}
object Identifier {
/** These are names that can appear in bindings. Importantly, we can't bind
* constructor names except to define types
*/
sealed abstract class Bindable extends Identifier
final case class Constructor(asString: String) extends Identifier
final case class Name(asString: String) extends Bindable
final case class Backticked(asString: String) extends Bindable
final case class Operator(asString: String) extends Bindable
private[this] val opPrefix = Doc.text("operator ")
object Bindable {
implicit def bindableOrder: Order[Bindable] =
Identifier.order
}
implicit def document[A <: Identifier]: Document[A] =
Document.instance[A] {
case Backticked(lit) =>
Doc.char('`') + Doc.text(Parser.escape('`', lit)) + Doc.char('`')
case Constructor(n) => Doc.text(n)
case Name(n) => Doc.text(n)
case Operator(n) => opPrefix + Doc.text(n)
}
val nameParser: P[Name] =
lowerIdent.map(n => Name(n.intern))
val consParser: P[Constructor] =
upperIdent.map(c => Constructor(c.intern))
/** This is used to apply operators, it is the raw operator tokens without an
* `operator` prefix
*/
val rawOperator: P[Operator] =
Operators.operatorToken.map(op => Operator(op.intern))
/** the keyword operator preceding a rawOperator
*/
val operator: P[Operator] =
(P.string("operator").soft *> Parser.spaces) *> rawOperator
/** Name, Backticked or non-raw operator
*/
val bindableParser: P[Bindable] =
// operator has to come first to not look like a Name
P.oneOf(operator :: nameParser :: Parser.escapedString('`').map { b =>
Backticked(b.intern)
} :: Nil)
val parser: P[Identifier] =
bindableParser.orElse(consParser)
// When we are allocating new names, we want
// them to be similar
def appendToName(i: Bindable, suffix: String): Bindable =
i match {
case Backticked(b) => Backticked(b + suffix)
case _ =>
// try to stry the same
val p = operator.orElse(nameParser)
val cand = i.sourceCodeRepr + suffix
p.parseAll(cand) match {
case Right(ident) => ident
case _ =>
// just turn it into a Backticked
Backticked(i.asString + suffix)
}
}
/** Build an Identifier by parsing a string
*/
def unsafe(str: String): Identifier =
unsafeParse(parser, str)
def unsafeBindable(str: String): Bindable =
unsafeParse(bindableParser, str)
def optionParse[A](pa: P0[A], str: String): Option[A] =
Parser.optionParse(pa, str)
def unsafeParse[A](pa: P0[A], str: String): A =
Parser.unsafeParse(pa, str)
implicit def order[A <: Identifier]: Order[A] =
Order.by[A, String](_.asString)
implicit def ordering[A <: Identifier]: Ordering[A] =
order[A].toOrdering
def synthetic(name: String): Bindable =
Name("_" + name)
}