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

William Chen #10

Open
wants to merge 4 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
159 changes: 159 additions & 0 deletions .cache-main

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions .classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<classpath>
<classpathentry kind="src" path="src/main/scala"/>
<classpathentry kind="src" path="src/test/scala"/>
<classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/>
<classpathentry kind="lib" path="/Users/wchen/.ivy2/cache/org.scalatest/scalatest_2.11/bundles/scalatest_2.11-2.2.6.jar"/>
<classpathentry kind="lib" path="/Users/wchen/.ivy2/cache/org.scala-lang.modules/scala-xml_2.11/bundles/scala-xml_2.11-1.0.2.jar"/>
<classpathentry kind="lib" path="/Users/wchen/.ivy2/cache/org.scalacheck/scalacheck_2.11/jars/scalacheck_2.11-1.12.5.jar"/>
<classpathentry kind="lib" path="/Users/wchen/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>
13 changes: 13 additions & 0 deletions .project
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<projectDescription>
<name>regular-expressions</name>
<buildSpec>
<buildCommand>
<name>org.scala-ide.sdt.core.scalabuilder</name>
</buildCommand>
</buildSpec>
<natures>
<nature>org.scala-ide.sdt.core.scalanature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<linkedResources> </linkedResources>
</projectDescription>
16 changes: 15 additions & 1 deletion reflection.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
# Reflection on implementing regular expressions of a DSL

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mm, makes sense. I couldn't find my notes and was also struggling because of it. Glad you were able to make good use of them!

I tried to complete this assignment without my notes from the Complex number tutorial, and had the hardest time remembering all the nuances necessary when implementing an internal DSL. After fishing out my notes from that class, the assignment went by very quickly as it more or less layed out a framework for what needed to be done. It also saved me a lot of time by reminding me of several Scala-specifics such as putting the implicit conversions inside the companion object, importing the class when using implicit methods, importing postfixOps when using postfix operations, among other details.

## Which operators were easiest to implement and why?

In particular, ||, ~, *, and + were extremely straight forward to implement considering they were all one liners. It helped a lot that the functionality was already implemented such that we only had to figure out the syntactical sugar. Even then, most of the tricks that were necessary to make the syntactical changes were already covered when we did the Complex number tutorial.

## Which operators were most difficult to implement and why?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found {n} repetition similarly difficult. I like how you specifically rleate your work to the different parts of the complex number tutorial.


The operator that proved to be most difficult was probably the {n} repetition, but even then, after realizing to use an apply method, the actual implementation was not very difficult. I think conceptually I struggled most with implicit conversions as there are very Scala-specific details that need to addressed. For instance, I completely forgot that I had to import RegularExpression._ in order to use the implicit conversions. I can't even begin to imagine how difficult this assignment would have been had we not done the Complex number tutorial in class, as the tutorial covered companion objects, implicit conversion, postfix operators, when to use apply, among several other things. I think it also helped that this isn't my first time seeing Regular Expressions (CS81), so I could focus more on the implementation details rather than trying to understand the design and the motivations behind the DSL.

## Comment on the design of this internal DSL

