feat: debug symbols for line file/line location and functions in main file #44
@@ -2,6 +2,7 @@ package wacc
|
|||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
|
import wacc.ast.Position
|
||||||
|
|
||||||
private class LabelGenerator {
|
private class LabelGenerator {
|
||||||
import assemblyIR._
|
import assemblyIR._
|
||||||
@@ -9,7 +10,9 @@ private class LabelGenerator {
|
|||||||
import asmGenerator.escaped
|
import asmGenerator.escaped
|
||||||
|
|
||||||
private val strings = mutable.HashMap[String, String]()
|
private val strings = mutable.HashMap[String, String]()
|
||||||
|
private val files = mutable.HashMap[String, Int]()
|
||||||
private var labelVal = -1
|
private var labelVal = -1
|
||||||
|
private var permittedFuncFile: Option[String] = None
|
||||||
|
|
||||||
/** Get an arbitrary label. */
|
/** Get an arbitrary label. */
|
||||||
def getLabel(): String = {
|
def getLabel(): String = {
|
||||||
@@ -39,6 +42,25 @@ private class LabelGenerator {
|
|||||||
def getLabelArg(src: String, name: String): LabelArg =
|
def getLabelArg(src: String, name: String): LabelArg =
|
||||||
LabelArg(strings.getOrElseUpdate(src, s".L.$name.str${strings.size}"))
|
LabelArg(strings.getOrElseUpdate(src, s".L.$name.str${strings.size}"))
|
||||||
|
|
||||||
|
/** Get a debug directive for a file. */
|
||||||
|
def getDebugFile(file: java.io.File): Int =
|
||||||
|
files.getOrElseUpdate(file.getCanonicalPath, files.size)
|
||||||
|
|
||||||
|
/** Get a debug directive for a function. */
|
||||||
|
def getDebugFunc(pos: Position, name: String, label: LabelDef): Chain[AsmLine] = {
|
||||||
|
permittedFuncFile match {
|
||||||
|
case Some(f) if f != pos.file.getCanonicalPath => Chain.empty
|
||||||
|
case _ =>
|
||||||
|
val customLabel = if name == "main" then Chain.empty else Chain(LabelDef(name))
|
||||||
|
permittedFuncFile = Some(pos.file.getCanonicalPath)
|
||||||
|
customLabel ++ Chain(
|
||||||
|
Directive.Location(getDebugFile(pos.file), pos.line, None),
|
||||||
|
Directive.Type(label, SymbolType.Function),
|
||||||
|
Directive.Func(name, label)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Generate the assembly labels for constants that were labelled using the LabelGenerator. */
|
/** Generate the assembly labels for constants that were labelled using the LabelGenerator. */
|
||||||
def generateConstants: Chain[AsmLine] =
|
def generateConstants: Chain[AsmLine] =
|
||||||
strings.foldLeft(Chain.empty) { case (acc, (str, label)) =>
|
strings.foldLeft(Chain.empty) { case (acc, (str, label)) =>
|
||||||
@@ -47,4 +69,10 @@ private class LabelGenerator {
|
|||||||
Directive.Asciz(str.escaped)
|
Directive.Asciz(str.escaped)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Generates debug directives that were created using the LabelGenerator. */
|
||||||
|
def generateDebug: Chain[AsmLine] =
|
||||||
|
files.foldLeft(Chain.empty) { case (acc, (file, no)) =>
|
||||||
|
acc :+ Directive.File(no, file)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,11 +36,14 @@ object asmGenerator {
|
|||||||
given labelGenerator: LabelGenerator = LabelGenerator()
|
given labelGenerator: LabelGenerator = LabelGenerator()
|
||||||
val Program(funcs, main) = microProg
|
val Program(funcs, main) = microProg
|
||||||
|
|
||||||
val progAsm = Chain(LabelDef("main")).concatAll(
|
val mainLabel = LabelDef("main")
|
||||||
|
val mainAsm = labelGenerator.getDebugFunc(microProg.pos, "main", mainLabel) + mainLabel
|
||||||
|
val progAsm = mainAsm.concatAll(
|
||||||
funcPrologue(),
|
funcPrologue(),
|
||||||
main.foldMap(generateStmt(_)),
|
main.foldMap(generateStmt(_)),
|
||||||
Chain.one(Xor(RAX, RAX)),
|
Chain.one(Xor(RAX, RAX)),
|
||||||
funcEpilogue(),
|
funcEpilogue(),
|
||||||
|
Chain(Directive.Size(mainLabel, SizeExpr.Relative(mainLabel)), Directive.EndFunc),
|
||||||
generateBuiltInFuncs(),
|
generateBuiltInFuncs(),
|
||||||
RuntimeError.all.foldMap(_.generate),
|
RuntimeError.all.foldMap(_.generate),
|
||||||
funcs.foldMap(generateUserFunc(_))
|
funcs.foldMap(generateUserFunc(_))
|
||||||
@@ -51,6 +54,7 @@ object asmGenerator {
|
|||||||
Directive.Global("main"),
|
Directive.Global("main"),
|
||||||
Directive.RoData
|
Directive.RoData
|
||||||
).concatAll(
|
).concatAll(
|
||||||
|
labelGenerator.generateDebug,
|
||||||
labelGenerator.generateConstants,
|
labelGenerator.generateConstants,
|
||||||
Chain.one(Directive.Text),
|
Chain.one(Directive.Text),
|
||||||
progAsm
|
progAsm
|
||||||
@@ -75,7 +79,10 @@ object asmGenerator {
|
|||||||
// Setup the stack with param 7 and up
|
// Setup the stack with param 7 and up
|
||||||
func.params.drop(argRegs.size).foreach(stack.reserve(_))
|
func.params.drop(argRegs.size).foreach(stack.reserve(_))
|
||||||
stack.reserve(Size.Q64) // Reserve return pointer slot
|
stack.reserve(Size.Q64) // Reserve return pointer slot
|
||||||
var asm = Chain.one[AsmLine](labelGenerator.getLabelDef(func.name))
|
val funcLabel = labelGenerator.getLabelDef(func.name)
|
||||||
|
var asm = labelGenerator.getDebugFunc(func.pos, func.name.name, funcLabel)
|
||||||
|
val debugFunc = asm.size > 0
|
||||||
|
asm += funcLabel
|
||||||
asm ++= funcPrologue()
|
asm ++= funcPrologue()
|
||||||
// Push the rest of params onto the stack for simplicity
|
// Push the rest of params onto the stack for simplicity
|
||||||
argRegs.zip(func.params).foreach { (reg, param) =>
|
argRegs.zip(func.params).foreach { (reg, param) =>
|
||||||
@@ -83,6 +90,10 @@ object asmGenerator {
|
|||||||
}
|
}
|
||||||
asm ++= func.body.foldMap(generateStmt(_))
|
asm ++= func.body.foldMap(generateStmt(_))
|
||||||
// No need for epilogue here since all user functions must return explicitly
|
// No need for epilogue here since all user functions must return explicitly
|
||||||
|
if (debugFunc) {
|
||||||
|
asm += Directive.Size(funcLabel, SizeExpr.Relative(funcLabel))
|
||||||
|
asm += Directive.EndFunc
|
||||||
|
}
|
||||||
asm
|
asm
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +170,8 @@ object asmGenerator {
|
|||||||
stack: Stack,
|
stack: Stack,
|
||||||
labelGenerator: LabelGenerator
|
labelGenerator: LabelGenerator
|
||||||
): Chain[AsmLine] = {
|
): Chain[AsmLine] = {
|
||||||
var asm = Chain.empty[AsmLine]
|
val fileNo = labelGenerator.getDebugFile(stmt.pos.file)
|
||||||
asm += Comment(stmt.toString)
|
var asm = Chain.one[AsmLine](Directive.Location(fileNo, stmt.pos.line, None))
|
||||||
stmt match {
|
stmt match {
|
||||||
case Assign(lhs, rhs) =>
|
case Assign(lhs, rhs) =>
|
||||||
lhs match {
|
lhs match {
|
||||||
@@ -261,7 +272,7 @@ object asmGenerator {
|
|||||||
asm += stack.push(KnownType.String.size, RAX)
|
asm += stack.push(KnownType.String.size, RAX)
|
||||||
case ty =>
|
case ty =>
|
||||||
asm ++= generateCall(
|
asm ++= generateCall(
|
||||||
microWacc.Call(Builtin.Malloc, List(IntLiter(array.heapSize))),
|
microWacc.Call(Builtin.Malloc, List(IntLiter(array.heapSize)))(array.pos),
|
||||||
isTail = false
|
isTail = false
|
||||||
)
|
)
|
||||||
asm += stack.push(KnownType.Array(?).size, RAX)
|
asm += stack.push(KnownType.Array(?).size, RAX)
|
||||||
|
|||||||
@@ -199,18 +199,49 @@ object assemblyIR {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum Directive extends AsmLine {
|
enum Directive extends AsmLine {
|
||||||
case IntelSyntax, RoData, Text
|
case IntelSyntax, RoData, Text, EndFunc
|
||||||
case Global(name: String)
|
case Global(name: String)
|
||||||
case Int(value: scala.Int)
|
case Int(value: scala.Int)
|
||||||
case Asciz(string: String)
|
case Asciz(string: String)
|
||||||
|
case File(no: scala.Int, file: String)
|
||||||
|
case Location(fileNo: scala.Int, lineNo: scala.Int, colNo: Option[scala.Int])
|
||||||
|
case Func(name: String, label: LabelDef)
|
||||||
|
case Type(label: LabelDef, symbolType: SymbolType)
|
||||||
|
case Size(label: LabelDef, expr: SizeExpr)
|
||||||
|
|
||||||
override def toString(): String = this match {
|
override def toString(): String = this match {
|
||||||
case IntelSyntax => ".intel_syntax noprefix"
|
case IntelSyntax => ".intel_syntax noprefix"
|
||||||
case Global(name) => s".globl $name"
|
case Global(name) => s".globl $name"
|
||||||
case Text => ".text"
|
case Text => ".text"
|
||||||
case RoData => ".section .rodata"
|
case RoData => ".section .rodata"
|
||||||
case Int(value) => s"\t.int $value"
|
case Int(value) => s"\t.int $value"
|
||||||
case Asciz(string) => s"\t.asciz \"$string\""
|
case Asciz(string) => s"\t.asciz \"$string\""
|
||||||
|
case File(no, file) => s".file $no \"${file}\""
|
||||||
|
case Location(fileNo, lineNo, colNo) =>
|
||||||
|
s"\t.loc $fileNo $lineNo" + colNo.map(c => s" $c").getOrElse("")
|
||||||
|
case Func(name, label) =>
|
||||||
|
s".func $name, ${label.name}"
|
||||||
|
case EndFunc => ".endfunc"
|
||||||
|
case Type(label, symbolType) =>
|
||||||
|
s".type ${label.name}, @${symbolType.toString}"
|
||||||
|
case Directive.Size(label, expr) =>
|
||||||
|
s".size ${label.name}, ${expr.toString}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SymbolType {
|
||||||
|
case Function
|
||||||
|
|
||||||
|
override def toString(): String = this match {
|
||||||
|
case Function => "function"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SizeExpr {
|
||||||
|
case Relative(label: LabelDef)
|
||||||
|
|
||||||
|
override def toString(): String = this match {
|
||||||
|
case Relative(label) => s".-${label.name}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -223,7 +223,9 @@ object ast {
|
|||||||
val pos: Position
|
val pos: Position
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait RValue
|
sealed trait RValue {
|
||||||
|
val pos: Position
|
||||||
|
}
|
||||||
case class ArrayLiter(elems: List[Expr])(val pos: Position) extends RValue
|
case class ArrayLiter(elems: List[Expr])(val pos: Position) extends RValue
|
||||||
object ArrayLiter extends ParserBridgePos1[List[Expr], ArrayLiter]
|
object ArrayLiter extends ParserBridgePos1[List[Expr], ArrayLiter]
|
||||||
case class NewPair(fst: Expr, snd: Expr)(val pos: Position) extends RValue
|
case class NewPair(fst: Expr, snd: Expr)(val pos: Position) extends RValue
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package wacc
|
|||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
|
|
||||||
object microWacc {
|
object microWacc {
|
||||||
|
import wacc.ast.Position
|
||||||
import wacc.types._
|
import wacc.types._
|
||||||
|
|
||||||
sealed trait CallTarget(val retTy: SemType)
|
sealed trait CallTarget(val retTy: SemType)
|
||||||
@@ -13,7 +14,7 @@ object microWacc {
|
|||||||
case class IntLiter(v: Int) extends Expr(KnownType.Int)
|
case class IntLiter(v: Int) extends Expr(KnownType.Int)
|
||||||
case class BoolLiter(v: Boolean) extends Expr(KnownType.Bool)
|
case class BoolLiter(v: Boolean) extends Expr(KnownType.Bool)
|
||||||
case class CharLiter(v: Char) extends Expr(KnownType.Char)
|
case class CharLiter(v: Char) extends Expr(KnownType.Char)
|
||||||
case class ArrayLiter(elems: List[Expr])(ty: SemType) extends Expr(ty)
|
case class ArrayLiter(elems: List[Expr])(ty: SemType, val pos: Position) extends Expr(ty)
|
||||||
case class NullLiter()(ty: SemType) extends Expr(ty)
|
case class NullLiter()(ty: SemType) extends Expr(ty)
|
||||||
case class Ident(name: String, uid: Int)(identTy: SemType)
|
case class Ident(name: String, uid: Int)(identTy: SemType)
|
||||||
extends Expr(identTy)
|
extends Expr(identTy)
|
||||||
@@ -65,7 +66,9 @@ object microWacc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Statements
|
// Statements
|
||||||
sealed trait Stmt
|
sealed trait Stmt {
|
||||||
|
val pos: Position
|
||||||
|
}
|
||||||
|
|
||||||
case class Builtin(val name: String)(retTy: SemType) extends CallTarget(retTy) {
|
case class Builtin(val name: String)(retTy: SemType) extends CallTarget(retTy) {
|
||||||
override def toString(): String = name
|
override def toString(): String = name
|
||||||
@@ -79,13 +82,16 @@ object microWacc {
|
|||||||
object PrintCharArray extends Builtin("printCharArray")(?)
|
object PrintCharArray extends Builtin("printCharArray")(?)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class Assign(lhs: LValue, rhs: Expr) extends Stmt
|
case class Assign(lhs: LValue, rhs: Expr)(val pos: Position) extends Stmt
|
||||||
case class If(cond: Expr, thenBranch: Chain[Stmt], elseBranch: Chain[Stmt]) extends Stmt
|
case class If(cond: Expr, thenBranch: Chain[Stmt], elseBranch: Chain[Stmt])(val pos: Position)
|
||||||
case class While(cond: Expr, body: Chain[Stmt]) extends Stmt
|
extends Stmt
|
||||||
case class Call(target: CallTarget, args: List[Expr]) extends Stmt with Expr(target.retTy)
|
case class While(cond: Expr, body: Chain[Stmt])(val pos: Position) extends Stmt
|
||||||
case class Return(expr: Expr) extends Stmt
|
case class Call(target: CallTarget, args: List[Expr])(val pos: Position)
|
||||||
|
extends Stmt
|
||||||
|
with Expr(target.retTy)
|
||||||
|
case class Return(expr: Expr)(val pos: Position) extends Stmt
|
||||||
|
|
||||||
// Program
|
// Program
|
||||||
case class FuncDecl(name: Ident, params: List[Ident], body: Chain[Stmt])
|
case class FuncDecl(name: Ident, params: List[Ident], body: Chain[Stmt])(val pos: Position)
|
||||||
case class Program(funcs: Chain[FuncDecl], stmts: Chain[Stmt])
|
case class Program(funcs: Chain[FuncDecl], stmts: Chain[Stmt])(val pos: Position)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ object renamer {
|
|||||||
(chunks :+ func, errors ++ scope.add(name, public = true))
|
(chunks :+ func, errors ++ scope.add(name, public = true))
|
||||||
}
|
}
|
||||||
// ...and main body.
|
// ...and main body.
|
||||||
val mainBodyIdent = Ident(MAIN, ty = FuncType(?, Nil))(prog.pos)
|
val mainBodyIdent = Ident(MAIN, ty = FuncType(?, Nil))(main.head.pos)
|
||||||
val mainBodyErrors = scope.add(mainBodyIdent, public = false)
|
val mainBodyErrors = scope.add(mainBodyIdent, public = false)
|
||||||
val mainBodyChunk = FuncDecl(IntType()(prog.pos), mainBodyIdent, Nil, main)(prog.pos)
|
val mainBodyChunk = FuncDecl(IntType()(prog.pos), mainBodyIdent, Nil, main)(prog.pos)
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ object semantics {
|
|||||||
case Some((head, tail)) => (head.body, tail)
|
case Some((head, tail)) => (head.body, tail)
|
||||||
case None => (Chain.empty, Chain.empty)
|
case None => (Chain.empty, Chain.empty)
|
||||||
}
|
}
|
||||||
} yield (microWacc.Program(funcs, typedMain), globalErrors ++ errors)
|
} yield (microWacc.Program(funcs, typedMain)(main.pos), globalErrors ++ errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ object typeChecker {
|
|||||||
microWacc.Ident(ident.v, ident.guid)(ty)
|
microWacc.Ident(ident.v, ident.guid)(ty)
|
||||||
},
|
},
|
||||||
body
|
body
|
||||||
),
|
)(func.pos),
|
||||||
bodyErrors
|
bodyErrors
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ object typeChecker {
|
|||||||
microWacc.Assign(
|
microWacc.Assign(
|
||||||
microWacc.Ident(name.v, name.guid)(expectedTy.asInstanceOf[SemType]),
|
microWacc.Ident(name.v, name.guid)(expectedTy.asInstanceOf[SemType]),
|
||||||
typedValue
|
typedValue
|
||||||
)
|
)(stmt.pos)
|
||||||
),
|
),
|
||||||
valueErrors
|
valueErrors
|
||||||
)
|
)
|
||||||
@@ -141,7 +141,10 @@ object typeChecker {
|
|||||||
)
|
)
|
||||||
case _ => Chain.empty
|
case _ => Chain.empty
|
||||||
}
|
}
|
||||||
(Chain.one(microWacc.Assign(lhsTyped, rhsTyped)), lhsErrors ++ rhsErrors ++ unknownError)
|
(
|
||||||
|
Chain.one(microWacc.Assign(lhsTyped, rhsTyped)(stmt.pos)),
|
||||||
|
lhsErrors ++ rhsErrors ++ unknownError
|
||||||
|
)
|
||||||
case ast.Read(dest) =>
|
case ast.Read(dest) =>
|
||||||
val (destTyped, destErrors) = checkLValue(dest, Constraint.Unconstrained)
|
val (destTyped, destErrors) = checkLValue(dest, Constraint.Unconstrained)
|
||||||
val (destTy, destTyErrors) = destTyped.ty match {
|
val (destTy, destTyErrors) = destTyped.ty match {
|
||||||
@@ -170,13 +173,13 @@ object typeChecker {
|
|||||||
microWacc.Builtin.Read,
|
microWacc.Builtin.Read,
|
||||||
List(
|
List(
|
||||||
destTy match {
|
destTy match {
|
||||||
case KnownType.Int => " %d".toMicroWaccCharArray
|
case KnownType.Int => " %d".toMicroWaccCharArray(stmt.pos)
|
||||||
case KnownType.Char | _ => " %c".toMicroWaccCharArray
|
case KnownType.Char | _ => " %c".toMicroWaccCharArray(stmt.pos)
|
||||||
},
|
},
|
||||||
destTyped
|
destTyped
|
||||||
)
|
)
|
||||||
)
|
)(dest.pos)
|
||||||
)
|
)(stmt.pos)
|
||||||
),
|
),
|
||||||
destErrors ++ destTyErrors
|
destErrors ++ destTyErrors
|
||||||
)
|
)
|
||||||
@@ -189,14 +192,14 @@ object typeChecker {
|
|||||||
"free must be applied to an array or pair"
|
"free must be applied to an array or pair"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
(Chain.one(microWacc.Call(microWacc.Builtin.Free, List(lhsTyped))), lhsErrors)
|
(Chain.one(microWacc.Call(microWacc.Builtin.Free, List(lhsTyped))(stmt.pos)), lhsErrors)
|
||||||
case ast.Return(expr) =>
|
case ast.Return(expr) =>
|
||||||
val (exprTyped, exprErrors) = checkValue(expr, returnConstraint)
|
val (exprTyped, exprErrors) = checkValue(expr, returnConstraint)
|
||||||
(Chain.one(microWacc.Return(exprTyped)), exprErrors)
|
(Chain.one(microWacc.Return(exprTyped)(stmt.pos)), exprErrors)
|
||||||
case ast.Exit(expr) =>
|
case ast.Exit(expr) =>
|
||||||
val (exprTyped, exprErrors) =
|
val (exprTyped, exprErrors) =
|
||||||
checkValue(expr, Constraint.Is(KnownType.Int, "exit value must be int"))
|
checkValue(expr, Constraint.Is(KnownType.Int, "exit value must be int"))
|
||||||
(Chain.one(microWacc.Call(microWacc.Builtin.Exit, List(exprTyped))), exprErrors)
|
(Chain.one(microWacc.Call(microWacc.Builtin.Exit, List(exprTyped))(stmt.pos)), exprErrors)
|
||||||
case ast.Print(expr, newline) =>
|
case ast.Print(expr, newline) =>
|
||||||
// This constraint should never fail, the scope-checker should have caught it already
|
// This constraint should never fail, the scope-checker should have caught it already
|
||||||
val (exprTyped, exprErrors) = checkValue(expr, Constraint.Unconstrained)
|
val (exprTyped, exprErrors) = checkValue(expr, Constraint.Unconstrained)
|
||||||
@@ -212,10 +215,10 @@ object typeChecker {
|
|||||||
microWacc.Call(
|
microWacc.Call(
|
||||||
func,
|
func,
|
||||||
List(
|
List(
|
||||||
s"$exprFormat${if newline then "\n" else ""}".toMicroWaccCharArray,
|
s"$exprFormat${if newline then "\n" else ""}".toMicroWaccCharArray(stmt.pos),
|
||||||
value
|
value
|
||||||
)
|
)
|
||||||
)
|
)(stmt.pos)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
@@ -224,9 +227,9 @@ object typeChecker {
|
|||||||
Chain.one(
|
Chain.one(
|
||||||
microWacc.If(
|
microWacc.If(
|
||||||
exprTyped,
|
exprTyped,
|
||||||
printfCall(microWacc.Builtin.Printf, "true".toMicroWaccCharArray),
|
printfCall(microWacc.Builtin.Printf, "true".toMicroWaccCharArray(stmt.pos)),
|
||||||
printfCall(microWacc.Builtin.Printf, "false".toMicroWaccCharArray)
|
printfCall(microWacc.Builtin.Printf, "false".toMicroWaccCharArray(stmt.pos))
|
||||||
)
|
)(stmt.pos)
|
||||||
)
|
)
|
||||||
case KnownType.Array(KnownType.Char) =>
|
case KnownType.Array(KnownType.Char) =>
|
||||||
printfCall(microWacc.Builtin.PrintCharArray, exprTyped)
|
printfCall(microWacc.Builtin.PrintCharArray, exprTyped)
|
||||||
@@ -240,14 +243,14 @@ object typeChecker {
|
|||||||
val (thenStmtTyped, thenErrors) = thenStmt.foldMap(checkStmt(_, returnConstraint))
|
val (thenStmtTyped, thenErrors) = thenStmt.foldMap(checkStmt(_, returnConstraint))
|
||||||
val (elseStmtTyped, elseErrors) = elseStmt.foldMap(checkStmt(_, returnConstraint))
|
val (elseStmtTyped, elseErrors) = elseStmt.foldMap(checkStmt(_, returnConstraint))
|
||||||
(
|
(
|
||||||
Chain.one(microWacc.If(condTyped, thenStmtTyped, elseStmtTyped)),
|
Chain.one(microWacc.If(condTyped, thenStmtTyped, elseStmtTyped)(cond.pos)),
|
||||||
condErrors ++ thenErrors ++ elseErrors
|
condErrors ++ thenErrors ++ elseErrors
|
||||||
)
|
)
|
||||||
case ast.While(cond, body) =>
|
case ast.While(cond, body) =>
|
||||||
val (condTyped, condErrors) =
|
val (condTyped, condErrors) =
|
||||||
checkValue(cond, Constraint.Is(KnownType.Bool, "while condition must be a bool"))
|
checkValue(cond, Constraint.Is(KnownType.Bool, "while condition must be a bool"))
|
||||||
val (bodyTyped, bodyErrors) = body.foldMap(checkStmt(_, returnConstraint))
|
val (bodyTyped, bodyErrors) = body.foldMap(checkStmt(_, returnConstraint))
|
||||||
(Chain.one(microWacc.While(condTyped, bodyTyped)), condErrors ++ bodyErrors)
|
(Chain.one(microWacc.While(condTyped, bodyTyped)(cond.pos)), condErrors ++ bodyErrors)
|
||||||
case ast.Block(body) => body.foldMap(checkStmt(_, returnConstraint))
|
case ast.Block(body) => body.foldMap(checkStmt(_, returnConstraint))
|
||||||
case skip @ ast.Skip() => (Chain.empty, Chain.empty)
|
case skip @ ast.Skip() => (Chain.empty, Chain.empty)
|
||||||
}
|
}
|
||||||
@@ -277,7 +280,7 @@ object typeChecker {
|
|||||||
(microWacc.CharLiter(v), errors)
|
(microWacc.CharLiter(v), errors)
|
||||||
case l @ ast.StrLiter(v) =>
|
case l @ ast.StrLiter(v) =>
|
||||||
val (_, errors) = KnownType.String.satisfies(constraint, l.pos)
|
val (_, errors) = KnownType.String.satisfies(constraint, l.pos)
|
||||||
(v.toMicroWaccCharArray, errors)
|
(v.toMicroWaccCharArray(l.pos), errors)
|
||||||
case l @ ast.PairLiter() =>
|
case l @ ast.PairLiter() =>
|
||||||
val (ty, errors) = KnownType.Pair(?, ?).satisfies(constraint, l.pos)
|
val (ty, errors) = KnownType.Pair(?, ?).satisfies(constraint, l.pos)
|
||||||
(microWacc.NullLiter()(ty), errors)
|
(microWacc.NullLiter()(ty), errors)
|
||||||
@@ -296,13 +299,16 @@ object typeChecker {
|
|||||||
// Start with an unknown param type, make it more specific while checking the elements.
|
// Start with an unknown param type, make it more specific while checking the elements.
|
||||||
.Array(elemTy)
|
.Array(elemTy)
|
||||||
.satisfies(constraint, l.pos)
|
.satisfies(constraint, l.pos)
|
||||||
(microWacc.ArrayLiter(elemsTyped)(arrayTy), elemsErrors ++ arrayErrors)
|
(microWacc.ArrayLiter(elemsTyped)(arrayTy, l.pos), elemsErrors ++ arrayErrors)
|
||||||
case l @ ast.NewPair(fst, snd) =>
|
case l @ ast.NewPair(fst, snd) =>
|
||||||
val (fstTyped, fstErrors) = checkValue(fst, Constraint.Unconstrained)
|
val (fstTyped, fstErrors) = checkValue(fst, Constraint.Unconstrained)
|
||||||
val (sndTyped, sndErrors) = checkValue(snd, Constraint.Unconstrained)
|
val (sndTyped, sndErrors) = checkValue(snd, Constraint.Unconstrained)
|
||||||
val (pairTy, pairErrors) =
|
val (pairTy, pairErrors) =
|
||||||
KnownType.Pair(fstTyped.ty, sndTyped.ty).satisfies(constraint, l.pos)
|
KnownType.Pair(fstTyped.ty, sndTyped.ty).satisfies(constraint, l.pos)
|
||||||
(microWacc.ArrayLiter(List(fstTyped, sndTyped))(pairTy), fstErrors ++ sndErrors ++ pairErrors)
|
(
|
||||||
|
microWacc.ArrayLiter(List(fstTyped, sndTyped))(pairTy, l.pos),
|
||||||
|
fstErrors ++ sndErrors ++ pairErrors
|
||||||
|
)
|
||||||
case ast.Call(id, args) =>
|
case ast.Call(id, args) =>
|
||||||
val funcTy @ FuncType(retTy, paramTys) = id.ty.asInstanceOf[FuncType]
|
val funcTy @ FuncType(retTy, paramTys) = id.ty.asInstanceOf[FuncType]
|
||||||
val lenError =
|
val lenError =
|
||||||
@@ -318,7 +324,7 @@ object typeChecker {
|
|||||||
}
|
}
|
||||||
val (retTyChecked, retErrors) = retTy.satisfies(constraint, id.pos)
|
val (retTyChecked, retErrors) = retTy.satisfies(constraint, id.pos)
|
||||||
(
|
(
|
||||||
microWacc.Call(microWacc.Ident(id.v, id.guid)(retTyChecked), argsTyped),
|
microWacc.Call(microWacc.Ident(id.v, id.guid)(retTyChecked), argsTyped)(id.pos),
|
||||||
lenError ++ argsErrors ++ retErrors
|
lenError ++ argsErrors ++ retErrors
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -480,7 +486,7 @@ object typeChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension (s: String) {
|
extension (s: String) {
|
||||||
def toMicroWaccCharArray: microWacc.ArrayLiter =
|
def toMicroWaccCharArray(pos: ast.Position): microWacc.ArrayLiter =
|
||||||
microWacc.ArrayLiter(s.map(microWacc.CharLiter(_)).toList)(KnownType.String)
|
microWacc.ArrayLiter(s.map(microWacc.CharLiter(_)).toList)(KnownType.String, pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user