Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

External Lab - Ellen Hui #5

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/main/scala/calculator/ir/AST.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ package calculator.ir
* -----------
* Grammar
* -----------
*
* n ∈ 𝒵
*
*
* n ∈ 𝒵
*
* e ∈ Expr ::= e + t | e - t | t
* t ∈ Term ::= t * f | t / f | f
* f ∈ Fact ::= n | ( e )
*
*
*/

sealed abstract class AST
sealed abstract class Expr extends AST

case class Num(n: Int) extends Expr
case class Plus(left: Expr, right: Expr) extends Expr
case class Minus(left: Expr, right: Expr) extends Expr
case class Times(left: Expr, right: Expr) extends Expr
case class Div(left: Expr, right: Expr) extends Expr
9 changes: 6 additions & 3 deletions src/main/scala/calculator/ir/sugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ package object ir {

// to use a number on its own
implicit def int2Number(i: Int): Num = Num(i)

// to use a number as part of a binary operation...
implicit def intToExprBuilder(n: Int) = new ExprBuilder(Num(n))

// to build up operations using infix notation from left to right...
// ExprBuilder saves the left operand and defines methods that
// take the right operand and returns the appropriate Expr
// ExprBuilder saves the left operand and defines methods that
// take the right operand and returns the appropriate Expr
implicit class ExprBuilder(val left: Expr) {
def |+|(right: Expr) = Plus(left, right)
def |-|(right: Expr) = Minus(left, right)
def |*|(right: Expr) = Times(left, right)
def |/|(right: Expr) = Div(left, right)
}
}
23 changes: 16 additions & 7 deletions src/main/scala/calculator/parser/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,24 @@ object CalcParser extends JavaTokenParsers with PackratParsers {
def apply(s: String): ParseResult[AST] = parseAll(expr, s)

// expressions
lazy val expr: PackratParser[Expr] =
( expr~"+"~fact ^^ {case l~"+"~r ⇒ l |+| r}
| fact )

lazy val expr: PackratParser[Expr] =
( expr~"+"~term ^^ {case l~"+"~r ⇒ l |+| r}
| expr~"-"~term ^^ {case l~"-"~r => l |-| r}
| term )


lazy val term: PackratParser[Expr] =
( term~"*"~fact ^^ {case l~"*"~r => l |*| r}
| term~"/"~fact ^^ {case l~"/"~r => l |/| r}
| fact
)

// factors
lazy val fact: PackratParser[Expr] =
number

( number
| "("~>expr<~")" )

// numbers
def number: Parser[Num] = wholeNumber ^^ {s ⇒ Num(s.toInt)}

}
5 changes: 4 additions & 1 deletion src/main/scala/calculator/semantics/Interpreter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@ package object semantics {
def eval(ast: AST): Int = ast match {
case Num(i) ⇒ i
case Plus(left, right) ⇒ eval(left) + eval(right)
case Minus(left, right) ⇒ eval(left) - eval(right)
case Times(left, right) ⇒ eval(left) * eval(right)
case Div(left, right) ⇒ eval(left) / eval(right)
}
}
}
56 changes: 50 additions & 6 deletions src/test/scala/calculator/parser/ParserTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,82 @@ import edu.hmc.langtools._
class CalcParserTests extends FunSpec with LangParseMatchers[AST] {

override val parser = CalcParser.apply _

describe("A number") {

it("can be a single digit") {
program("1") should parseAs ( 1 )
}

it ("can be multiple digits") {
program("10") should parseAs ( 10 )
program("121") should parseAs ( 121 )
}

it ("can be a negative number") {
program("-10") should parseAs ( -10 )
}

it ("cannot be floating-point number") {
program("1.1") should not (parse)
program(" .3") should not (parse)
}

}

describe("Addition") {

it("can add two numbers") {
program("1+1") should parseAs ( 1 |+| 1 )
}

it("can be chained (and is left-associative)") {
program("1 + 2 + 100") should parseAs ( (1 |+| 2) |+| 100 )
}

}

describe("Subtraction") {

it("can subtract two numbers") {
program("1-1") should parseAs ( 1 |-| 1 )
}

it("can be chained (and is left-associative)") {
program("1 - 2 - 2") should parseAs ( (1 |-| 2) |-| 2 )
}
}

describe("Multiplication") {

it("can multiply two numbers") {
program("2*2") should parseAs ( 2 |*| 2 )
}

it("can be chained (and is left-associative)") {
program("1 * 2 * 2") should parseAs ( (1 |*| 2) |*| 2 )
}
}

describe("Division") {

it("can divide two numbers") {
program("2/2") should parseAs ( 2 |/| 2 )
}

it("can be chained (and is left-associative)") {
program("1 / 2 / 2") should parseAs ( (1 |/| 2) |/| 2 )
}
}

describe("Parentheticals") {
it("is legal") {
program("(1)") should parseAs (1)
}

it("changes precedence ") {
program("1 + (1 + 1)") should parseAs (1 |+| (1 |+| 1))
}
}

}
62 changes: 62 additions & 0 deletions src/test/scala/calculator/semantics/SemanticsTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,66 @@ class NumSemanticsTests extends FunSpec

}

describe("Subtraction") {

it("can subtract two numbers") {
program("1 - 1") should compute (0)
}

it("can be chained (and is left-associative)") {
program("1 - 2 - 2") should compute (-3)
}

it("can handle negative numbers") {
program("1 - -1") should compute (2)
}

}

describe("Multiplication") {

it("can multiply two numbers") {
program("2 * 3") should compute (6)
}

it("can be chained (and is left-associative)") {
program("1 * 2 * 2") should compute (4)
}

it("can handle negative numbers") {
program("1 * -1") should compute (-1)
}

it("can handle TWO negative numbers") {
program("-1 * -1") should compute (1)
}

}

describe("Division") {

it("can divide two numbers") {
program("6 / 3") should compute (2)
}

it("can be chained (and is left-associative)") {
program("8 / 2 / 2") should compute (2)
}

it("can handle negative numbers") {
program("1 * -1") should compute (-1)
}

it("can handle TWO negative numbers") {
program("-1 * -1") should compute (1)
}
}

describe("Parentheticals") {

it("can change associativity") {
program("1 - (1 - 1)") should compute (1)
}
}

}