在Go语言中的Scheme解释器

huangapple go评论91阅读模式
英文:

Scheme interpreter in Go

问题

我是你的中文翻译助手,以下是翻译好的内容:

我是一个相当基础的Go程序员,最近在研究一个小的Scheme解释器,试图理解它的工作原理。

我在这里找到了它:
https://pkelchte.wordpress.com/2013/12/31/scm-go/

我阅读了网页,但仍然难以理解它的工作原理,因为源代码显然是由一个比我更熟悉Go的人编写的。

特别是有这些行让我难以理解:

e := expression.(type) // 第73行

我不确定.(type)部分是什么意思,我以为它是类型转换,但它看起来不像我之前见过的类型转换。

switch p := procedure.(type) {
case func(...scmer) scmer:
	value = p(args...)
case proc:
	en := &env{make(vars), p.en}
	switch params := p.params.(type) {
	case []scmer:
		for i, param := range params {
			en.vars[param.(symbol)] = args[i]
		}
	default:
		en.vars[params.(symbol)] = args
	}
	value = eval(p.body, en)

我实际上不太理解这段代码。第73至86行。

*tokens = (*tokens)[1:] // 第208行

由于它的奇怪语法,我不确定这行代码的意思。我知道它涉及指针,并且括号是因为*。但我不确定这行代码在做什么。

最后是这些行:

token := (*tokens)[0]
*tokens = (*tokens)[1:]
switch token {
case "(": //列表开始
	L := make([]scmer, 0)
	for (*tokens)[0] != ")" {
		if i := readFrom(tokens); i != symbol("") {
			L = append(L, i)
		}
	}
	*tokens = (*tokens)[1:]
	return L

我也不知道这些行的作用。第198至209行。

如果你需要完整的代码,这是它的完整代码。我知道它有250行,但我希望能尽可能多地解释它的功能。

/*
 * A minimal Scheme interpreter, as seen in lis.py and SICP
 * http://norvig.com/lispy.html
 * http://mitpress.mit.edu/sicp/full-text/sicp/book/node77.html
 *
 * Pieter Kelchtermans 2013
 * LICENSE: WTFPL 2.0
 */
package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"reflect"
	"strconv"
	"strings"
)

func main() {
	Repl()
}

/*
 Eval / Apply
*/

func eval(expression scmer, en *env) (value scmer) {
	switch e := expression.(type) {
	case number:
		value = e
	case symbol:
		value = en.Find(e).vars[e]
	case []scmer:
		switch car, _ := e[0].(symbol); car {
		case "quote":
			value = e[1]
		case "if":
			if eval(e[1], en).(bool) {
				value = eval(e[2], en)
			} else {
				value = eval(e[3], en)
			}
		case "set!":
			v := e[1].(symbol)
			en.Find(v).vars[v] = eval(e[2], en)
			value = "ok"
		case "define":
			en.vars[e[1].(symbol)] = eval(e[2], en)
			value = "ok"
		case "lambda":
			value = proc{e[1], e[2], en}
		case "begin":
			for _, i := range e[1:] {
				value = eval(i, en)
			}
		default:
			operands := e[1:]
			values := make([]scmer, len(operands))
			for i, x := range operands {
				values[i] = eval(x, en)
			}
			value = apply(eval(e[0], en), values)
		}
	default:
		log.Println("Unknown expression type - EVAL", e)
	}
	return
}

func apply(procedure scmer, args []scmer) (value scmer) {
	switch p := procedure.(type) {
	case func(...scmer) scmer:
		value = p(args...)
	case proc:
		en := &env{make(vars), p.en}
		switch params := p.params.(type) {
		case []scmer:
			for i, param := range params {
				en.vars[param.(symbol)] = args[i]
			}
		default:
			en.vars[params.(symbol)] = args
		}
		value = eval(p.body, en)
	default:
		log.Println("Unknown procedure type - APPLY", p)
	}
	return
}

type proc struct {
	params, body scmer
	en           *env
}

/*
 Environments
*/

type vars map[symbol]scmer
type env struct {
	vars
	outer *env
}

func (e *env) Find(s symbol) *env {
	if _, ok := e.vars
展开收缩
; ok { return e } else { return e.outer.Find(s) } } /* Primitives */ var globalenv env func init() { globalenv = env{ vars{ //aka an incomplete set of compiled-in functions "+": func(a ...scmer) scmer { v := a[0].(number) for _, i := range a[1:] { v += i.(number) } return v }, "-": func(a ...scmer) scmer { v := a[0].(number) for _, i := range a[1:] { v -= i.(number) } return v }, "*": func(a ...scmer) scmer { v := a[0].(number) for _, i := range a[1:] { v *= i.(number) } return v }, "/": func(a ...scmer) scmer { v := a[0].(number) for _, i := range a[1:] { v /= i.(number) } return v }, "<=": func(a ...scmer) scmer { return a[0].(number) <= a[1].(number) }, "equal?": func(a ...scmer) scmer { return reflect.DeepEqual(a[0], a[1]) }, "cons": func(a ...scmer) scmer { switch car := a[0]; cdr := a[1].(type) { case []scmer: return append([]scmer{car}, cdr...) default: return []scmer{car, cdr} } }, "car": func(a ...scmer) scmer { return a[0].([]scmer)[0] }, "cdr": func(a ...scmer) scmer { return a[0].([]scmer)[1:] }, "list": eval(read( "(lambda z z)"), &globalenv), }, nil} } /* Parsing */ //symbols, numbers, expressions, procedures, lists, ... all implement this interface, which enables passing them along in the interpreter type scmer interface{} type symbol string //symbols are represented by strings type number float64 //numbers by float64 func read(s string) (expression scmer) { tokens := tokenize(s) return readFrom(&tokens) } //Syntactic Analysis func readFrom(tokens *[]string) (expression scmer) { //pop first element from tokens token := (*tokens)[0] *tokens = (*tokens)[1:] switch token { case "(": //a list begins L := make([]scmer, 0) for (*tokens)[0] != ")" { if i := readFrom(tokens); i != symbol("") { L = append(L, i) } } *tokens = (*tokens)[1:] return L default: //an atom occurs if f, err := strconv.ParseFloat(token, 64); err == nil { return number(f) } else { return symbol(token) } } } //Lexical Analysis func tokenize(s string) []string { return strings.Split( strings.Replace(strings.Replace(s, "(", "( ", -1), ")", " )", -1), " ") } /* Interactivity */ func String(v scmer) string { switch v := v.(type) { case []scmer: l := make([]string, len(v)) for i, x := range v { l[i] = String(x) } return "(" + strings.Join(l, " ") + ")" default: return fmt.Sprint(v) } } func Repl() { scanner := bufio.NewScanner(os.Stdin) for fmt.Print("> "); scanner.Scan(); fmt.Print("> ") { fmt.Println("==>", String(eval(read(scanner.Text()), &globalenv))) } }

希望对你有帮助!如果有任何其他问题,请随时提问。

英文:

I'm quite a basic Go programmer and I've been taking a look at this small Scheme interpreter and I've been trying to understand how it works.

I found it here:
https://pkelchte.wordpress.com/2013/12/31/scm-go/

I read the webpage, but I'm still struggling to understand how it works because the source code is obviously written by someone who's a lot more familiar with Go than I am.

Particularly there's these lines that I'm struggling to understand:

e := expression.(type) // Line 73

I'm not sure what the .(type) part means, I thought it was casting but it doesn't look like the casting I've seen before.

switch p := procedure.(type) {
case func(...scmer) scmer:
	value = p(args...)
case proc:
	en := &amp;env{make(vars), p.en}
	switch params := p.params.(type) {
	case []scmer:
		for i, param := range params {
			en.vars[param.(symbol)] = args[i]
		}
	default:
		en.vars[params.(symbol)] = args
	}
	value = eval(p.body, en)

I don't really understand any of this code to be honest. Lines 73 - 86

*tokens = (*tokens)[1:] // Line 208

I'm not sure what this line means due to it's weird syntax. I get that its pointers and that the parenthesises are because of the *. But I'm not sure what that lines doing.

Finally there's these lines:

token := (*tokens)[0]
*tokens = (*tokens)[1:]
switch token {
case &quot;(&quot;: //a list begins
	L := make([]scmer, 0)
	for (*tokens)[0] != &quot;)&quot; {
		if i := readFrom(tokens); i != symbol(&quot;&quot;) {
			L = append(L, i)
		}
	}
	*tokens = (*tokens)[1:]
	return L

I don't know what these lines do either. Lines 198 - 209

Here's the complete code if you want it, I realise it's 250 lines long but I'd really appreciate as many explanations about what its doing as possible.

/*
 * A minimal Scheme interpreter, as seen in lis.py and SICP
 * http://norvig.com/lispy.html
 * http://mitpress.mit.edu/sicp/full-text/sicp/book/node77.html
 *
 * Pieter Kelchtermans 2013
 * LICENSE: WTFPL 2.0
 */
package main

import (
	&quot;bufio&quot;
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;os&quot;
	&quot;reflect&quot;
	&quot;strconv&quot;
	&quot;strings&quot;
)

func main() {
	Repl()
}

/*
 Eval / Apply
*/

func eval(expression scmer, en *env) (value scmer) {
	switch e := expression.(type) {
	case number:
		value = e
	case symbol:
		value = en.Find(e).vars[e]
	case []scmer:
		switch car, _ := e[0].(symbol); car {
		case &quot;quote&quot;:
			value = e[1]
		case &quot;if&quot;:
			if eval(e[1], en).(bool) {
				value = eval(e[2], en)
			} else {
				value = eval(e[3], en)
			}
		case &quot;set!&quot;:
			v := e[1].(symbol)
			en.Find(v).vars[v] = eval(e[2], en)
			value = &quot;ok&quot;
		case &quot;define&quot;:
			en.vars[e[1].(symbol)] = eval(e[2], en)
			value = &quot;ok&quot;
		case &quot;lambda&quot;:
			value = proc{e[1], e[2], en}
		case &quot;begin&quot;:
			for _, i := range e[1:] {
				value = eval(i, en)
			}
		default:
			operands := e[1:]
			values := make([]scmer, len(operands))
			for i, x := range operands {
				values[i] = eval(x, en)
			}
			value = apply(eval(e[0], en), values)
		}
	default:
		log.Println(&quot;Unknown expression type - EVAL&quot;, e)
	}
	return
}

func apply(procedure scmer, args []scmer) (value scmer) {
	switch p := procedure.(type) {
	case func(...scmer) scmer:
		value = p(args...)
	case proc:
		en := &amp;env{make(vars), p.en}
		switch params := p.params.(type) {
		case []scmer:
			for i, param := range params {
				en.vars[param.(symbol)] = args[i]
			}
		default:
			en.vars[params.(symbol)] = args
		}
		value = eval(p.body, en)
	default:
		log.Println(&quot;Unknown procedure type - APPLY&quot;, p)
	}
	return
}

type proc struct {
	params, body scmer
	en           *env
}

/*
 Environments
*/

type vars map[symbol]scmer
type env struct {
	vars
	outer *env
}

func (e *env) Find(s symbol) *env {
	if _, ok := e.vars
展开收缩
; ok { return e } else { return e.outer.Find(s) } } /* Primitives */ var globalenv env func init() { globalenv = env{ vars{ //aka an incomplete set of compiled-in functions &quot;+&quot;: func(a ...scmer) scmer { v := a[0].(number) for _, i := range a[1:] { v += i.(number) } return v }, &quot;-&quot;: func(a ...scmer) scmer { v := a[0].(number) for _, i := range a[1:] { v -= i.(number) } return v }, &quot;*&quot;: func(a ...scmer) scmer { v := a[0].(number) for _, i := range a[1:] { v *= i.(number) } return v }, &quot;/&quot;: func(a ...scmer) scmer { v := a[0].(number) for _, i := range a[1:] { v /= i.(number) } return v }, &quot;&lt;=&quot;: func(a ...scmer) scmer { return a[0].(number) &lt;= a[1].(number) }, &quot;equal?&quot;: func(a ...scmer) scmer { return reflect.DeepEqual(a[0], a[1]) }, &quot;cons&quot;: func(a ...scmer) scmer { switch car := a[0]; cdr := a[1].(type) { case []scmer: return append([]scmer{car}, cdr...) default: return []scmer{car, cdr} } }, &quot;car&quot;: func(a ...scmer) scmer { return a[0].([]scmer)[0] }, &quot;cdr&quot;: func(a ...scmer) scmer { return a[0].([]scmer)[1:] }, &quot;list&quot;: eval(read( &quot;(lambda z z)&quot;), &amp;globalenv), }, nil} } /* Parsing */ //symbols, numbers, expressions, procedures, lists, ... all implement this interface, which enables passing them along in the interpreter type scmer interface{} type symbol string //symbols are represented by strings type number float64 //numbers by float64 func read(s string) (expression scmer) { tokens := tokenize(s) return readFrom(&amp;tokens) } //Syntactic Analysis func readFrom(tokens *[]string) (expression scmer) { //pop first element from tokens token := (*tokens)[0] *tokens = (*tokens)[1:] switch token { case &quot;(&quot;: //a list begins L := make([]scmer, 0) for (*tokens)[0] != &quot;)&quot; { if i := readFrom(tokens); i != symbol(&quot;&quot;) { L = append(L, i) } } *tokens = (*tokens)[1:] return L default: //an atom occurs if f, err := strconv.ParseFloat(token, 64); err == nil { return number(f) } else { return symbol(token) } } } //Lexical Analysis func tokenize(s string) []string { return strings.Split( strings.Replace(strings.Replace(s, &quot;(&quot;, &quot;( &quot;, -1), &quot;)&quot;, &quot; )&quot;, -1), &quot; &quot;) } /* Interactivity */ func String(v scmer) string { switch v := v.(type) { case []scmer: l := make([]string, len(v)) for i, x := range v { l[i] = String(x) } return &quot;(&quot; + strings.Join(l, &quot; &quot;) + &quot;)&quot; default: return fmt.Sprint(v) } } func Repl() { scanner := bufio.NewScanner(os.Stdin) for fmt.Print(&quot;&gt; &quot;); scanner.Scan(); fmt.Print(&quot;&gt; &quot;) { fmt.Println(&quot;==&gt;&quot;, String(eval(read(scanner.Text()), &amp;globalenv))) } }

答案1

得分: 3

第一个是类型切换。你可以在这里阅读更多关于它的内容:链接

第二个是像Python中的切片操作(如果你了解的话,应该很熟悉):

ls = ls[1:]

我们跳过了第一个元素,取出了切片/列表的剩余部分。

从你的评论中我看出,我认为这不是一个好的起点来尝试使用Go语言。

英文:

First one is type switch. You can read about it more here.

Second one is slicing like in python (if you know it, should be familiar)

ls = ls[1:]

We're skipping first item and taking the rest of the slice/list.

As I see from your comments, I think it's not a good starting point to experiment with golang.

答案2

得分: 2

e := expression.(type) 是一个类型断言

switch p := procedure.(type) {(后面跟着的case语句)是一个类型切换

*tokens = (*tokens)[1:] 是一个切片表达式,用于改变*tokens中存储的值。

token := (*tokens)[0]       // 将token设置为tokens切片的第一个元素。
*tokens = (*tokens)[1:]     // 更新tokens,移除第一个元素。
switch token {              // 根据token进行切换。
case "(": //列表开始   // 如果token是"("
    L := make([]scmer, 0)       // 创建一个新的空切片。
    for (*tokens)[0] != ")" {   // 当tokens的第一个元素不是")"时:
                                    // 从tokens中读取;如果不是空符号:
        if i := readFrom(tokens); i != symbol("") {
            L = append(L, i)        // 将读取的标记(存储在i中)添加到L中。
        }
    }
    *tokens = (*tokens)[1:]     // 从tokens中移除第一个元素。
    return L                    // 返回新创建的切片。
英文:

e := expression.(type) is a type assertion.

switch p := procedure.(type) { (with the following case statements) is a type switch.

*tokens = (*tokens)[1:] is a slice expression changing the value stored in *tokens.

token := (*tokens)[0]       // Set token to first element of tokens slice.
*tokens = (*tokens)[1:]     // Update tokens to remove first element.
switch token {              // Switch on token.
case &quot;(&quot;: //a list begins   // If token is &quot;(&quot;
    L := make([]scmer, 0)       // Make a new, empty slice.
    for (*tokens)[0] != &quot;)&quot; {   // While the first element of tokens is not &quot;)&quot;:
                                    // Read from tokens; if not empty symbol:
        if i := readFrom(tokens); i != symbol(&quot;&quot;) {
            L = append(L, i)        // Add the token read (in i) to L.
        }
    }
    *tokens = (*tokens)[1:]     // Remove the first element from tokens.
    return L                    // Return the newly created slice.

huangapple
  • 本文由 发表于 2015年7月8日 05:26:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/31279557.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定