Skip to content

Commit

Permalink
added type inference, autocasting, better parsing error messages.
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles Sherk committed Aug 11, 2021
1 parent 80730aa commit 295eccf
Show file tree
Hide file tree
Showing 16 changed files with 1,343 additions and 725 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ out
testOutputs/*
miscnotes
\#*
tmp/

### SBT ###

Expand Down
10 changes: 5 additions & 5 deletions src/main/scala/pipedsl/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ object Main {
throw new RuntimeException(s"File $inputFile does not exist")
}
val p: Parser = new Parser()
val r = p.parseAll(p.prog, new String(Files.readAllBytes(inputFile.toPath)))
val outputName = FilenameUtils.getBaseName(inputFile.getName) + ".parse"
val prog = p.parseCode(new String(Files.readAllBytes(inputFile.toPath)))
val outputName = FilenameUtils.getBaseName(inputFile.getName) + ".parse"
val outputFile = new File(Paths.get(outDir.getPath, outputName).toString)
if (printOutput) new PrettyPrinter(Some(outputFile)).printProgram(r.get)
r.get
if (printOutput) new PrettyPrinter(Some(outputFile)).printProgram(prog)
prog
}

def interpret(maxIterations:Int, memoryInputs: Seq[String], inputFile: File, outDir: File): Unit = {
Expand All @@ -67,7 +67,7 @@ object Main {
try {
val verifProg = AddVerifyValuesPass.run(prog)
val canonProg1 = new CanonicalizePass().run(verifProg)
val canonProg = (new TypeInference(autocast)).checkProgram(canonProg1)
val canonProg = new TypeInference(autocast).checkProgram(canonProg1)
val basetypes = BaseTypeChecker.check(canonProg, None)
val nprog = new BindModuleTypes(basetypes).run(canonProg)
TimingTypeChecker.check(nprog, Some(basetypes))
Expand Down
169 changes: 111 additions & 58 deletions src/main/scala/pipedsl/Parser.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package pipedsl
import scala.util.parsing.combinator._
import common.Syntax._
import common.Utilities._
import common.Locks._
import pipedsl.common.LockImplementation

Expand All @@ -10,6 +9,45 @@ import scala.util.matching.Regex
class Parser extends RegexParsers with PackratParsers {
type P[T] = PackratParser[T]

var bitVarCount = 0
var namedTypeCount = 0

/*TODO: is this really the best way of doing this?*/
var finalFail :Option[ParseResult[Any]] = None

val debug = false

/**
* adds debugging info to a parser if [[debug]] is true
*/
def dlog[T](p: => P[T])(msg :String) :P[T] = if (debug) log(p)(msg) else p

/**
* wrap this around a parser for its failures to be recorded in a way to be
* reported to the user
*/
def failRecord[T](p: => P[T]) :P[T] = Parser {in =>
val r = p(in)
r match {
case Failure(msg, _) if msg != "Base Failure" =>
finalFail = Some(r)
case _ => ()
}
r
}

def genBitVar() :TBitWidth =
{
bitVarCount += 1
TBitWidthVar(Id("__PARSER__BITWIDTH__" + bitVarCount))
}

def genTypeVar() :TNamedType =
{
namedTypeCount += 1
TNamedType(Id("__PARSER__NAMED__" + namedTypeCount))
}

// General parser combinators
def braces[T](parser: P[T]): P[T] = "{" ~> parser <~ "}"

