diff --git a/.commitlintrc.yml b/.commitlintrc.yml new file mode 100644 index 0000000..175ef04 --- /dev/null +++ b/.commitlintrc.yml @@ -0,0 +1 @@ +extends: "@commitlint/config-conventional" diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f0e0cf3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true + +[Makefile] +indent_style = tab +indent_size = 4 + diff --git a/.gitignore b/.gitignore index c3923f1..64ecf6a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .bsp/ .scala-build/ .vscode/ +wacc-examples/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..760b3d3 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,48 @@ +stages: + - check + - compile + - test + +default: + image: gumjoe/wacc-ci-scala:slim + +check_format: + stage: check + needs: [] + before_script: + - cs install scalafmt + script: + - scalafmt --check . + +check_commits: + stage: check + needs: [] + image: node:lts-alpine + before_script: + - apk add git + - npm install -g @commitlint/cli @commitlint/config-conventional + - git pull origin master + script: + - npx commitlint --from origin/master --to HEAD --verbose + +compile_jvm: + stage: compile + needs: [] + script: + - scala compile --platform jvm -Werror . + artifacts: + paths: + - .bsp/ + - .scala-build/ + +test_jvm: + stage: test + # Use our own runner (not cloud VM or shared) to ensure we have multiple cores. + tags: [ large ] + # This is expensive, so do use `dependencies` instead of `needs` to + # ensure all previous stages pass. + dependencies: [ compile_jvm ] + before_script: + - git clone https://$EXAMPLES_AUTH@gitlab.doc.ic.ac.uk/lab2425_spring/wacc-examples.git + script: + - scala test --platform jvm . diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..4d4a975 --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,2 @@ +version = 3.8.6 +runner.dialect = scala36 diff --git a/README.md b/README.md index 954e909..b9daa80 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,10 @@ builds your WACC compiler. Currently running 'make' will call `wacc-compiler` in the root directory of the project. If this doesn't work for whatever reason, there are a few different alternatives you can try in the makefile. **Do not use the makefile as you're working, it's for labts/CI!** + +# Contributing + +- All commit messages must follow the [conventional commits spec](https://www.conventionalcommits.org/en/v1.0.0/). + - **This includes merge request titles**. +- All scala code must be formated with `scala format`. +- All scala code must compile without errors OR WARNINGS. diff --git a/src/main/wacc/Main.scala b/src/main/wacc/Main.scala index 898b5f8..a4c9fd7 100644 --- a/src/main/wacc/Main.scala +++ b/src/main/wacc/Main.scala @@ -3,13 +3,14 @@ package wacc import parsley.{Success, Failure} def main(args: Array[String]): Unit = { - println("hello WACC!") + println("hello WACC!") - args.headOption match { - case Some(expr) => parser.parse(expr) match { - case Success(x) => println(s"$expr = $x") - case Failure(msg) => println(msg) - } - case None => println("please enter an expression") - } + args.headOption match { + case Some(expr) => + parser.parse(expr) match { + case Success(x) => println(s"$expr = $x") + case Failure(msg) => println(msg) + } + case None => println("please enter an expression") + } } diff --git a/src/main/wacc/lexer.scala b/src/main/wacc/lexer.scala index 7a1872f..f633bf6 100644 --- a/src/main/wacc/lexer.scala +++ b/src/main/wacc/lexer.scala @@ -5,12 +5,12 @@ import parsley.token.Lexer import parsley.token.descriptions.* object lexer { - private val desc = LexicalDesc.plain.copy( - // your configuration goes here - ) - private val lexer = Lexer(desc) + private val desc = LexicalDesc.plain.copy( + // your configuration goes here + ) + private val lexer = Lexer(desc) - val integer = lexer.lexeme.integer.decimal - val implicits = lexer.lexeme.symbol.implicits - def fully[A](p: Parsley[A]): Parsley[A] = lexer.fully(p) + val integer = lexer.lexeme.integer.decimal + val implicits = lexer.lexeme.symbol.implicits + def fully[A](p: Parsley[A]): Parsley[A] = lexer.fully(p) } diff --git a/src/main/wacc/parser.scala b/src/main/wacc/parser.scala index 083a8d0..370f8aa 100644 --- a/src/main/wacc/parser.scala +++ b/src/main/wacc/parser.scala @@ -7,14 +7,14 @@ import lexer.implicits.implicitSymbol import lexer.{integer, fully} object parser { - def parse(input: String): Result[String, BigInt] = parser.parse(input) - private val parser = fully(expr) - - private val add = (x: BigInt, y: BigInt) => x + y - private val sub = (x: BigInt, y: BigInt) => x - y + def parse(input: String): Result[String, BigInt] = parser.parse(input) + private val parser = fully(expr) - private lazy val expr: Parsley[BigInt] = - chain.left1(integer | "(" ~> expr <~ ")")( - ("+" as add) | ("-" as sub) - ) + private val add = (x: BigInt, y: BigInt) => x + y + private val sub = (x: BigInt, y: BigInt) => x - y + + private lazy val expr: Parsley[BigInt] = + chain.left1(integer | "(" ~> expr <~ ")")( + ("+" as add) | ("-" as sub) + ) } diff --git a/src/test/wacc/examples.scala b/src/test/wacc/examples.scala new file mode 100644 index 0000000..6400646 --- /dev/null +++ b/src/test/wacc/examples.scala @@ -0,0 +1,92 @@ +package wacc + +import org.scalatest.{ParallelTestExecution, BeforeAndAfterAll} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.Inspectors.forEvery +import parsley.{Success, Failure} + +class ParallelExamplesSpec + extends AnyFlatSpec + with BeforeAndAfterAll + with ParallelTestExecution { + val files = + allWaccFiles("wacc-examples/valid").map { p => + (p.toString, (_: Int) == 0) + } ++ + allWaccFiles("wacc-examples/invalid/syntaxErr").map { p => + (p.toString, (_: Int) == 100) + } ++ + allWaccFiles("wacc-examples/invalid/semanticErr").map { p => + (p.toString, (_: Int) == 200) + } ++ + allWaccFiles("wacc-examples/invalid/whack").map { p => + (p.toString, List(0, 100, 200).contains) + } + + // tests go here + forEvery(files.filter { (filename, _) => + !fileIsDissallowed(filename) + }) { (filename, expectedResult) => + s"$filename" should "be parsed with correct result" in { + val contents = os.read(os.Path(filename)) + parser.parse(contents) match { + case Success(x) => assert(expectedResult(x.toInt)) + case Failure(msg) => fail(msg) + } + } + } + + def allWaccFiles(dir: String): IndexedSeq[os.Path] = + val d = java.io.File(dir) + os.walk(os.Path(d.getAbsolutePath)).filter { _.ext == "wacc" } + + def fileIsDissallowed(filename: String): Boolean = + Seq( + "wacc-examples/valid/advanced", + "wacc-examples/valid/array", + "wacc-examples/valid/basic/exit", + "wacc-examples/valid/basic/skip", + "wacc-examples/valid/expressions", + "wacc-examples/valid/function/nested_functions", + "wacc-examples/valid/function/simple_functions", + "wacc-examples/valid/if", + "wacc-examples/valid/IO/print", + "wacc-examples/valid/IO/read", + "wacc-examples/valid/IO/IOLoop.wacc", + "wacc-examples/valid/IO/IOSequence.wacc", + "wacc-examples/valid/pairs", + "wacc-examples/valid/runtimeErr", + "wacc-examples/valid/scope", + "wacc-examples/valid/sequence", + "wacc-examples/valid/variables", + "wacc-examples/valid/while", + // invalid (syntax) + "wacc-examples/invalid/syntaxErr/array", + "wacc-examples/invalid/syntaxErr/basic", + "wacc-examples/invalid/syntaxErr/expressions", + "wacc-examples/invalid/syntaxErr/function", + "wacc-examples/invalid/syntaxErr/if", + "wacc-examples/invalid/syntaxErr/literals", + "wacc-examples/invalid/syntaxErr/pairs", + "wacc-examples/invalid/syntaxErr/print", + "wacc-examples/invalid/syntaxErr/sequence", + "wacc-examples/invalid/syntaxErr/variables", + "wacc-examples/invalid/syntaxErr/while", + // invalid (semantic) + "wacc-examples/invalid/semanticErr/array", + "wacc-examples/invalid/semanticErr/exit", + "wacc-examples/invalid/semanticErr/expressions", + "wacc-examples/invalid/semanticErr/function", + "wacc-examples/invalid/semanticErr/if", + "wacc-examples/invalid/semanticErr/IO", + "wacc-examples/invalid/semanticErr/multiple", + "wacc-examples/invalid/semanticErr/pairs", + "wacc-examples/invalid/semanticErr/print", + "wacc-examples/invalid/semanticErr/read", + "wacc-examples/invalid/semanticErr/scope", + "wacc-examples/invalid/semanticErr/variables", + "wacc-examples/invalid/semanticErr/while", + // invalid (whack) + "wacc-examples/invalid/whack" + ).find(filename.contains).isDefined +}