Scaladocs
Merge request lab2425_spring/WACC_37!18 Co-authored-by: Guy C <gc1523@ic.ac.uk> Co-authored-by: Jonny <j.sinteix@gmail.com>
This commit is contained in:
commit
2ff7fff7eb
@ -3,6 +3,8 @@ package wacc
|
|||||||
import wacc.ast.Position
|
import wacc.ast.Position
|
||||||
import wacc.types._
|
import wacc.types._
|
||||||
|
|
||||||
|
/** Error types for semantic errors
|
||||||
|
*/
|
||||||
enum Error {
|
enum Error {
|
||||||
case DuplicateDeclaration(ident: ast.Ident)
|
case DuplicateDeclaration(ident: ast.Ident)
|
||||||
case UndeclaredVariable(ident: ast.Ident)
|
case UndeclaredVariable(ident: ast.Ident)
|
||||||
@ -14,6 +16,13 @@ enum Error {
|
|||||||
case InternalError(pos: Position, msg: String)
|
case InternalError(pos: Position, msg: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Function to handle printing the details of a given semantic error
|
||||||
|
*
|
||||||
|
* @param error
|
||||||
|
* Error object
|
||||||
|
* @param errorContent
|
||||||
|
* Contents of the file to generate code snippets
|
||||||
|
*/
|
||||||
def printError(error: Error)(using errorContent: String): Unit = {
|
def printError(error: Error)(using errorContent: String): Unit = {
|
||||||
println("Semantic error:")
|
println("Semantic error:")
|
||||||
error match {
|
error match {
|
||||||
@ -49,6 +58,15 @@ def printError(error: Error)(using errorContent: String): Unit = {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Function to highlight a section of code for an error message
|
||||||
|
*
|
||||||
|
* @param pos
|
||||||
|
* Position of the error
|
||||||
|
* @param size
|
||||||
|
* Size(in chars) of section to highlight
|
||||||
|
* @param errorContent
|
||||||
|
* Contents of the file to generate code snippets
|
||||||
|
*/
|
||||||
def highlight(pos: Position, size: Int)(using errorContent: String): Unit = {
|
def highlight(pos: Position, size: Int)(using errorContent: String): Unit = {
|
||||||
val lines = errorContent.split("\n")
|
val lines = errorContent.split("\n")
|
||||||
|
|
||||||
@ -62,6 +80,11 @@ def highlight(pos: Position, size: Int)(using errorContent: String): Unit = {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Function to print the position of an error
|
||||||
|
*
|
||||||
|
* @param pos
|
||||||
|
* Position of the error
|
||||||
|
*/
|
||||||
def printPosition(pos: Position): Unit = {
|
def printPosition(pos: Position): Unit = {
|
||||||
println(s"(line ${pos.line}, column ${pos.column}):")
|
println(s"(line ${pos.line}, column ${pos.column}):")
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,8 @@ import parsley.syntax.zipped._
|
|||||||
import cats.data.NonEmptyList
|
import cats.data.NonEmptyList
|
||||||
|
|
||||||
object ast {
|
object ast {
|
||||||
// Expressions
|
/* ============================ EXPRESSIONS ============================ */
|
||||||
|
|
||||||
sealed trait Expr extends RValue {
|
sealed trait Expr extends RValue {
|
||||||
val pos: Position
|
val pos: Position
|
||||||
}
|
}
|
||||||
@ -19,7 +20,8 @@ object ast {
|
|||||||
sealed trait Expr5 extends Expr4
|
sealed trait Expr5 extends Expr4
|
||||||
sealed trait Expr6 extends Expr5
|
sealed trait Expr6 extends Expr5
|
||||||
|
|
||||||
// Atoms
|
/* ============================ ATOMIC EXPRESSIONS ============================ */
|
||||||
|
|
||||||
case class IntLiter(v: Int)(val pos: Position) extends Expr6
|
case class IntLiter(v: Int)(val pos: Position) extends Expr6
|
||||||
object IntLiter extends ParserBridgePos1[Int, IntLiter]
|
object IntLiter extends ParserBridgePos1[Int, IntLiter]
|
||||||
case class BoolLiter(v: Boolean)(val pos: Position) extends Expr6
|
case class BoolLiter(v: Boolean)(val pos: Position) extends Expr6
|
||||||
@ -44,7 +46,8 @@ object ast {
|
|||||||
case class Parens(expr: Expr)(val pos: Position) extends Expr6
|
case class Parens(expr: Expr)(val pos: Position) extends Expr6
|
||||||
object Parens extends ParserBridgePos1[Expr, Parens]
|
object Parens extends ParserBridgePos1[Expr, Parens]
|
||||||
|
|
||||||
// Unary operators
|
/* ============================ UNARY OPERATORS ============================ */
|
||||||
|
|
||||||
sealed trait UnaryOp extends Expr {
|
sealed trait UnaryOp extends Expr {
|
||||||
val x: Expr
|
val x: Expr
|
||||||
}
|
}
|
||||||
@ -59,7 +62,8 @@ object ast {
|
|||||||
case class Chr(x: Expr6)(val pos: Position) extends Expr6 with UnaryOp
|
case class Chr(x: Expr6)(val pos: Position) extends Expr6 with UnaryOp
|
||||||
object Chr extends ParserBridgePos1[Expr6, Chr]
|
object Chr extends ParserBridgePos1[Expr6, Chr]
|
||||||
|
|
||||||
// Binary operators
|
/* ============================ BINARY OPERATORS ============================ */
|
||||||
|
|
||||||
sealed trait BinaryOp(val name: String) extends Expr {
|
sealed trait BinaryOp(val name: String) extends Expr {
|
||||||
val x: Expr
|
val x: Expr
|
||||||
val y: Expr
|
val y: Expr
|
||||||
@ -101,7 +105,8 @@ object ast {
|
|||||||
case class Or(x: Expr1, y: Expr)(val pos: Position) extends Expr with BinaryOp("logical or")
|
case class Or(x: Expr1, y: Expr)(val pos: Position) extends Expr with BinaryOp("logical or")
|
||||||
object Or extends ParserBridgePos2[Expr1, Expr, Or]
|
object Or extends ParserBridgePos2[Expr1, Expr, Or]
|
||||||
|
|
||||||
// Types
|
/* ============================ TYPES ============================ */
|
||||||
|
|
||||||
sealed trait Type
|
sealed trait Type
|
||||||
sealed trait BaseType extends Type with PairElemType
|
sealed trait BaseType extends Type with PairElemType
|
||||||
case class IntType()(val pos: Position) extends BaseType
|
case class IntType()(val pos: Position) extends BaseType
|
||||||
@ -125,11 +130,13 @@ object ast {
|
|||||||
case class UntypedPairType()(val pos: Position) extends PairElemType
|
case class UntypedPairType()(val pos: Position) extends PairElemType
|
||||||
object UntypedPairType extends ParserBridgePos0[UntypedPairType]
|
object UntypedPairType extends ParserBridgePos0[UntypedPairType]
|
||||||
|
|
||||||
// waccadoodledo
|
/* ============================ PROGRAM STRUCTURE ============================ */
|
||||||
|
|
||||||
case class Program(funcs: List[FuncDecl], main: NonEmptyList[Stmt])(val pos: Position)
|
case class Program(funcs: List[FuncDecl], main: NonEmptyList[Stmt])(val pos: Position)
|
||||||
object Program extends ParserBridgePos2[List[FuncDecl], NonEmptyList[Stmt], Program]
|
object Program extends ParserBridgePos2[List[FuncDecl], NonEmptyList[Stmt], Program]
|
||||||
|
|
||||||
// Function Definitions
|
/* ============================ FUNCTION STRUCTURE ============================ */
|
||||||
|
|
||||||
case class FuncDecl(
|
case class FuncDecl(
|
||||||
returnType: Type,
|
returnType: Type,
|
||||||
name: Ident,
|
name: Ident,
|
||||||
@ -151,7 +158,8 @@ object ast {
|
|||||||
case class Param(paramType: Type, name: Ident)(val pos: Position)
|
case class Param(paramType: Type, name: Ident)(val pos: Position)
|
||||||
object Param extends ParserBridgePos2[Type, Ident, Param]
|
object Param extends ParserBridgePos2[Type, Ident, Param]
|
||||||
|
|
||||||
// Statements
|
/* ============================ STATEMENTS ============================ */
|
||||||
|
|
||||||
sealed trait Stmt
|
sealed trait Stmt
|
||||||
case class Skip()(val pos: Position) extends Stmt
|
case class Skip()(val pos: Position) extends Stmt
|
||||||
object Skip extends ParserBridgePos0[Skip]
|
object Skip extends ParserBridgePos0[Skip]
|
||||||
@ -178,6 +186,8 @@ object ast {
|
|||||||
case class Block(stmt: NonEmptyList[Stmt])(val pos: Position) extends Stmt
|
case class Block(stmt: NonEmptyList[Stmt])(val pos: Position) extends Stmt
|
||||||
object Block extends ParserBridgePos1[NonEmptyList[Stmt], Block]
|
object Block extends ParserBridgePos1[NonEmptyList[Stmt], Block]
|
||||||
|
|
||||||
|
/* ============================ LVALUES & RVALUES ============================ */
|
||||||
|
|
||||||
sealed trait LValue {
|
sealed trait LValue {
|
||||||
val pos: Position
|
val pos: Position
|
||||||
}
|
}
|
||||||
@ -196,7 +206,8 @@ object ast {
|
|||||||
case class Snd(elem: LValue)(val pos: Position) extends PairElem
|
case class Snd(elem: LValue)(val pos: Position) extends PairElem
|
||||||
object Snd extends ParserBridgePos1[LValue, Snd]
|
object Snd extends ParserBridgePos1[LValue, Snd]
|
||||||
|
|
||||||
// Parser bridges
|
/* ============================ PARSER BRIDGES ============================ */
|
||||||
|
|
||||||
case class Position(line: Int, column: Int)
|
case class Position(line: Int, column: Int)
|
||||||
|
|
||||||
trait ParserSingletonBridgePos[+A] extends ErrorBridge {
|
trait ParserSingletonBridgePos[+A] extends ErrorBridge {
|
||||||
|
@ -6,6 +6,8 @@ import parsley.token.{Basic, Lexer}
|
|||||||
import parsley.token.descriptions.*
|
import parsley.token.descriptions.*
|
||||||
import parsley.token.errors._
|
import parsley.token.errors._
|
||||||
|
|
||||||
|
/** ErrorConfig for producing more informative error messages
|
||||||
|
*/
|
||||||
val errConfig = new ErrorConfig {
|
val errConfig = new ErrorConfig {
|
||||||
override def labelSymbol = Map(
|
override def labelSymbol = Map(
|
||||||
"!=" -> Label("binary operator"),
|
"!=" -> Label("binary operator"),
|
||||||
@ -37,6 +39,9 @@ val errConfig = new ErrorConfig {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
object lexer {
|
object lexer {
|
||||||
|
|
||||||
|
/** Language description for the WACC lexer
|
||||||
|
*/
|
||||||
private val desc = LexicalDesc.plain.copy(
|
private val desc = LexicalDesc.plain.copy(
|
||||||
nameDesc = NameDesc.plain.copy(
|
nameDesc = NameDesc.plain.copy(
|
||||||
identifierStart = Basic(c => c.isLetter || c == '_'),
|
identifierStart = Basic(c => c.isLetter || c == '_'),
|
||||||
@ -74,6 +79,8 @@ object lexer {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** Token definitions for the WACC lexer
|
||||||
|
*/
|
||||||
private val lexer = Lexer(desc, errConfig)
|
private val lexer = Lexer(desc, errConfig)
|
||||||
val ident = lexer.lexeme.names.identifier
|
val ident = lexer.lexeme.names.identifier
|
||||||
val integer = lexer.lexeme.integer.decimal32[Int]
|
val integer = lexer.lexeme.integer.decimal32[Int]
|
||||||
@ -82,6 +89,8 @@ object lexer {
|
|||||||
val stringLit = lexer.lexeme.string.ascii
|
val stringLit = lexer.lexeme.string.ascii
|
||||||
val implicits = lexer.lexeme.symbol.implicits
|
val implicits = lexer.lexeme.symbol.implicits
|
||||||
|
|
||||||
|
/** Tokens for producing lexer-backed error messages
|
||||||
|
*/
|
||||||
val errTokens = Seq(
|
val errTokens = Seq(
|
||||||
lexer.nonlexeme.names.identifier.map(v => s"identifier $v"),
|
lexer.nonlexeme.names.identifier.map(v => s"identifier $v"),
|
||||||
lexer.nonlexeme.integer.decimal32[Int].map(n => s"integer $n"),
|
lexer.nonlexeme.integer.decimal32[Int].map(n => s"integer $n"),
|
||||||
|
@ -77,6 +77,7 @@ object parser {
|
|||||||
SOps(InfixL)(Mul from "*", Div from "/", Mod from "%") +:
|
SOps(InfixL)(Mul from "*", Div from "/", Mod from "%") +:
|
||||||
SOps(Prefix)(
|
SOps(Prefix)(
|
||||||
Not from "!",
|
Not from "!",
|
||||||
|
// notFollowedBy(negateCheck) ensures that negative numbers are parsed as a single int literal
|
||||||
(Negate from (notFollowedBy(negateCheck) ~> "-")).hide,
|
(Negate from (notFollowedBy(negateCheck) ~> "-")).hide,
|
||||||
Len from "len",
|
Len from "len",
|
||||||
Ord from "ord",
|
Ord from "ord",
|
||||||
@ -119,7 +120,15 @@ object parser {
|
|||||||
((`<pair-elems-type>` <**> `<array-type>`)
|
((`<pair-elems-type>` <**> `<array-type>`)
|
||||||
.map(arr => (_: UntypedPairType) => arr) </> identity))
|
.map(arr => (_: UntypedPairType) => arr) </> identity))
|
||||||
|
|
||||||
// Statements
|
/* Statements
|
||||||
|
Atomic is used in two places here:
|
||||||
|
1. Atomic for function return type - code may be a variable declaration instead, If we were
|
||||||
|
to factor out the type, the resulting code would be rather messy. It can only fail once
|
||||||
|
in the entire program so it creates minimal overhead.
|
||||||
|
2. Atomic for function missing return type check - there is no easy way around an explicit
|
||||||
|
invalid syntax check, this only happens at most once per program so this is not a major
|
||||||
|
concern.
|
||||||
|
*/
|
||||||
private lazy val `<program>` = Program(
|
private lazy val `<program>` = Program(
|
||||||
"begin" ~> (
|
"begin" ~> (
|
||||||
many(
|
many(
|
||||||
@ -192,6 +201,13 @@ object parser {
|
|||||||
)
|
)
|
||||||
|
|
||||||
extension (stmts: NonEmptyList[Stmt]) {
|
extension (stmts: NonEmptyList[Stmt]) {
|
||||||
|
|
||||||
|
/** Determines whether a function body is guaranteed to return in all cases This is required as
|
||||||
|
* all functions must end via a "return" or "exit" statement
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* true if the statement list ends in a return statement, false otherwise
|
||||||
|
*/
|
||||||
def isReturning: Boolean = stmts.last match {
|
def isReturning: Boolean = stmts.last match {
|
||||||
case Return(_) | Exit(_) => true
|
case Return(_) | Exit(_) => true
|
||||||
case If(_, thenStmt, elseStmt) => thenStmt.isReturning && elseStmt.isReturning
|
case If(_, thenStmt, elseStmt) => thenStmt.isReturning && elseStmt.isReturning
|
||||||
|
Loading…
x
Reference in New Issue
Block a user