weixin_39624774
weixin_39624774
2020-12-25 20:50

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, rt and lt by (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_whitespace to 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_programs directory. Creativity is encouraged so please don't hesitate to make your own! The more interesting, the better.
  • [ ] Support repeat command 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 if command (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 because if should 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.

该提问来源于开源项目:sunjay/turtle

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

4条回答

  • weixin_39624389 weixin_39624389 4月前

    I would like to work on this issue.

    点赞 评论 复制链接分享
  • weixin_39624774 weixin_39624774 4月前

    Please do! Let me know on Zulip or in the comments here if you have any questions. :)

    点赞 评论 复制链接分享
  • weixin_39624774 weixin_39624774 4月前

    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:

    rust
    //! 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) {
        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.

    点赞 评论 复制链接分享
  • weixin_39624389 weixin_39624389 4月前

    I made the necessary changes you suggested and made a PR #43 .

    点赞 评论 复制链接分享

相关推荐