Expand All @@ -32,31 +70,31 @@ class Parser extends RegexParsers with PackratParsers {
"\"" ~> "[^\"]*".r <~ "\"" ^^ {n => EString(n)}

private def toInt(n: Int, base: Int, bits: Option[Int], isUnsigned: Boolean): EInt = {
val e = EInt(n, base, if (bits.isDefined) bits.get else log2(n))
val e = EInt(n, base, if (bits.isDefined) bits.get else -1)
e.typ = bits match {
case Some(b) => Some(TSizedInt(TBitWidthLen(b), SignFactory.ofBool(!isUnsigned)))
case None if isUnsigned => Some(TSizedInt(TBitWidthLen(e.bits), TUnsigned()))
case _ => None
case None if isUnsigned => Some(TSizedInt(genBitVar(), TUnsigned()))
case _ => Some(genTypeVar())
}
// e.typ = Some(TSizedInt(TBitWidthLen(e.bits), unsigned = isUnsigned))
e
}

// Atoms
lazy val dec: P[EInt] = "u".? ~ "-?[0-9]+".r ~ angular(posint).? ^^ {
lazy val dec: P[EInt] = positioned { "u".? ~ "-?[0-9]+".r ~ angular(posint).? ^^ {
case u ~ n ~ bits => toInt(n.toInt, 10, bits, u.isDefined)
}
lazy val hex: P[EInt] = "u".? ~ "0x-?[0-9a-fA-F]+".r ~ angular(posint).? ^^ {
}}
lazy val hex: P[EInt] = positioned { "u".? ~ "0x-?[0-9a-fA-F]+".r ~ angular(posint).? ^^ {
case u ~ n ~ bits => toInt(Integer.parseInt(n.substring(2), 16), 16, bits, u.isDefined)
}
lazy val octal: P[EInt] = "u".? ~ "0-?[0-7]+".r ~ angular(posint).? ^^ {
}}
lazy val octal: P[EInt] = positioned { "u".? ~ "0-?[0-7]+".r ~ angular(posint).? ^^ {
case u ~ n ~ bits => toInt(Integer.parseInt(n.substring(1), 8), 8, bits, u.isDefined)
}
lazy val binary: P[EInt] = "u".? ~ "0b-?[0-1]+".r ~ angular(posint).? ^^ {
}}
lazy val binary: P[EInt] = positioned { "u".? ~ "0b-?[0-1]+".r ~ angular(posint).? ^^ {
case u ~ n ~ bits => toInt(Integer.parseInt(n.substring(2), 2), 2, bits, u.isDefined)
}
}}

lazy val num: P[EInt] = dec | hex | octal | binary
lazy val num: P[EInt] = binary | hex | octal | dec ^^
{ x: EInt => x.typ.get.setPos(x.pos); x }

lazy val boolean: P[Boolean] = "true" ^^ { _ => true } | "false" ^^ { _ => false }

Expand All @@ -66,7 +104,7 @@ class Parser extends RegexParsers with PackratParsers {
}

lazy val variable: P[EVar] = positioned {
iden ^^ (id => EVar(id))
iden ^^ (id => { EVar(id)})
}

lazy val recAccess: P[Expr] = positioned {
Expand All @@ -85,17 +123,16 @@ class Parser extends RegexParsers with PackratParsers {
expr ~ braces(posint ~ ":" ~ posint) ^^ { case n ~ (e ~ _ ~ s) => EBitExtract(n, s, e) }
}

lazy val ternary: P[Expr] = positioned {
parens(expr) ~ "?" ~ expr ~ ":" ~ expr ^^ { case c ~ _ ~ t ~ _ ~ v => ETernary(c, t, v) }
}

lazy val cast: P[Expr] = positioned {
"cast" ~> parens(expr ~ "," ~ typ) ^^ { case e ~ _ ~ t => ECast(t, e) }
"cast" ~> parens(expr ~ "," ~ typ) ^^ { case e ~ _ ~ t => ECast(t, e) }
}

//UOps
lazy val not: P[UOp] = positioned("!" ^^ { _ => NotOp() })

lazy val binv :P[UOp] = positioned("~" ^^ {_ => InvOp() } )

lazy val mag: P[Expr] = positioned {
"mag" ~> parens(expr) ^^ (e => EUop(MagOp(), e))
}
Expand All @@ -108,24 +145,20 @@ class Parser extends RegexParsers with PackratParsers {
}
lazy val simpleAtom: P[Expr] = positioned {
"call" ~> iden ~ parens(repsep(expr, ",")) ^^ { case i ~ args => ECall(i, args) } |
not ~ expr ^^ { case n ~ e => EUop(n, e) } |
not ~ simpleAtom ^^ { case n ~ e => EUop(n, e) } |
neg |
cast |
mag |
sign |
memAccess |
bitAccess |
recAccess |
ternary |
recLiteral |
hex |
octal |
binary |
dec |
num |
stringVal |
boolean ^^ (b => EBool(b)) |
iden ~ parens(repsep(expr, ",")) ^^ { case f ~ args => EApp(f, args) } |
variable |
variable |
parens(expr)
}

Expand Down Expand Up @@ -162,7 +195,11 @@ class Parser extends RegexParsers with PackratParsers {


def parseOp(base: P[Expr], op: P[BOp]): P[Expr] = positioned {
chainl1[Expr](base, op ^^ (op => EBinop(op, _, _)))
chainl1[Expr](base, op ^^ (op => { EBinop(op, _, _)}))
}

lazy val ternary: P[Expr] = positioned {
parens(expr) ~ "?" ~ nontern ~ ":" ~ nontern ^^ { case c ~ _ ~ t ~ _ ~ v => ETernary(c, t, v) }
}

lazy val binMul: P[Expr] = parseOp(simpleAtom, mulOps)
Expand All @@ -175,15 +212,16 @@ class Parser extends RegexParsers with PackratParsers {
lazy val binAnd: P[Expr] = parseOp(binBOr, and)
lazy val binOr: P[Expr] = parseOp(binAnd, or)
lazy val binConcat: P[Expr] = parseOp(binOr, concat)
lazy val expr: Parser[Expr] = positioned(binConcat)
lazy val nontern: Parser[Expr] = positioned(binConcat)
lazy val expr :Parser[Expr] = ternary | nontern


lazy val lhs: Parser[Expr] = memAccess | variable

lazy val simpleCmd: P[Command] = positioned {
speccall |
typ.? ~ variable ~ "=" ~ expr ^^ { case t ~ n ~ _ ~ r => n.typ = t; CAssign(n, r, t) } |
typ.? ~ lhs ~ "<-" ~ expr ^^ { case t ~ l ~ _ ~ r => l.typ = t
CRecv(l, r, t)
} |
typ.? ~ variable ~ "=" ~ expr ^^ { case t ~ n ~ _ ~ r => n.typ = t; CAssign(n, r, t) } |
typ.? ~ lhs ~ "<-" ~ expr ^^ { case t ~ l ~ _ ~ r => l.typ = t; CRecv(l, r, t) } |
check |
resolveSpec |
"start" ~> parens(iden) ^^ { i => CLockStart(i) } |
Expand All @@ -192,10 +230,10 @@ class Parser extends RegexParsers with PackratParsers {
"reserve" ~> parens(lockArg ~ ("," ~> lockType).?) ^^ { case i ~ t => CLockOp(i, Reserved, t)} |
"block" ~> parens(lockArg) ^^ { i => CLockOp(i, Acquired, None) } |
"release" ~> parens(lockArg) ^^ { i => CLockOp(i, Released, None)} |
"print" ~> parens(repsep(expr, ",")) ^^ (e => CPrint(e)) |
"print" ~> parens(repsep(expr, ",")) ^^ (e => { CPrint(e)}) |
"return" ~> expr ^^ (e => CReturn(e)) |
"output" ~> expr ^^ (e => COutput(e)) |
expr ^^ (e => CExpr(e))
"output" ~> expr ^^ (e => { COutput(e)}) |
expr ^^ (e => { CExpr(e)})
}

lazy val lockArg: P[LockArg] = positioned {
Expand Down Expand Up @@ -249,32 +287,44 @@ class Parser extends RegexParsers with PackratParsers {
}

lazy val conditional: P[Command] = positioned {
"if" ~> parens(expr) ~ block ~ ("else" ~> blockCmd).? ^^ {
case cond ~ cons ~ alt => CIf(cond, cons, if (alt.isDefined) alt.get else CEmpty())
failRecord("if" ~> parens(expr) ~ block ~ ("else" ~> blockCmd).? ^^ {
case cond ~ cons ~ alt => CIf(cond, cons, alt.getOrElse(CEmpty()))
})
}

lazy val parseEmpty: P[Command] =
{
"" ^^ {_ => CEmpty()}
}

def printPos[T](p: => P[T])(tag :String) :P[T] = Parser {
in =>
println(tag + in.pos); p(in)
}

lazy val seqCmd: P[Command] = {
simpleCmd ~ ";" ~ seqCmd ^^ { case c1 ~ _ ~ c2 => CSeq(c1, c2) } |
blockCmd ~ seqCmd ^^ { case c1 ~ c2 => CSeq(c1, c2) } |
simpleCmd <~ ";" | blockCmd | "" ^^ { _ => CEmpty() }
simpleCmd ~ ";" ~ seqCmd ^^ { case c1 ~ _ ~ c2 => CSeq(c1, c2) } |
simpleCmd <~ ";" | blockCmd
}

lazy val cmd: P[Command] = positioned {
lazy val cmd: P[Command] = failRecord( positioned {
seqCmd ~ "---" ~ cmd ^^ { case c1 ~ _ ~ c2 => CTBar(c1, c2) } |
seqCmd
}
"---" ~> cmd ^^ {c => CTBar(CEmpty(), c)} |
cmd <~ "---" ^^ {c => CTBar(c, CEmpty())} |
seqCmd } )


lazy val sizedInt: P[Type] = "int" ~> angular(posint) ^^ { bits => TSizedInt(TBitWidthLen(bits), TSigned() /*unsigned = false*/) } |
"uint" ~> angular(posint) ^^ { bits => TSizedInt(TBitWidthLen(bits), TUnsigned() /*unsigned = true*/) }
lazy val sizedInt: P[Type] = "int" ~> angular(posint) ^^ { bits => TSizedInt(TBitWidthLen(bits), TSigned() ) } |
"uint" ~> angular(posint) ^^ { bits => TSizedInt(TBitWidthLen(bits), TUnsigned() ) }

lazy val latency: P[Latency.Latency] =
"c" ^^ { _ => Latency.Combinational } |
"s" ^^ { _ => Latency.Sequential } |
"a" ^^ { _ => Latency.Asynchronous }

lazy val lat_and_ports: P[(Latency.Latency, Int)] =
latency ~ ((posint).?) ^^
latency ~ posint.? ^^
{
case lat ~ int => int match
{
Expand All @@ -301,16 +351,15 @@ class Parser extends RegexParsers with PackratParsers {
((l1, i1), (l2, i2))
} |
"a" ~> intopt ^^
{
case n =>
val v :(Latency.Latency, Int) = (Latency.Asynchronous, n)
{ n =>
val v: (Latency.Latency, Int) = (Latency.Asynchronous, n)
(v, v)
}
}

lazy val lockedMemory: P[Type] =
sizedInt ~ brackets(posint) ~
(angular(latsnports)
/*((latency ~ intopt) ~ ("," ~> (latency ~ intopt)))*/ ~ parens(iden).?).? ^^
~ parens(iden).?).? ^^
{
case elem ~ size ~ lats =>
if (lats.isDefined) {
Expand All @@ -334,14 +383,7 @@ class Parser extends RegexParsers with PackratParsers {
{
case elem ~ size ~ ports ~ lock =>
val mtyp = TMemType(elem, size, Latency.Asynchronous, Latency.Asynchronous, ports, ports)
lock match
{
case lk =>
//case Some(lk) =>
TLockedMemType(mtyp, None, LockImplementation.getLockImpl(lk))
// case None =>
// TLockedMemType(mtyp, None, LockImplementation.getDefaultLockImpl)
}
TLockedMemType(mtyp, None, LockImplementation.getLockImpl(lock))

}

Expand Down Expand Up @@ -434,9 +476,20 @@ class Parser extends RegexParsers with PackratParsers {
"circuit" ~> braces(cseq)
}


lazy val prog: P[Prog] = positioned {
fdef.* ~ moddef.* ~ circuit ^^ {
case f ~ p ~ c => Prog(f, p, c)
}
}


def parseCode(code :String) :Prog = {
val r = parseAll(prog, code)
r match {
case Success(program, _) => program
case x :Failure => throw new RuntimeException(finalFail.getOrElse(x).toString)
case x :Error => throw new RuntimeException(x.toString())
}
}
}
10 changes: 9 additions & 1 deletion src/main/scala/pipedsl/common/Errors.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package pipedsl.common

import scala.util.parsing.input.Position
import scala.util.parsing.input.{NoPosition, Position}
import Syntax._
import pipedsl.common.Locks.LockState

Expand Down Expand Up @@ -182,4 +182,12 @@ object Errors {
case class UnificationError(t1: Type, t2: Type) extends RuntimeException(
withPos(s"Unable to unify type $t1 and type $t2", t1.pos)
)

case class TypeMeetError(t1 :Type, t2 :Type) extends RuntimeException(
withPos(s"Cannot generate meet of type $t1 and type $t2", if (t1.pos eq NoPosition) t2.pos else t1.pos)
)

case class LackOfConstraints(e :Expr) extends RuntimeException(
withPos(s"Not enough constraints provided to infer types. Found error at $e", e.pos)
)
}
7 changes: 4 additions & 3 deletions src/main/scala/pipedsl/common/PrettyPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ class PrettyPrinter(output: Option[File]) {
case 16 => "0x" + v.toHexString
}) + "<" + bits.toString + ">"
case Syntax.EBool(v) => v.toString
case Syntax.EUop(op, ex) => op.op + printExprToString(ex)
case Syntax.EBinop(op, e1, e2) => printExprToString(e1) + " " + op.op + " " + printExprToString(e2)
case Syntax.EUop(op, ex) => op.op + "(" + printExprToString(ex) + ")"
case Syntax.EBinop(op, e1, e2) => "(" + printExprToString(e1) + " " + op.op + " " + printExprToString(e2) + ")"
case Syntax.ERecAccess(rec, fieldName) => printExprToString(rec) + "." + fieldName
case Syntax.ERecLiteral(fields) => "{" + fields.keySet.map(i => i.v + printExprToString(fields(i))).mkString(",") + "}"
case Syntax.EMemAccess(mem, index, m) => mem.v + "[" + printExprToString(index) +
Expand Down Expand Up @@ -158,7 +158,8 @@ class PrettyPrinter(output: Option[File]) {
"<" + rlat + rPorts + ", " + wlat + wPorts + ">"
case TModType(_, _, _, _) => "TODO MOD TYPE"
case TNamedType(name) => name.v
case _ => throw UnexpectedType(t.pos, "pretty printing", "unimplemented", t)
case x => x.toString
//case _ => throw UnexpectedType(t.pos, "pretty printing", "unimplemented", t)
}

def printStageGraph(name: String, stages: List[PStage]): Unit = {
Expand Down
Loading

0 comments on commit 295eccf

Please sign in to comment.