-
Notifications
You must be signed in to change notification settings - Fork 13
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
base: master
Are you sure you want to change the base?
William Chen #10
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
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> |
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> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,15 @@ | ||
# Reflection on implementing regular expressions of a DSL | ||
|
||
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? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
|
@@ -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...") | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,54 @@ package dsls.regex | |
abstract class RegularExpression { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 */ | ||
|
There was a problem hiding this comment.
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!