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

Add list literal constructors #91

Merged
merged 7 commits into from
Oct 28, 2018
Merged
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
18 changes: 16 additions & 2 deletions core/src/main/resources/bosatsu/predef.bosatsu
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ export [
String,
Test(),
add,
eq_Int,
cmp_Int,
concat,
eq_Int,
foldLeft,
mod_Int,
sub,
range,
reverse,
reverse_concat,
sub,
times,
trace,
]
Expand All @@ -31,6 +34,17 @@ external def foldLeft(lst: List[a], item: b, fn: b -> a -> b) -> b

external def range(exclusiveUpper: Int) -> List[Int]

def reverse_concat(front: List[a], back: List[a]) -> List[a]:
foldLeft(front, back, \tail, h -> NonEmptyList(h, tail))

def reverse(as: List[a]) -> List[a]:
reverse_concat(as, EmptyList)

def concat(front: List[a], back: List[a]) -> List[a]:
match back:
EmptyList: front
_: reverse_concat(reverse(front), back)

enum Comparison:
LT
EQ
Expand Down
4 changes: 3 additions & 1 deletion core/src/main/scala/org/bykn/bosatsu/Codegen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ trait CodeGen {
case Annotation(expr, _, _) =>
// TODO we might want to use the type info
apply(expr, topLevel, pack)
case Var(n, _, _) =>
case Var(None, n, _, _) =>
NameKind(pack, n) match {
case Some(NameKind.Constructor(_, _, _, _)) =>
Monad[Output].pure(Doc.text(toConstructorName(n)))
Expand All @@ -220,6 +220,8 @@ trait CodeGen {
v = scope.toMap.get(n).fold(toExportedName(n)) { u => toFieldName(n, u.id) }
} yield Doc.text(v)
}
case Var(Some(_), n, _, _) =>
sys.error("TODO: handle fully qualified names")
case App(fn, arg, _, _) =>
for {
fnDoc <- apply(fn, topLevel, pack)
Expand Down
60 changes: 50 additions & 10 deletions core/src/main/scala/org/bykn/bosatsu/Declaration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ sealed abstract class Declaration {
case Apply(fn, args, dotApply) =>
val fnDoc = fn match {
case Var(n) => Doc.text(n)
case Constructor(c) => Doc.text(c)
case p@Parens(_) => p.toDoc
case other => Doc.char('(') + other.toDoc + Doc.char(')')
}
Expand Down Expand Up @@ -92,6 +93,9 @@ sealed abstract class Declaration {
case Parens(p) =>
Doc.char('(') + p.toDoc + Doc.char(')')
case Var(name) => Doc.text(name)

case ListDecl(list) =>
ListLang.document.document(list)
}
}

Expand All @@ -115,7 +119,7 @@ sealed abstract class Declaration {
case Comment(CommentStatement(_, Padding(_, decl))) =>
loop(decl).map(_ => this)
case Constructor(name) =>
Expr.Var(name, this)
Expr.Var(None, name, this)
case DefFn(defstmt@DefStatement(_, _, _, _)) =>
val (bodyExpr, inExpr) = defstmt.result match {
case (oaBody, Padding(_, in)) =>
Expand All @@ -124,10 +128,6 @@ sealed abstract class Declaration {
val lambda = defstmt.toLambdaExpr(bodyExpr, this)(_.toType(nameToType))
Expr.Let(defstmt.name, lambda, inExpr, this)
case IfElse(ifCases, elseCase) =>

// TODO: we need a way to have an full name to the constructor in order for this "macro" to
// be safe. So, we want to say Bosatsu/Predef#True or something.
// we could just have ConstructorName require a PackageName
def ifExpr(cond: Expr[Declaration], ifTrue: Expr[Declaration], ifFalse: Expr[Declaration]): Expr[Declaration] =
Expr.If(cond, ifTrue, ifFalse, this)

Expand All @@ -151,14 +151,42 @@ sealed abstract class Declaration {
case Parens(p) =>
loop(p).map(_ => this)
case Var(name) =>
Expr.Var(name, this)
Expr.Var(None, name, this)
case Match(arg, branches) =>
val expBranches = branches.get.map { case (pat, oidecl) =>
val decl = oidecl.get
val newPattern = pat.mapName(nameToCons).mapType(_.toType(nameToType))
(newPattern, loop(decl))
}
Expr.Match(loop(arg), expBranches, this)
case l@ListDecl(list) =>
list match {
case ListLang.Cons(items) =>
val revDecs: List[ListLang.SpliceOrItem[Expr[Declaration]]] = items.reverseMap {
case ListLang.SpliceOrItem.Splice(s) =>
ListLang.SpliceOrItem.Splice(loop(s))
case ListLang.SpliceOrItem.Item(item) =>
ListLang.SpliceOrItem.Item(loop(item))
}

// TODO we need to refer to Predef/EmptyList no matter what here
// but we have no way to fully refer to an item
val pn = Option(Predef.packageName)
val empty: Expr[Declaration] = Expr.Var(pn, "EmptyList", l)
def cons(head: Expr[Declaration], tail: Expr[Declaration]): Expr[Declaration] =
Expr.App(Expr.App(Expr.Var(pn, "NonEmptyList", l), head, l), tail, l)

def concat(headList: Expr[Declaration], tail: Expr[Declaration]): Expr[Declaration] =
Expr.App(Expr.App(Expr.Var(pn, "concat", l), headList, l), tail, l)

revDecs.foldLeft(empty) {
case (tail, ListLang.SpliceOrItem.Item(i)) =>
cons(i, tail)
case (tail, ListLang.SpliceOrItem.Splice(s)) =>
concat(s, tail)
}
case ListLang.Comprehension(_, _, _, _) => ???
}
}

loop(this)
Expand Down Expand Up @@ -195,6 +223,11 @@ object Declaration {
case class Parens(of: Declaration)(implicit val region: Region) extends Declaration
case class Var(name: String)(implicit val region: Region) extends Declaration

/**
* This represents the list construction language
*/
case class ListDecl(list: ListLang[Declaration])(implicit val region: Region) extends Declaration

private val restP: Indy[Padding[Declaration]] =
(Indy.parseIndent *> parser).mapF(Padding.parser(_))

Expand Down Expand Up @@ -302,11 +335,16 @@ object Declaration {
case (varD@Var(v), Some(fn)) => fn(v, varD.region)
}

private def listP(p: P[Declaration]): P[ListDecl] =
ListLang.parser(p)
.region
.map { case (r, l) => ListDecl(l)(r) }

private[this] val parserCache: String => P[Declaration] =
Memoize.function[String, P[Declaration]] { (indent, rec) =>

val postOperators: List[P[Declaration => Declaration]] = {
val params = P(rec(indent).nonEmptyList.parens)
val params = P(rec(indent).nonEmptyList.parensCut)
// here we are using . syntax foo.bar(1, 2)
val dotApply =
P("." ~/ varP ~ params.?).region.map { case (r2, (fn, argsOpt)) =>
Expand All @@ -323,7 +361,7 @@ object Declaration {

// here is if/ternary operator
val ternary =
P(spaces ~ P("if") ~ spaces ~/ rec(indent) ~ spaces ~ "else" ~ spaces ~ rec(indent))
P(spaces ~ P("if") ~ spaces ~ rec(indent) ~ spaces ~ "else" ~ spaces ~/ rec(indent))
.region
.map { case (region, (cond, falseCase)) =>
{ trueCase: Declaration =>
Expand All @@ -337,7 +375,7 @@ object Declaration {

val recIndy = Indy(rec)

val prefix = P(defP(indent) | literalIntP | literalStringP | lambdaP(indent) | matchP(recIndy)(indent) |
val prefix = P(listP(rec(indent)) | defP(indent) | literalIntP | literalStringP | lambdaP(indent) | matchP(recIndy)(indent) |
ifElseP(recIndy)(indent) | varOrBind(indent) | constructorP | commentP(indent) |
P(rec(indent).parens).region.map { case (r, p) => Parens(p)(r) })

Expand All @@ -350,7 +388,9 @@ object Declaration {
case h :: tail => loop(h(a), tail)
}

P(prefix ~ opsList).map { case (arg, fns) => loop(arg, fns) }
P(prefix ~ opsList)
.map { case (arg, fns) => loop(arg, fns) }
.opaque(s"Declaration.parser($indent)")
}

lazy val parser: Indy[Declaration] =
Expand Down
6 changes: 5 additions & 1 deletion core/src/main/scala/org/bykn/bosatsu/Evaluation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,14 @@ case class Evaluation(pm: PackageMap.Inferred, externals: Externals) {
// TODO, we need to probably do something with this
evalTypedExpr(p, e, recurse)
case Annotation(e, _, _) => evalTypedExpr(p, e, recurse)
case Var(v, _, _) =>
case Var(None, v, _, _) =>
val onMissing = recurse((p, Left(v)))._1

Scoped.orElse(v, onMissing)
case Var(Some(p), v, _, _) =>
val pack = pm.toMap.get(p).getOrElse(sys.error(s"cannot find $p, shouldn't happen due to typechecking"))
val (scoped, _) = eval((Package.asInferred(pack), Left(v)))
scoped
case App(AnnotatedLambda(name, _, fn, _), arg, _, _) =>
val argE = recurse((p, Right(arg)))._1
val fnE = recurse((p, Right(fn)))._1
Expand Down
18 changes: 9 additions & 9 deletions core/src/main/scala/org/bykn/bosatsu/Expr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ sealed abstract class Expr[T] {
def setTag(t: T): Expr[T] =
this match {
case a@Annotation(_, _, _) => a.copy(tag = t)
case v@Var(_, _) => v.copy(tag = t)
case v@Var(_, _, _) => v.copy(tag = t)
case a@App(_, _, _) => a.copy(tag = t)
case l@Lambda(_, _, _) => l.copy(tag = t)
case a@AnnotatedLambda(_, _, _, _) => a.copy(tag = t)
Expand All @@ -32,7 +32,7 @@ object Expr {
case class AnnotatedLambda[T](arg: String, tpe: rankn.Type, expr: Expr[T], tag: T) extends Expr[T] {
def toLambda: Lambda[T] = Lambda(arg, expr, tag)
}
case class Var[T](name: String, tag: T) extends Expr[T]
case class Var[T](pack: Option[PackageName], name: String, tag: T) extends Expr[T]
case class App[T](fn: Expr[T], arg: Expr[T], tag: T) extends Expr[T]
case class Lambda[T](arg: String, expr: Expr[T], tag: T) extends Expr[T]
case class Let[T](arg: String, expr: Expr[T], in: Expr[T], tag: T) extends Expr[T]
Expand All @@ -50,7 +50,7 @@ object Expr {
(traverseType(e, fn), fn(tpe)).mapN(Annotation(_, _, a))
case AnnotatedLambda(arg, tpe, expr, a) =>
(fn(tpe), traverseType(expr, fn)).mapN(AnnotatedLambda(arg, _, _, a))
case v@Var(_, _) => F.pure(v)
case v@Var(_, _, _) => F.pure(v)
case App(f, a, t) =>
(traverseType(f, fn), traverseType(a, fn)).mapN(App(_, _, t))
case Lambda(arg, expr, t) =>
Expand Down Expand Up @@ -85,8 +85,8 @@ object Expr {
Annotation(nest(expr), tpe, e)
case AnnotatedLambda(arg, tpe, expr, _) =>
AnnotatedLambda(arg, tpe, nest(expr), e)
case Var(s, _) =>
Var(s, e)
case Var(p, s, _) =>
Var(p, s, e)
case App(fn, a, _) =>
App(nest(fn), nest(a), e)
case Lambda(arg, expr, _) =>
Expand Down Expand Up @@ -120,8 +120,8 @@ object Expr {
(e.traverse(f), f(a)).mapN(Annotation(_, tpe, _))
case AnnotatedLambda(arg, tpe, expr, a) =>
(expr.traverse(f), f(a)).mapN(AnnotatedLambda(arg, tpe, _, _))
case Var(s, t) =>
f(t).map(Var(s, _))
case Var(p, s, t) =>
f(t).map(Var(p, s, _))
case App(fn, a, t) =>
(fn.traverse(f), a.traverse(f), f(t)).mapN { (fn1, a1, b) =>
App(fn1, a1, b)
Expand Down Expand Up @@ -155,7 +155,7 @@ object Expr {
case AnnotatedLambda(_, _, e, tag) =>
val b1 = foldLeft(e, b)(f)
f(b1, tag)
case Var(_, tag) => f(b, tag)
case Var(_, _, tag) => f(b, tag)
case App(fn, a, tag) =>
val b1 = foldLeft(fn, b)(f)
val b2 = foldLeft(a, b1)(f)
Expand Down Expand Up @@ -188,7 +188,7 @@ object Expr {
case AnnotatedLambda(_, _, e, tag) =>
val lb1 = foldRight(e, lb)(f)
f(tag, lb1)
case Var(_, tag) => f(tag, lb)
case Var(_, _, tag) => f(tag, lb)
case App(fn, a, tag) =>
val b1 = f(tag, lb)
val b2 = foldRight(a, b1)(f)
Expand Down
78 changes: 78 additions & 0 deletions core/src/main/scala/org/bykn/bosatsu/ListLang.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.bykn.bosatsu

import fastparse.all._

import Parser.{maybeSpacesAndLines, spacesAndLines}
import org.typelevel.paiges.{Doc, Document}

/**
* Represents the list construction sublanguage
*/
sealed abstract class ListLang[A]
object ListLang {
sealed abstract class SpliceOrItem[A] {
def value: A
}
object SpliceOrItem {
case class Splice[A](value: A) extends SpliceOrItem[A]
case class Item[A](value: A) extends SpliceOrItem[A]

def parser[A](pa: P[A]): P[SpliceOrItem[A]] =
P("*" ~ pa).map(Splice(_)) | pa.map(Item(_))

implicit def document[A](implicit A: Document[A]): Document[SpliceOrItem[A]] =
Document.instance[SpliceOrItem[A]] {
case Splice(a) => Doc.char('*') + A.document(a)
case Item(a) => A.document(a)
}
}

case class Cons[A](items: List[SpliceOrItem[A]]) extends ListLang[A]
case class Comprehension[A](expr: SpliceOrItem[A], binding: A, in: A, filter: Option[A]) extends ListLang[A]

def parser[A](pa: P[A]): P[ListLang[A]] = {
val sia = SpliceOrItem.parser(pa) ~ maybeSpacesAndLines
val comma = P("," ~ maybeSpacesAndLines)
val cons = sia
.rep(sep = comma)
.map { tail => { a: SpliceOrItem[A] => Cons(a :: tail.toList) } }
.opaque("ConsListTail")

val filterExpr = P("if" ~ spacesAndLines ~/ pa).?
val comp = P(
"for" ~ spacesAndLines ~ pa ~ spacesAndLines ~
"in" ~ spacesAndLines ~ pa ~ maybeSpacesAndLines ~
filterExpr)
.map { case (b, i, f) =>
{ e: SpliceOrItem[A] => Comprehension(e, b, i, f) }
}
.opaque("ListComprehension")

val inner = (comma ~ cons) | comp

P("[" ~ maybeSpacesAndLines ~ (sia ~ inner.?).? ~ maybeSpacesAndLines ~ "]")
.map {
case None => Cons(Nil)
case Some((a, None)) => Cons(a :: Nil)
case Some((a, Some(rest))) => rest(a)
}
}

implicit def document[A](implicit A: Document[A]): Document[ListLang[A]] =
Document.instance[ListLang[A]] {
case Cons(items) =>
Doc.char('[') + Doc.intercalate(Doc.text(", "),
items.map(SpliceOrItem.document(A).document(_))) +
Doc.char(']')
case Comprehension(e, b, i, f) =>
val filt = f match {
case None => Doc.empty
case Some(e) => Doc.text(" if ") + A.document(e)
}
Doc.char('[') + SpliceOrItem.document(A).document(e) + Doc.text(" for ") +
A.document(b) + Doc.text(" in ") +
A.document(i) + filt +
Doc.char(']')
}
}

9 changes: 8 additions & 1 deletion core/src/main/scala/org/bykn/bosatsu/Package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,15 @@ object Package {
val importedValues: Map[String, rankn.Type] =
Referant.importedValues(imps) ++ typeEnv.localValuesOf(p)

val withFQN: Map[(Option[PackageName], String), rankn.Type] =
(Referant.fullyQualifiedImportedValues(imps)(_.unfix.name)
.iterator
.map { case ((p, n), t) => ((Some(p), n), t) } ++
importedValues.iterator.map { case (n, t) => ((None, n), t) }
).toMap

Infer.typeCheckLets(lets)
.runFully(importedValues,
.runFully(withFQN,
Referant.typeConstructors(imps) ++ typeEnv.typeConstructors
)
.map { lets => (typeEnv, lets) }
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/scala/org/bykn/bosatsu/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ object Parser {
val nonSpaces: P[String] = P(CharsWhile { c => !isSpace(c) }.!)
val maybeSpace: P[Unit] = spaces.?

val spacesAndLines: P[Unit] = P(CharsWhile { c =>
isSpace(c) || (c == '\n' || c == '\r')
}).opaque("spacesAndLines")

val maybeSpacesAndLines: P[Unit] =
spacesAndLines.?.opaque("maybeSpacesAndLines")

val lowerIdent: P[String] =
P(CharIn('a' to 'z').! ~ CharsWhile(identifierChar _).?.!)
.map { case (a, b) => a + b }
Expand Down Expand Up @@ -233,6 +240,9 @@ object Parser {

def parens: P[T] =
wrappedSpace("(", ")")

def parensCut: P[T] =
P("(" ~/ maybeSpace ~ item ~ maybeSpace ~ ")")
}

val toEOL: P[Unit] = P(maybeSpace ~ "\n")
Expand Down
Loading