Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • shchen/cs320
  • raveendr/cs320
  • mwojnaro/cs320
3 results
Show changes
Showing
with 540 additions and 0 deletions
object MatchError
abstract class Foo
case class Bar(i: Int(32)) extends Foo
case class Baz(f1: Foo, f2: Foo) extends Foo
Baz(Baz(Bar(1), Bar(2)), Baz(Bar(3), Bar(4))) match {
case Baz(Baz(_, Bar(_)), _) => ()
}
end MatchError
object Match4
def sideEffect(): Int(32) = {
Std.printString("This side effect should only happen once!");
10
}
sideEffect() match {
case 1 => Std.printString("no"); false
case 2 => Std.printString("no"); false
case 10 => Std.printString("yes"); true
}
end Match4
object MixStdAndNonStd
def printString(str: String): Unit = {
Std.printString(str);
Std.printString(str)
}
val intInput: Int(32) = Std.readInt();
printString(Std.intToString(intInput))
end MixStdAndNonStd
\ No newline at end of file
object O
abstract class Option
case class None() extends Option
case class Some(v: Int(32)) extends Option
def isDefined(o: Option): Boolean = {
o match {
case None() => false
case _ => true
}
}
def get(o: Option): Int(32) = {
o match {
case Some(i) => i
case None() => error("get(None)")
}
}
def getOrElse(o: Option, i: Int(32)): Int(32) = {
o match {
case None() => i
case Some(oo) => oo
}
}
def orElse(o1: Option, o2: Option): Option = {
o1 match {
case Some(_) => o1
case None() => o2
}
}
def toList(o: Option): L.List = {
o match {
case Some(i) => L.Cons(i, L.Nil())
case None() => L.Nil()
}
}
end O
object ShortCircuit
true || error("");
false && error("")
end ShortCircuit
object SideEffect
def firstI(): Int(32) = {
Std.printInt(1);
1
}
def secondI(): Int(32) = {
Std.printInt(2);
2
}
def stringLeft(): String = {
Std.printString("SLeft");
"a"
}
def stringRight(): String = {
Std.printString("SRight");
"a"
}
def caseLeft(): L.List = {
Std.printString("CLeft");
L.Nil()
}
def caseRight(): L.List = {
Std.printString("CRight");
L.Nil()
}
// Make sure that operands to binary operations are interpreted only once
// and in the right order
firstI() + secondI();
firstI() - secondI();
firstI() * secondI();
firstI() / secondI();
firstI() % secondI();
firstI() < secondI();
firstI() <= secondI();
// Make sure that the rhs of comparisons to Unit (which always succeed)
// are interpreted.
Std.printString("Left") == Std.printString("Right");
// Make sure that string comparisons evaluate their arguments once
// and in the correct order
stringLeft() == stringRight();
// Make sure that case class comparisons evaluate their arguments once
// and in the correct order
caseLeft() == caseRight();
()
end SideEffect
\ No newline at end of file
/** This module contains basic functionality for Amy,
* including stub implementations for some built-in functions
* (implemented in WASM or JavaScript)
*/
object Std
def printInt(i: Int(32)): Unit = {
error("") // Stub implementation
}
def printString(s: String): Unit = {
error("") // Stub implementation
}
def printBoolean(b: Boolean): Unit = {
printString(booleanToString(b))
}
def readString(): String = {
error("") // Stub implementation
}
def readInt(): Int(32) = {
error("") // Stub implementation
}
def intToString(i: Int(32)): String = {
if (i < 0) {
"-" ++ intToString(-i)
} else {
val rem: Int(32) = i % 10;
val div: Int(32) = i / 10;
if (div == 0) { digitToString(rem) }
else { intToString(div) ++ digitToString(rem) }
}
}
def digitToString(i: Int(32)): String = {
error("") // Stub implementation
}
def booleanToString(b: Boolean): String = {
if (b) { "true" } else { "false" }
}
end Std
object StringInputToOutput
val stringInput: String = Std.readString();
val stringInput2: String = Std.readString();
Std.printString(stringInput ++ "!");
Std.printString(stringInput2 ++ "!")
end StringInputToOutput
\ No newline at end of file
object TestLists
val l: L.List = L.Cons(5, L.Cons(-5, L.Cons(-1, L.Cons(0, L.Cons(10, L.Nil())))));
Std.printInt(L.sum(l));
Std.printString(L.toString(L.mergeSort(l)))
end TestLists
package amyc.test
import amyc.utils._
import java.io.File
import org.junit.Assert.fail
abstract class CompilerTest extends TestUtils {
private def runPipeline(pipeline: Pipeline[List[File], Unit], fileNames: List[String]) = {
val ctx = Context(new Reporter, fileNames)
val files = ctx.files.map(new File(_))
pipeline.run(ctx)(files)
ctx.reporter.terminateIfErrors()
}
private def runPipelineRedirected(
pipeline: Pipeline[List[File], Unit],
compiledFiles: List[String],
input: String
): String = {
testWithRedirectedIO(runPipeline(pipeline, compiledFiles), input)
}
private def assertEqual(output: String, expected: String) = {
val rejectLine = (s: String) =>
s.isEmpty ||
s.startsWith("[ Info ]") ||
s.startsWith("[Warning]") ||
s.startsWith("[ Error ]") ||
s.startsWith("[ Fatal ]")
def filtered(s: String) = s.linesIterator.filterNot(rejectLine).mkString("\n")
val filteredOutput = filtered(output)
val filteredExpected = filtered(expected)
if (filteredOutput != filteredExpected) {
val sb = new StringBuffer()
sb.append("\nOutput is different:\n")
sb.append("\nOutput: \n")
sb.append(filteredOutput)
sb.append("\n\nExpected output: \n")
sb.append(filteredExpected)
sb.append("\n")
fail(sb.toString)
}
}
protected def compareOutputs(
pipeline: Pipeline[List[File], Unit],
compiledFiles: List[String],
expectedFile: String,
input: String = ""
) = {
try {
val output = runPipelineRedirected(pipeline, compiledFiles, input)
val expected = scala.io.Source.fromFile(new File(expectedFile)).mkString
assertEqual(output, expected)
} catch {
// We only want to catch AmyFatalError gracefully, the rest can propagate
case AmycFatalError(msg) =>
fail(s"\n $msg\n")
}
}
protected def demandPass(
pipeline: Pipeline[List[File], Unit],
compiledFiles: List[String],
input: String = ""
) = {
try {
runPipelineRedirected(pipeline, compiledFiles, input)
} catch {
case AmycFatalError(msg) =>
fail(s"\n $msg\n")
}
}
protected def demandFailure(
pipeline: Pipeline[List[File], Unit],
compiledFiles: List[String],
input: String = ""
) = {
try {
runPipelineRedirected(pipeline, compiledFiles, input)
fail("Test should fail but it passed!")
} catch {
case AmycFatalError(_) =>
// Ok, this is what we wanted. Other exceptions should propagate though
}
}
}
package amyc.test
import org.junit.Test
abstract class ExecutionTests extends TestSuite {
val baseDir = "execution"
val outputExt = "txt"
@Test def testEmptyObject = shouldOutput("EmptyObject")
@Test def testStringInputToOutput = shouldOutput(List("Std", "StringInputToOutput"), "StringInputToOutput", "Hello World\nHello World again")
@Test def testIntInputToOutput = shouldOutput(List("Std", "IntInputToOutput"), "IntInputToOutput", "42")
@Test def testMixStdAndNonStd = shouldOutput(List("Std", "MixStdAndNonStd"), "MixStdAndNonStd", "42")
@Test def testBasicArithmetic = shouldOutput(List("Std", "BasicArithmetic"), "BasicArithmetic", "")
@Test def testDivisionByZero = shouldFail("Division")
@Test def testBasicPatternMatching = shouldOutput(List("Std", "BasicPatternMatching"), "BasicPatternMatching", "")
@Test def testBasicBranching = shouldOutput(List("Std", "BasicBranching"), "BasicBranching", "")
@Test def testBasicConditions = shouldOutput(List("Std", "BasicConditions"), "BasicConditions", "")
@Test def testBasicError = shouldFail("BasicError")
@Test def testEquality = shouldOutput(List("Std", "Equality"), "Equality")
@Test def testFactorial = shouldOutput(List("Std", "Factorial"), "Factorial", "")
@Test def testArithmetic = shouldOutput(List("Std", "Arithmetic"), "Arithmetic", "")
@Test def testLists = shouldOutput(List("Std", "Option", "List", "TestLists"), "TestLists", "")
@Test def testMatchError1 = shouldFail("MatchError1")
@Test def testMatchError2 = shouldFail("MatchError2")
@Test def testMatchError3 = shouldFail("MatchError3")
@Test def testMatchError4 = shouldFail("MatchError4")
@Test def testMatch1 = shouldPass("Match1")
@Test def testMatch2 = shouldPass("Match2")
@Test def testMatch3 = shouldPass("Match3")
@Test def testMatch4 = shouldOutput(List("Std", "Match4"), "Match4")
@Test def testShortCircuit1 = shouldPass("ShortCircuit")
@Test def testShortCircuit2 = shouldFail("ShortCircuit")
@Test def testLocals1 = shouldPass("Locals")
@Test def testLocals2 = shouldFail("Locals")
@Test def testFunCallEnv = shouldOutput(List("FunCallEnv", "Std"), "FunCallEnv")
}
package amyc
package test
import parsing._
import analyzer._
import interpreter.Interpreter
import amyc.utils.Frontend
class InterpreterTests extends ExecutionTests {
val pipeline = Frontend.pipeline.andThen(Interpreter)
}
package amyc.test
import amyc.utils.Pipeline
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
abstract class TestSuite extends CompilerTest {
val pipeline: Pipeline[List[File], Unit]
val baseDir: String
lazy val effectiveBaseDir: String =
// getClass.getResource(s"/$baseDir").getPath
s"test/resources/$baseDir"
val passing = "passing"
val failing = "failing"
val outputs = "outputs"
val tmpDir = Files.createTempDirectory("amyc");
val outputExt: String
def getResourcePath(relativePath: String, otherPath: Option[String] = None): String =
val firstPath = Path.of(effectiveBaseDir, relativePath)
val (stream, path) =
if Files.exists(firstPath) then
(Files.newInputStream(firstPath), relativePath)
else
otherPath match
case Some(p) =>
val secondPath = Path.of(effectiveBaseDir, p)
(Files.newInputStream(secondPath), p)
case None =>
assert(false, s"can not read $effectiveBaseDir/$relativePath")
(null, "")
val targetPath = tmpDir.resolve(path)
Files.createDirectories(targetPath.getParent())
Files.copy(stream, targetPath, StandardCopyOption.REPLACE_EXISTING)
targetPath.toAbsolutePath().toString()
def shouldOutput(inputFiles: List[String], outputFile: String, input: String = ""): Unit = {
compareOutputs(
pipeline,
inputFiles map (f => getResourcePath(s"$passing/$f.amy", Some(s"$passing/$f.grading.amy"))),
getResourcePath(s"$outputs/$outputFile.$outputExt", Some(s"$outputs/$outputFile.grading.$outputExt")),
input
)
}
def shouldOutput(inputFile: String): Unit = {
shouldOutput(List(inputFile), inputFile)
}
def shouldFail(inputFiles: List[String], input: String = ""): Unit = {
demandFailure(
pipeline,
inputFiles map (f => getResourcePath(s"$failing/$f.amy", Some(s"$failing/$f.grading.amy"))),
input
)
}
def shouldFail(inputFile: String): Unit = {
shouldFail(List(inputFile))
}
def shouldPass(inputFiles: List[String], input: String = ""): Unit = {
demandPass(pipeline, inputFiles map (f => getResourcePath(s"$passing/$f.amy", Some(s"$passing/$f.grading.amy"))), input)
}
def shouldPass(inputFile: String): Unit = {
shouldPass(List(inputFile))
}
}
package amyc.test
import java.io._
/** Some utilities for running tests */
trait TestUtils {
/** Run test,
* with input also redirected from a String,
* and output is redirected to a local StringBuilder.
*/
def testWithRedirectedIO[T](test: => T, input: String): String = {
import scala.Console._
val inputS = new StringReader(input)
val outputS = new ByteArrayOutputStream()
withOut(outputS) {
withErr(outputS) {
withIn(inputS) {
test
}
}
}
outputS.toString()
}
}
version := "1.7"
organization := "ch.epfl.lara"
scalaVersion := "3.5.2"
assembly / test := {}
name := "amyc"
Compile / scalaSource := baseDirectory.value / "src"
scalacOptions ++= Seq("-feature")
Test / scalaSource := baseDirectory.value / "test" / "scala"
Test / parallelExecution := false
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.4" % "test"
testOptions += Tests.Argument(TestFrameworks.JUnit, "-v")
object Arithmetic
def pow(b: Int(32), e: Int(32)): Int(32) = {
if (e == 0) { 1 }
else {
if (e % 2 == 0) {
val rec: Int(32) = pow(b, e/2);
rec * rec
} else {
b * pow(b, e - 1)
}
}
}
def gcd(a: Int(32), b: Int(32)): Int(32) = {
if (a == 0 || b == 0) {
a + b
} else {
if (a < b) {
gcd(a, b % a)
} else {
gcd(a % b, b)
}
}
}
Std.printInt(pow(0, 10));
Std.printInt(pow(1, 5));
Std.printInt(pow(2, 10));
Std.printInt(pow(3, 3));
Std.printInt(gcd(0, 10));
Std.printInt(gcd(17, 99)); // 1
Std.printInt(gcd(16, 46)); // 2
Std.printInt(gcd(222, 888)) // 222
end Arithmetic
object Factorial
def fact(i: Int(32)): Int(32) = {
if (i < 2) { 1 }
else {
val rec: Int(32) = fact(i-1);
i * rec
}
}
Std.printString("5! = " ++ Std.intToString(fact(5)));
Std.printString("10! = " ++ Std.intToString(fact(10)))
end Factorial
object Hanoi
def solve(n : Int(32)) : Int(32) = {
if (n < 1) {
error("can't solve Hanoi for less than 1 plate")
} else {
if (n == 1) {
1
} else {
2 * solve(n - 1) + 1
}
}
}
Std.printString("Hanoi for 4 plates: " ++ Std.intToString(solve(4)))
end Hanoi
\ No newline at end of file
object Hello
Std.printString("Hello " ++ "world!")
end Hello
object HelloInt
Std.printString("What is your name?");
val name: String = Std.readString();
Std.printString("Hello " ++ name ++ "! And how old are you?");
val age: Int(32) = Std.readInt();
Std.printString(Std.intToString(age) ++ " years old then.")
end HelloInt