Example Idea: Logo Interpreter
This library and other libraries like it in other languages are based on the LOGO programming language. It would be really cool if we could have an example that interprets logo commands.
Logo Reference: http://www.logointerpreter.com/logo-reference/turtle-graphics-basic-motion.php Logo Tutorial: http://cs.brown.edu/courses/bridge/1997/Resources/LogoTutorial.html
The example could either accept input or run a file depending on the command line arguments provided. Feel free to make this as fancy or simple as you want!
Progress
- [x] Initial example implementation with
fd,bk,rtandltby @Jay9596 (https://github.com/sunjay/turtle/pull/43)
Suggested Future Enhancements
Please ask questions in the comments or on Zulip if you need more help with any of this!
For every feature in this list, make sure you submit an example (in the examples/sample_logo_programs directory) to go with what you implemented so we can check that it works.
- [ ] Support comments on their own line or following a line in the program
Instructions: Before we use
split_whitespaceto split the line into its arguments, split by the;character up to one time and use the first half as the line to parse and evaluate. - [ ] Support more commands Instructions: See the logo reference for a complete list of the commands that you could implement. Feel free to do as many as you can or as few as you want.
- [ ] Write/copy example logo programs
Instructions: Find good, informative logo sample programs and add them to the
examples/sample_logo_programsdirectory. Creativity is encouraged so please don't hesitate to make your own! The more interesting, the better. - [ ] Support
repeatcommand and other related commands (see tutorial for an example) Instructions: Add a repeat command in a similar way to how other commands are implemented, but figure out how to parse instruction lists (ask if you are not sure how to approach this) - [ ] Support
ifcommand (see tutorial for an example) Instructions: Add an if command in a similar way to how other commands are implemented. You will need to split up parsing and evaluation into two separate functions becauseifshould not evaluate its body if its condition is false. Try to avoid parsing more than necessary (don't parse the entire file at once). To accomplish this, you will need to make sure of Iterators or some other related concept.
I would like to work on this issue.
Please do! Let me know on Zulip or in the comments here if you have any questions. :)
@Jay9596 Thanks for working on this! If it's alright with you, I have a few suggestions for this code. :smile:
As I mentioned in chat, please move this code to an example (e.g. examples/logo_interpreter.rs).
To make it as easy as possible for you, I highly recommend that you start by structuring your code so that it looks something like this:
//! To run a LOGO program, run:
//! cargo run --example logo_interpreter < my_logo_program.txt
extern crate turtle;
use std::io::{self, BufRead, BufReader};
use std::collections::VecDeque;
use turtle::Turtle;
fn main() {
let mut turtle = Turtle::new();
let stdin = io::stdin();
let mut reader = BufReader::new(stdin);
loop {
let mut buffer = String::new();
let read_bytes = reader.read_line(&mut buffer)
.expect("Unable read input from stdin");
if read_bytes == 0 {
// Reached EOF, stop interpreter
break;
}
let mut cmd_args = buffer.split_whitespace().collect();
handle_command(&mut turtle, &mut cmd_args);
}
}
fn handle_command(turtle: &mut Turtle, args: &mut VecDeque<&str>) {
if args.is_empty() {
return;
}
// We already checked if args is empty, so we can unwrap here without fear
match args.pop_front().unwrap() {
"fd" | "forward" => {
let distance = parse_distance(args.pop_front()
.expect("Expected a distance value after fd/forward command"));
turtle.forward(distance)
},
_ => unimplemented!(), //TODO: a couple more commands (no need to do everything)
}
// Parse any remaining commands on this line
handle_command(turtle, args);
}
fn parse_distance(s: &str) -> f64 {
unimplemented!(); //TODO: Parse a distance and panic if it isn't valid
}
This is a fairly simple starting point with the simplest possible error handling (just panic and abort on every error). Structuring it this way allows you to figure out command parsing without worrying about reading command line arguments, parsing files, etc. I would really appreciate it if this example could implemented as a series of small PRs. This could be your first PR, and then you could incrementally improve on the interpreters in future PRs. That way, if you no longer have time to work on this or if someone else wants to pickup a task, others can help out as well.
I made the necessary changes you suggested and made a PR #43 .