Skip to content

Latest commit

 

History

History
112 lines (102 loc) · 3.65 KB

File metadata and controls

112 lines (102 loc) · 3.65 KB

RPPL

到目前为止,我们的 REPL 更像是一个 RLPL,一个 read-lex-print-loop。 我们还不知道如何评估代码,所以用“evaluate”替换“lex”仍然是不可能的。 但我们现在最肯定知道的是解析。 是时候用“parse”替换“lex”并构建一个RPPL。

// repl/repl.go

func Start(in io.Reader, out io.Writer) {
	scanner := bufio.NewScanner(in)

	for {
		fmt.Printf(PROMPT)
		scanned := scanner.Scan()
		if !scanned {
			return
		}

		line := scanner.Text()
		l := lexer.New(line)
		p := parser.New(l)

		program := p.ParseProgram()
		if len(p.Errors()) != 0 {
			printParserErrors(out, p.Errors())
			continue
		}

		io.WriteString(out, program.String())
		io.WriteString(out, "\n")
	}
}

func printParserErrors(out io.Writer, errors []string) {
    for _, msg := range errors {
        io.WriteString(out, "\t"+msg+"\n")
    }
}

在这里,我们扩展循环以解析我们刚刚在 REPL 中输入的行。 然后通过调用其 String 方法打印解析器的输出 *ast.Program,该方法递归调用属于该程序的所有语句的 String 方法。 现在我们可以使用解析器进行旋转 - 在命令行上交互:

$ go run main.go
Hello mrnugget! This is the Monkey programming language!
Feel free to type in commands
>> let x = 1 * 2 * 3 * 4 * 5
let x = ((((1 * 2) * 3) * 4) * 5);
>> x * y / 2 + 3 * 8 - 123
((((x * y) / 2) + (3 * 8)) - 123)
>> true == false
(true == false)
>>

很好! 现在,我们可以使用任何基于字符串的 AST 表示来输出,而不是调用 String。 我们可以添加一个 PrettyPrint 方法来打印 AST 节点的类型并正确地指定其子节点,或者我们可以使用 ASCII 颜色代码,或者我们可以打印 ASCII 图形,或者……重点是:天空是极限。 但是我们的 RPPL 仍然有一个巨大的缺点。

这是解析器遇到错误:

$ go run main.go
Hello mrnugget! This is the Monkey programming language!
Feel free to type in commands
>> let x 12 * 3;
        expected next token to be =, got INT instead
>>

这不是一个很好的错误信息。 我的意思是,它可以完成工作,是的,但它不是很好,是吗? 这Monkey 编程语言值得更好的。 这是一个更用户友好的 printParseError增强用户体验的功能:

// repl/repl.go

const MONKEY_FACE = `            __,__
   .--.  .-"     "-.  .--.
  / .. \/  .-. .-.  \/ .. \
 | |  '|  /   Y   \  |'  | |
 | \   \  \ 0 | 0 /  /   / |
  \ '- ,\.-"""""""-./, -' /
   ''-' /_   ^ ^   _\ '-''
       |  \._   _./  |
       \   \ '~' /   /
        '._ '-=-' _.'
           '-----'
`

func printParserErrors(out io.Writer, errors []string) {
	io.WriteString(out, MONKEY_FACE)
	io.WriteString(out, "Woops! We ran into some monkey business here!\n")
	io.WriteString(out, " parser errors:\n")
	for _, msg := range errors {
		io.WriteString(out, "\t"+msg+"\n")
	}
}

这样更好! 如果我们现在遇到任何解析器错误,我们就会看到一只猴子,这真的超出了任何人的要求:

$ go run main.go
Hello mrnugget! This is the Monkey programming language!
Feel free to type in commands
>> let x 12 * 3
            __,__
   .--.  .-"     "-.  .--.
  / .. \/  .-. .-.  \/ .. \
 | |  '|  /   Y   \  |'  | |
 | \   \  \ 0 | 0 /  /   / |
  \ '- ,\.-"""""""-./, -' /
   ''-' /_   ^ ^   _\ '-''
       |  \._   _./  |
       \   \ '~' /   /
        '._ '-=-' _.'
           '-----'
Woops! We ran into some monkey business here!
    parser errors:
        expected next token to be =, got INT instead
>>

转念一想……无论如何,是时候开始评估我们的 AST 了。

< 2.8扩展解析器 > 3.1赋予符号意义