From c6b57be2c37a2801f35e81ebb304c92fc5ea64ce Mon Sep 17 00:00:00 2001 From: Guy C Date: Fri, 7 Feb 2025 15:37:33 +0000 Subject: [PATCH 1/7] refactor: scaladocs-style commenting added in lexer --- src/main/wacc/lexer.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/wacc/lexer.scala b/src/main/wacc/lexer.scala index c6e2781..2efe517 100644 --- a/src/main/wacc/lexer.scala +++ b/src/main/wacc/lexer.scala @@ -6,6 +6,8 @@ import parsley.token.{Basic, Lexer} import parsley.token.descriptions.* import parsley.token.errors._ +/** ErrorConfig for producing more informative error messages + */ val errConfig = new ErrorConfig { override def labelSymbol = Map( "!=" -> Label("binary operator"), @@ -37,6 +39,9 @@ val errConfig = new ErrorConfig { ) } object lexer { + + /** Language description for the WACC lexer + */ private val desc = LexicalDesc.plain.copy( nameDesc = NameDesc.plain.copy( identifierStart = Basic(c => c.isLetter || c == '_'), @@ -74,6 +79,8 @@ object lexer { ) ) + /** Token definitions for the WACC lexer + */ private val lexer = Lexer(desc, errConfig) val ident = lexer.lexeme.names.identifier val integer = lexer.lexeme.integer.decimal32[Int] @@ -82,6 +89,8 @@ object lexer { val stringLit = lexer.lexeme.string.ascii val implicits = lexer.lexeme.symbol.implicits + /** Tokens for producing lexer-backed error messages + */ val errTokens = Seq( lexer.nonlexeme.names.identifier.map(v => s"identifier $v"), lexer.nonlexeme.integer.decimal32[Int].map(n => s"integer $n"), From f9e5ae4d98dc4276620939fc0093e6e832abb1ce Mon Sep 17 00:00:00 2001 From: Guy C Date: Fri, 7 Feb 2025 15:37:55 +0000 Subject: [PATCH 2/7] refactor: scaladocs-style commenting added in parser --- src/main/wacc/parser.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/wacc/parser.scala b/src/main/wacc/parser.scala index dac9fcd..888fc75 100644 --- a/src/main/wacc/parser.scala +++ b/src/main/wacc/parser.scala @@ -47,7 +47,8 @@ object parser { enum LabelType: case Expr case Pair - + + // Lexer-backed errorBuilder implicit val builder: ErrorBuilder[String] = new DefaultErrorBuilder with LexToken { def tokens = errTokens } @@ -151,8 +152,7 @@ object parser { ) | While("while" ~> ``.labelWithType(LabelType.Expr) <~ "do", `` <~ "done") | Block("begin" ~> `` <~ "end") - | VarDecl(``, `` <~ "=", ``.label("valid initial value for variable")) - // TODO: Can we inline the name of the variable in the message + | VarDecl(``, `` <~ "=", ``) | Assign(`` <~ "=", ``) private lazy val ``: Parsley[LValue] = `` | `` From ade1fdaffc4432605dd4869c1e696fecccdd1d25 Mon Sep 17 00:00:00 2001 From: Guy C Date: Fri, 7 Feb 2025 15:38:19 +0000 Subject: [PATCH 3/7] refactor: scaladocs-style commenting added in Error.scala --- src/main/wacc/Error.scala | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/wacc/Error.scala b/src/main/wacc/Error.scala index ba810f5..fec2662 100644 --- a/src/main/wacc/Error.scala +++ b/src/main/wacc/Error.scala @@ -3,6 +3,8 @@ package wacc import wacc.ast.Position import wacc.types._ +/** Error types for semantic errors + */ enum Error { case DuplicateDeclaration(ident: ast.Ident) case UndeclaredVariable(ident: ast.Ident) @@ -14,6 +16,13 @@ enum Error { 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 = { println("Semantic error:") 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 = { 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 = { println(s"(line ${pos.line}, column ${pos.column}):") } From d0324518b65a31db37ab0a9dd866313e2d872156 Mon Sep 17 00:00:00 2001 From: Guy C Date: Fri, 7 Feb 2025 15:40:14 +0000 Subject: [PATCH 4/7] fix: style fixes in parser --- src/main/wacc/parser.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/wacc/parser.scala b/src/main/wacc/parser.scala index 888fc75..92f7487 100644 --- a/src/main/wacc/parser.scala +++ b/src/main/wacc/parser.scala @@ -47,7 +47,7 @@ object parser { enum LabelType: case Expr case Pair - + // Lexer-backed errorBuilder implicit val builder: ErrorBuilder[String] = new DefaultErrorBuilder with LexToken { def tokens = errTokens From 09036040d6316ed63ac76722255e3078693eabe7 Mon Sep 17 00:00:00 2001 From: Guy C Date: Fri, 7 Feb 2025 16:13:50 +0000 Subject: [PATCH 5/7] refactor: further commenting to the parser --- src/main/wacc/parser.scala | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/wacc/parser.scala b/src/main/wacc/parser.scala index 92f7487..d4b4d25 100644 --- a/src/main/wacc/parser.scala +++ b/src/main/wacc/parser.scala @@ -73,6 +73,7 @@ object parser { SOps(InfixL)(Mul from "*", Div from "/", Mod from "%") +: SOps(Prefix)( Not from "!", + // notFollowedBy(negateCheck) ensures that negative numbers are parsed as a single int literal (Negate from (notFollowedBy(negateCheck) ~> "-")).hide, Len from "len", Ord from "ord", @@ -114,7 +115,16 @@ object parser { ((`` <**> ``) .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( "begin" ~> many( atomic(``.label("function declaration") <~> `` <~ "(") <**> `` @@ -175,6 +185,13 @@ object parser { ) 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 { case Return(_) | Exit(_) => true case If(_, thenStmt, elseStmt) => thenStmt.isReturning && elseStmt.isReturning From 764e335364a17870b664f7828ec98af0c01dc050 Mon Sep 17 00:00:00 2001 From: Jonny Date: Fri, 7 Feb 2025 16:28:58 +0000 Subject: [PATCH 6/7] refactor: add comments in ast.scala --- src/main/wacc/ast.scala | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/main/wacc/ast.scala b/src/main/wacc/ast.scala index 62b134c..ac0e585 100644 --- a/src/main/wacc/ast.scala +++ b/src/main/wacc/ast.scala @@ -8,7 +8,8 @@ import parsley.syntax.zipped._ import cats.data.NonEmptyList object ast { - // Expressions + /* ============================ EXPRESSIONS ============================ */ + sealed trait Expr extends RValue { val pos: Position } @@ -19,7 +20,8 @@ object ast { sealed trait Expr5 extends Expr4 sealed trait Expr6 extends Expr5 - // Atoms + /* ============================ ATOMIC EXPRESSIONS ============================ */ + case class IntLiter(v: Int)(val pos: Position) extends Expr6 object IntLiter extends ParserBridgePos1[Int, IntLiter] 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 object Parens extends ParserBridgePos1[Expr, Parens] - // Unary operators + /* ============================ UNARY OPERATORS ============================ */ + sealed trait UnaryOp extends Expr { val x: Expr } @@ -59,7 +62,8 @@ object ast { case class Chr(x: Expr6)(val pos: Position) extends Expr6 with UnaryOp object Chr extends ParserBridgePos1[Expr6, Chr] - // Binary operators + /* ============================ BINARY OPERATORS ============================ */ + sealed trait BinaryOp(val name: String) extends Expr { val x: 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") object Or extends ParserBridgePos2[Expr1, Expr, Or] - // Types + /* ============================ TYPES ============================ */ + sealed trait Type sealed trait BaseType extends Type with PairElemType case class IntType()(val pos: Position) extends BaseType @@ -125,11 +130,13 @@ object ast { case class UntypedPairType()(val pos: Position) extends PairElemType object UntypedPairType extends ParserBridgePos0[UntypedPairType] - // waccadoodledo + /* ============================ PROGRAM STRUCTURE ============================ */ + case class Program(funcs: List[FuncDecl], main: NonEmptyList[Stmt])(val pos: Position) object Program extends ParserBridgePos2[List[FuncDecl], NonEmptyList[Stmt], Program] - // Function Definitions + /* ============================ FUNCTION STRUCTURE ============================ */ + case class FuncDecl( returnType: Type, name: Ident, @@ -151,7 +158,8 @@ object ast { case class Param(paramType: Type, name: Ident)(val pos: Position) object Param extends ParserBridgePos2[Type, Ident, Param] - // Statements + /* ============================ STATEMENTS ============================ */ + sealed trait Stmt case class Skip()(val pos: Position) extends Stmt object Skip extends ParserBridgePos0[Skip] @@ -178,6 +186,8 @@ object ast { case class Block(stmt: NonEmptyList[Stmt])(val pos: Position) extends Stmt object Block extends ParserBridgePos1[NonEmptyList[Stmt], Block] + /* ============================ LVALUES & RVALUES ============================ */ + sealed trait LValue { val pos: Position } @@ -196,7 +206,8 @@ object ast { case class Snd(elem: LValue)(val pos: Position) extends PairElem object Snd extends ParserBridgePos1[LValue, Snd] - // Parser bridges + /* ============================ PARSER BRIDGES ============================ */ + case class Position(line: Int, column: Int) trait ParserSingletonBridgePos[+A] extends ErrorBridge { From e38932a3e8a7b96b714a72b10a825a11bd632049 Mon Sep 17 00:00:00 2001 From: Guy C Date: Fri, 7 Feb 2025 16:36:00 +0000 Subject: [PATCH 7/7] fix: uses correct comment syntax for comments on design justifications --- src/main/wacc/parser.scala | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/wacc/parser.scala b/src/main/wacc/parser.scala index d4b4d25..71ae4c4 100644 --- a/src/main/wacc/parser.scala +++ b/src/main/wacc/parser.scala @@ -115,16 +115,15 @@ object parser { ((`` <**> ``) .map(arr => (_: UntypedPairType) => arr) identity)) - /** 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. - */ + /* 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( "begin" ~> many( atomic(``.label("function declaration") <~> `` <~ "(") <**> ``