Write a few brief paragraphs that discuss:
Expand All @@ -15,4 +21,12 @@ Write a few brief paragraphs that discuss:
you implement it _or_ what features of Scala would prevent you from
implementing it? (You don't have to write code for this part. You could say
"I would use literal extension to..." or "Scala's rules for valid
identifiers prevent...")
identifiers prevent...")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think there's a way you could change the syntax to make order of operations more specific? Maybe add parenthesis or something?

This design seems to be geared more towards mathemeticians not so well versed in computer science that want to use regular expressions. Almost immediately, the first application that comes to mind is CS81, which is a Logic and Computability course offered at Harvey Mudd. It seems like this DSL would be helpful for students in that class as the syntax is more intuitive in that it reflects exactly how regular expressions look when they are taught or when they are introduced in the textbook. However, at the same time, to someone who isn't familiar with regular expressions but is familiar with computer science, perhaps calling each method would make more sense than the DSL we provided. For instance, Concat(Star(hello), world) may make more sense than hello* + world as the ordering of operations is not as explicit in our DSL.

The DSL makes writing regular expressions both easy and intuitive, as it should. As such, the DSL makes writing any pattern-oriented data pretty easy (by nature of regular expressions). On the same note, the DSL is extremely domain specific in that trying to do anything else besides writing regular expressions would be quite cumbersome. Things that cannot be easily described with regular expressions prove to be difficult. For instance, checking a string to see if it's a palindrome would be difficult because the string is not regular.

One syntactic change that I actually made was implementing both the Kleene star <*> and the concatanation operator <+> as just * and +. I felt like this change was not particularly difficult to make (as it involved simply leveraging this <*> and this <+>). It makes the DSL look pretty much exactly like how a regular expression would be written. I think although it would be pretty bad to overload + and * as these are already existing operators that are very commonly used, given how specific this DSL is, I don't imagine the users would explain a lot.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure that I agree with your point about it not being a good idea to overload * and +. One advantage of that notation is less to type and thus less to potentially mess up.


56 changes: 36 additions & 20 deletions src/main/scala/dsls/regex/Program.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
package dsls.regex
import scala.language.postfixOps
// Import to allow for implicit conversions in regular expressions
import RegularExpression._

object Program extends App {

Expand All @@ -9,16 +12,26 @@ object Program extends App {
* val zero = '0'
* etc.
***************************************************************************/
val zero = Literal('0')
val one = Literal('1')
val two = Literal('2')
val three = Literal('3')
val four = Literal('4')
val five = Literal('5')
val six = Literal('6')
val seven = Literal('7')
val eight = Literal('8')
val nine = Literal('9')
// val zero = Literal('0')
// val one = Literal('1')
// val two = Literal('2')
// val three = Literal('3')
// val four = Literal('4')
// val five = Literal('5')
// val six = Literal('6')
// val seven = Literal('7')
// val eight = Literal('8')
// val nine = Literal('9')
val zero = '0'
val one = '1'
val two = '2'
val three = '3'
val four = '4'
val five = '5'
val six = '6'
val seven = '7'
val eight = '8'
val nine = '9'

require(zero matches "0")
require(one matches "1")
Expand All @@ -37,7 +50,8 @@ object Program extends App {
* Make it possible to replace the definition of answer with:
* val answer = "42"
***************************************************************************/
val answer = Concat(four, two)
// val answer = Concat(four, two)
val answer = "42"

require(answer matches "42")

Expand All @@ -47,9 +61,9 @@ object Program extends App {
* Make it possible to replace the definition of digit with:
* val digit = '0' || '1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9'
***************************************************************************/
val digit = Union(zero, Union(one, Union(two, Union(three, Union(four,
Union(five, Union(six, Union(seven, Union(eight, nine)))))))))

// val digit = Union(zero, Union(one, Union(two, Union(three, Union(four,
// Union(five, Union(six, Union(seven, Union(eight, nine)))))))))
val digit = '0' || '1' || '2' || '3' || '4' || '5' || '6' || '7' || '8' || '9'
require(digit matches "0")
require(digit matches "1")
require(digit matches "2")
Expand All @@ -67,8 +81,8 @@ object Program extends App {
* Make it possible to replace the definition of digit with:
* val pi = '3' ~ '1' ~ '4'
***************************************************************************/
val pi = Concat(Literal('3'), Concat(Literal('1'), Literal('4')))

// val pi = Concat(Literal('3'), Concat(Literal('1'), Literal('4')))
val pi = '3' ~ '1' ~ '4'
require(pi matches "314")

/****************************************************************************
Expand All @@ -77,7 +91,8 @@ object Program extends App {
* Make it possible to replace the definition of zeroOrMoreDigits with:
* val zeroOrMoreDigits = digit <*>
***************************************************************************/
val zeroOrMoreDigits = Star(digit)
// val zeroOrMoreDigits = Star(digit)
val zeroOrMoreDigits = digit*

require(zeroOrMoreDigits matches "")
require(zeroOrMoreDigits matches "0")
Expand All @@ -91,7 +106,8 @@ object Program extends App {
* Make it possible to replace the definition of number with:
* val number = digit <+>
***************************************************************************/
val number = Concat(digit, zeroOrMoreDigits)
// val number = Concat(digit, zeroOrMoreDigits)
val number = digit+

require(!(number matches ""))
require(number matches "0")
Expand All @@ -105,8 +121,8 @@ object Program extends App {
* Make it possible to replace the definition of cThree with:
* val cThree = 'c'{3}
***************************************************************************/
val cThree = Concat(Literal('c'), Concat(Literal('c'), Literal('c')))

// val cThree = Concat(Literal('c'), Concat(Literal('c'), Literal('c')))
val cThree = 'c'{3}
require(cThree matches "ccc")

/****************************************************************************
Expand Down
48 changes: 48 additions & 0 deletions src/main/scala/dsls/regex/RegularExpression.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,54 @@ package dsls.regex
abstract class RegularExpression {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Most of our implementation matches up pretty well, with a few small differences here and there.

/** returns true if the given string matches this regular expression */
def matches(string: String) = RegexMatcher.matches(string, this)
// Handles matches for a character input, not necessary if we have implicit conversion
// def matches(char: Char) = RegexMatcher.matches(char.toString, this)
/** allows users to use || as union operator */
def ||(rest: RegularExpression) = Union(this, rest)

/** allows users to use ~ as concatenation operator */
def ~(rest: RegularExpression) = Concat(this, rest)

/** allows users to use * or <*> for 0 or more repetitions */
def <*> = Star(this)
/** leverage implemented <*> to allow for flexibility in syntax */
def * = this <*>

/** allows users to use + or <+> for 1 or more repetitions */
def <+> = this ~ this*
/** leverage implemented <+> to allow for flexibility in syntax */
def + = this <+>

/** uses pattern matching to allow users to use {n} to represent n repetitions of this */
def apply(repeats: Int): RegularExpression = {
repeats match {
case 0 => EPSILON
// Recursive case where we concatenate 1 instance of this
case x => this ~ this{repeats-1}
}
}
}

/** A companion object for RegularExpression to handle implicit conversions*/
object RegularExpression {
/** implicit conversion from Char to valid RegularExpression */
implicit def charToRegex(char: Char): RegularExpression = Literal(char)

/** implicit conversion from String to valid RegularExpression */
implicit def stringToRegex(string: String): RegularExpression = {
// Convert string to a list of literals such that they are valid RegularExpressions
val listLiterals: List[RegularExpression] = string.toList map Literal

// Method that uses pattern matching to reconstruct the literals
def literalsCombine(list: List[RegularExpression]): RegularExpression = {
list match {
case Nil => EPSILON
case (x::xs) => Concat(x, literalsCombine(xs))
}
}
// Call literalsCombine to concatenate literals to make a RegularExpression
literalsCombine(listLiterals)
}
}

/** a regular expression that matches nothing */
Expand Down