-
Notifications
You must be signed in to change notification settings - Fork 10
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
Joshua Landgraf #5
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,17 @@ | ||
# Building and running Piconot | ||
You can compile and run code for the external dls using the command provided on the website. | ||
|
||
`sbt "run-main piconot.external.Piconot resources/empty.txt src/main/scala/piconot/external/Empty.bot"` | ||
|
||
It's easy to modify this command for different maps and code files. The following command runs the other example code file on the other provided map. | ||
|
||
`sbt "run-main piconot.external.Piconot resources/maze.txt src/main/scala/piconot/external/RightHand.bot"` | ||
|
||
You can also run programs written with the internal DSL via sbt. The following commands will run the two example programs for the internal DSL. | ||
|
||
`sbt "run-main fillEmptyRoom"` | ||
|
||
`sbt "run-main fillMaze"` | ||
|
||
|
||
Since I didn't use Eclipse at all during this assignment, I have no idea how compatible it is with my implementations. However, since sbt works, I suspect that Eclipse will work as well. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,25 @@ | ||
# Design | ||
|
||
## Who is the target for this design, e.g., are you assuming any knowledge on the part of the language users? | ||
Given the complexity of the problem, the user will be expected to be familiar with how picobot works. So, they will already need to know that picobot has 100 possible states, that it can only check if there are walls to the north / south / east / west. They syntax also looks kind of like python's, so people who are familiar with python might have a slight edge (it was not my original idea to steal Python's syntax, but I found it helped make my code a lot more compact and was one of the last changes I made to my desigh). | ||
|
||
## Why did you choose this design, i.e., why did you think it would be a good idea for users to express the maze-searching computation using this syntax? | ||
I think there are two major changes that really help make writing programs easier in my design. First, states are named, not numbered. This helps the programmer keep track of what that state is supposed to accomplish or what they know is true in that state. Second, NEWS is replaced with up / right / down / left, which is more natural for the problem. | ||
|
||
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. We made (or tried to make) the same changes. I agree, both help immensely with readability and debugging! |
||
For better or worse, I tried to make the syntax more natural so that it is easier for the programmer to express their thoughts and for a reader to determine the programmer's intentions. While natural language programming is generally a bad idea, I think this syntax is unambiguous and much easier to understand than before. This should help reduce the cognitive load of converting between ideas in your head and code. In fact, I think the syntax may even make most of the comments unnecessary as it flows really well. | ||
|
||
In later iterations of my design, I also tried to remove repeated elements out of the code by modifying the design itself. For instance, it makes a lot of sense to define rules starting in a particular state next to each other, so I moved the starting state out of the rules and grouped them by their starting state. I also heavily rely on implicit behavior to prevent the natural language code from getting too long. Most rules can be written in less than 80 characters without much effort at all. In fact, the longest line of code (excluding comments) is 69 characters in example-ideal.txt and 52 characters in example-ideal-maze.txt. | ||
|
||
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. This sounds great. I like it! |
||
## What behaviors are easier to express in your design than in Picobot’s original design? If there are no such behaviors, why not? | ||
The natural language syntax makes handling states, wildcards, not moving, and staying in the same state really easy because you don't really have to express them at all. Each different state name is implicity a different state number, so the programmer doesn't have to keep track of which number state does what (since there is no "state 0", the starting state is the first one in the code). Wildcards are automatically generated when the programmer's logic does not depend on whether they can move in a direction or not, don't moves are automatically generated when the programmer doesn't specify a movement direction, and transitions to the same state are automatically generated if the programmer doesn't specify a state transition. This helps keep programs short (despite the verbosity of natural language) and helps the programmer focus on the trickier parts of the implementation. | ||
|
||
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. We made the same design decision, but one question we wrestled with was, "Is that really easier?" Might people make a mistake in their code, like forgetting to put a move direction, and then have trouble debugging because the code still compiles and runs just fine so they don't realize they made a mistake? |
||
## What behaviors are more difficult to express in your design than in Picobot’s original design? If there are no such behaviors, why not? | ||
While the design is very different, the logic and concepts are largely the same. So, ignoring that my design is more verbose, you can express logic at least as easily as in the original design. | ||
|
||
## On a scale of 1–10 (where 10 is “very different”), how different is your syntax from PicoBot’s original design? | ||
About 8 or so. The only similarity between the two designs is that it follows roughly the same order (current state, pattern, movement direction, next state). I didn't give it a higher score because I feel like it might be possible to create higher-level logic in this language. For example, the logic "move in a certain direction until you cannot anymore" may be used often in robot programming and a really great DSL might make it easy to do these kinds of common things. For what it's worth, it still is pretty easy and fast to do this in my design (you could do it in 55 characters (including newlines) with 1-character state names). | ||
|
||
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 love how you consider and test the verbosity of your language. |
||
## Is there anything you would improve about your design? | ||
Like I said above, adding some pre-defined logic could simplify the process or creating an advanced program. It would have also been nice to test CS 5 students on how they liked the natural language aspect as it may not work well with how some people think (though it can't really be worse than the original design, so at least it wouldn't make programming harder for some students). | ||
|
||
P.S. After working on implementing the internal and external DSLs, I have realized a few flaws in the original design and bugs in the example code. However, rather than change these, I decided to leave them as they are (except for this commment) so that you can see my original thoughts and code. If you want to see what flaws / bugs I found, I have documented them in my evaulation of the DSL implementations in evaluation.md. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,15 +5,39 @@ | |
_Describe each change from your ideal syntax to the syntax you implemented, and | ||
describe_ why _you made the change._ | ||
|
||
Many of the changes had to do with limitations with Scala (or at least what I could accomplish in it in a reasonable about of time). In fact, most of the changes can be described as making my "ideal" language more Scala-y. For example, comments are now made with a double slash instead of a pound sign. I also had to move away from a more Pythonic syntax to Scala's own syntax. So, state blocks don't start with a colon and are instead surrounded by curly brackets. Scala line indentation also applies. Since we have to use Scala for the internal DSL, we also have to run the code within an object that extends JFXApp and incldue import statements, which takes away from the clean interface of the original language. Another notable change is the addition of parentheses everywhere and the lack of spaces between many words. These were also forced by Scala's own contrains and the time I wanted to spend on this assignment. | ||
|
||
One feature that I _added_ to the internal DSL was the "WhenIn" keyword to indicate that you were entering a state block. I spent a long time trying to get it working without a keyword beforehand, but implicit conversion and by-name parameters apparently do not mix well in Scala. Not even my hacky workarounds succeeded (I tried returning a function to do the same job as a by-name parameter like in my final WhenIn implementation). While I was somewhat forced to create WhenIn, it was actually kind of a nice addition and some people might actually prefer using it. | ||
|
||
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 like this work around. Its a good idea! |
||
Other changes include requiring quotes around state names, which can actually be nice if your text editor has syntax highlighting, and eliminating some spaced between words for compactness and ease of implementation. The compact names also helped make the automatic completion in my text editor more useful. One feature I ended up droping was the ability to group directions when specifying constraints. In the original design, it was legal to say "If cannot move left or right or down...". This would not only have been more work for me, but could also annoy programmers if they needed even more parethesis to make this work (an alternative solution would be to create an object for every combinatin of directions, which could significantly affect compile times as there would be about 128 unique objects). | ||
|
||
Finally, one last feature I added was a "runOnMaze" statement that allows the user to specify the map they want to use. As before, this was also kind of forced as I needed some way to know that all the rules had been specified and which map to load. This is something I didn't think about much in the design phase and would have been good to incorporate as it's currently ambiguous what map the code should be run on. | ||
|
||
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. Huh, we didn't think about this, we just put it in when we ran our program. I like that you put some thought into it! |
||
I would like to end by saying that despite all these changes, I was able to presearve most of the spirit of the original DSL. It it much, much easier to express thoughts in my DSL than in the original one. In some ways, I think the internal DSL may be even improved on my original design, even if many of the changes were somewhat forced by Scala. The internal DSL implementation also preserves the compactness and structure of the original design. If you quickly flip between the examples and the actual implementations, it is surprising how close they are to each other. It also keeps many of the cool features from the design, like automatically mapping names to state numbers (it even poroperly handles references to states that haven't been processed yet). It should also support many of the inference capabilities of the original design. If you leave out a state transition, it automatically infers you want to stay in the same state, and so on. | ||
|
||
**On a scale of 1–10 (where 10 is "a lot"), how much did you have to change your syntax?** | ||
|
||
If you count a lot of the "minor" changes due to Scala's syntax, then I might give this a 6 or so. The structure as a whole is very similar, but many of the details had to change (like colons into brakets or the addition of parenthesis since more than 1 parameter was used in my If and WhenIn functions). | ||
|
||
**On a scale of 1–10 (where 10 is "very difficult"), how difficult was it to map your syntax to the provided API?** | ||
|
||
The API itself wasn't much of an issue compared to implementing the rest of the DSL, but it did suck that the starter file provided did not document all of the behavior very well. I had to use some small hacks to work around not knowing many of the types in the API and it took a while to figue out how to actually run applications again (apparently JFXApp already includes App, which was nice for internal DSL code). So, I'd give it about a 3. It didn't get in my way a whole lot and I was able to work around issues relatively fast. | ||
|
||
## External DSL | ||
|
||
_Describe each change from your ideal syntax to the syntax you implemented, and | ||
describe_ why _you made the change._ | ||
|
||
I am proud to say that my external DSL was able to correctly run my original example programs without modification. Well, with one small modification: the external DSL actually revealed two bugs in my original code (I refered to the state moveUp with moveUP, so the names didn't match). After I fixed that, it ran just fine. This is exciting because it means that I was able to get the more "advanced" functionality of the DSL working. So, no quotes around variable names, inference of the start state and state transitions, and chained logic (stuff like "cannot move up or left or right" is equivalent to "cannot move up and cannot move left and cannot move right"). I think having these features (or lack of syntax) is a nice "feature" of my DSL, so it was great to see it working. I'm also happy that I was able to easily get the full natural language syntax working. I think it really helps with readability and ease of use. | ||
|
||
There was one or two things that I didn't implement that may have been expected of the original language. First of all, the lanugage does not support weird cases where you specify no conditions (so, you don't care whether any of the tiles around you are blocked). While this may be desirable if you wanted to exploit the order in which the rules were evaluated, this could also make code harder to follow (since you now have to figure out what you can infer from previous conditions). If you're moving, you should check to make sure you can actually move in that direction. If you're not moving and you're not checking anything around you, then why not elimiate the current state entirely or transition directly to the next state? If you _really_ want to do this, you can always write two rules that accomplish the same task (which is annoying, but still possible). My DSL also doesn't support infering that you do not want to move and you want to transition to the state you're already in. This would create an infinite loop, but it's still possible with my DSL (you just have to explicitly speficy that you're transitioning to the state you're in). Since these are generally behaviours you want to avoid, it's actually kind of a good thing that my DSL doesn't make them easy so that perhaps the programmer will realize that what they are doing is probably a bad idea. And, if they know what they're doing, they can still accomplish what they want. | ||
|
||
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 love that you address the "What is hard to express in this language?" question and give good reasons for why they should be difficult! I agree! |
||
**On a scale of 1–10 (where 10 is "a lot"), how much did you have to change your syntax?** | ||
|
||
One, I guess. As stated above, I had to make zero changes to the code (besides bug fixes). | ||
|
||
**On a scale of 1–10 (where 10 is "very difficult"), how difficult was it to map your syntax to the provided API?** | ||
|
||
Somewhere around 5. It was one of those things that took a long time, but wasn't necessarily hard during the process. Part of why it took so long is because I revised previous code a lot, especially the AST. I originally went for more rules but found I could recycle some stuff from picolib (for instance, instead of creating more classes for directions, I ended up using their MoveDirection type). This was a doubly good move because it meant less converting between AST datatypes and picolib datatypes. It also took long because of the lack of documentation on picolib and JFXApp. I had to figure some things out for myself and spend a lot of time researching how to do things in Scala. | ||
|
||
Another issue I ran into was with specifying how to parse the DSL. I wanted to enforce certain things like newlines or spaces in certain areas, but JavaTokenParsers / PackratParser did not make that easy. While it parses the example code just fine, I would like some reassurance that you can't do weird things like put two rules on one line. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# A picobot program that can solve a maze, using the right-hand rule | ||
|
||
moveUp: # moving up | ||
If can move right, move right and moveRight # we can go right | ||
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. This line is actually kind of confusing because of the "can move right, move right and moveRight" its hard to tell which thing is which and what each one is doing. I'm not sure how you would change the syntax to make it more readable though. |
||
If cannot move right but can move up, move up # we can't go right, try going up | ||
If cannot move up or right, moveDown # we can't go right or up, try turning around | ||
|
||
moveRight: # moving right | ||
If can move down, move down and moveDown # we can go right | ||
If cannot move down but can move right, move right # we can't go right, try going forward | ||
If cannot move right or down, moveLeft # we can't go right or forward, try turning around | ||
|
||
moveLeft: # moving left | ||
If can move up, move up and moveUP # we can go right | ||
If cannot move up but can move left, move left # we can't go right, try going forward (west) | ||
If cannot move up or left, moveRight # we can't go right or forward, try turning around | ||
|
||
moveDown: # moving down | ||
If can move left, move left and moveLeft # we can go right | ||
If cannot move left but can move down, move down # we can't go right, try going forward | ||
If cannot move left or down, moveUP # we can't go right or forward, try turning around |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# A Picobot program that can fill an empty room | ||
|
||
# move to the top left | ||
|
||
moveLeft: | ||
If can move left, move left # go all the way to the left | ||
If cannot move left, moveUp # can't go left anymore, so try to go up | ||
|
||
moveUp: | ||
If can move up, move up # go all the way to the top | ||
If cannot move up and can move down, move down and fillColumnDown # can't go up any more, so try to go down | ||
|
||
|
||
# fill from top to bottom, left to right | ||
|
||
fillColumnDown: # fill this column to the bottom | ||
If can move down, move down # go all the way to the bottom | ||
If cannot move down but can move right, move right and fillColumnUp # can't go down anymore, so try to go right | ||
|
||
fillColumnUp: # fill this column to the top | ||
If can move up, move up # go all the way to the top | ||
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 love the clarity of the state names. They are so much more readable than the numbers states. |
||
If cannot move up but can move right, move right and fillColumnDown # can't go up anymore, so try to go right |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# A Picobot program that can fill an empty room | ||
|
||
# move to the top left | ||
|
||
moveLeft: | ||
If can move left, move left # go all the way to the left | ||
If cannot move left, moveUp # can't go left anymore, so try to go up | ||
|
||
moveUp: | ||
If can move up, move up # go all the way to the top | ||
If cannot move up and can move down, move down and fillColumnDown # can't go up any more, so try to go down | ||
|
||
|
||
# fill from top to bottom, left to right | ||
|
||
fillColumnDown: # fill this column to the bottom | ||
If can move down, move down # go all the way to the bottom | ||
If cannot move down but can move right, move right and fillColumnUp # can't go down anymore, so try to go right | ||
|
||
fillColumnUp: # fill this column to the top | ||
If can move up, move up # go all the way to the top | ||
If cannot move up but can move right, move right and fillColumnDown # can't go up anymore, so try to go right |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package piconot.external | ||
|
||
import piconot.external.PicoDSL.interpreter.semantics | ||
import piconot.external.PicoDSL.parser.PicoParser | ||
import picolib.maze.Maze | ||
import picolib.semantics._ | ||
import scalafx.application.JFXApp | ||
import scala.io.Source | ||
|
||
|
||
object Piconot { | ||
def main(args: Array[String]): Unit = { | ||
// needs a path to a map and a code file | ||
if (args.length < 2) {println("Not enough args"); return} | ||
|
||
// extract args | ||
val mapPath:String = args(0) | ||
val filePath:String = args(1) | ||
|
||
// read file into string, parse, and interpret | ||
val code: String = Source.fromFile(filePath).mkString | ||
val parse = PicoParser.apply(code) | ||
if (!parse.successful) {println(parse); return} // display parsing error (if any) | ||
val rules: List[Rule] = semantics.eval(parse.get) | ||
|
||
// run rules using picolib | ||
val emptyMaze = Maze(mapPath) | ||
object helper extends JFXApp { | ||
object EmptyBot extends Picobot(emptyMaze, rules) with TextDisplay with GUIDisplay | ||
stage = EmptyBot.mainStage | ||
EmptyBot.run() | ||
} | ||
helper.main(Array()) | ||
} | ||
} |
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.
When we looked at the API, it seemed like you could have more than 100 states, it was just in the DSL that CS5 used to write their codes that you were limited to 100 possible states.