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:
		| @@ -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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user