~mna/snow

ref: c0bc63be93dc0ff086a2703f5a03e4f82681c76c snow/pkg/compiler/printer.go -rw-r--r-- 2.9 KiB
c0bc63beMartin Angers pkg/compiler: simple statements, more tests 1 year, 11 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package compiler

import (
	"fmt"
	gotoken "go/token"
	"io"
	"strconv"
	"strings"
)

type printer struct {
	w            io.Writer
	file         *gotoken.File
	depth        int
	withPos      bool // print start and end position in [startoffset:endoffset] format
	longPos      bool // print position in filename:startline:col:endline:col format
	withComments bool

	err error
}

func (p *printer) Print(n node, file *gotoken.File) error {
	p.file = file
	walk(p, n)
	return p.err
}

func (p *printer) printMsg(s string, indent bool) {
	if p.err != nil {
		return
	}
	repeat := p.depth - 1
	if !indent {
		repeat = 0
	}
	_, p.err = fmt.Fprintf(p.w, "%s%s", strings.Repeat("  ", repeat), s)
}

func (p *printer) printPosln(n node) {
	if p.err != nil {
		return
	}

	var startLbl, endLbl string
	start, end := n.Pos(), n.End()
	if !p.longPos {
		startLbl, endLbl = "-", "-"
		if start.IsValid() {
			startLbl = strconv.Itoa(p.file.Offset(start))
		}
		if end.IsValid() {
			endLbl = strconv.Itoa(p.file.Offset(end))
		}
		_, p.err = fmt.Fprintf(p.w, " [%s:%s]\n", startLbl, endLbl)
		return
	}

	if start.IsValid() {
		lstart := p.file.Position(start)
		startLbl = fmt.Sprintf("%s:%d:%d", lstart.Filename, lstart.Line, lstart.Column)
	} else {
		startLbl = fmt.Sprintf("%s:-:-", p.file.Name())
	}
	if end.IsValid() {
		lend := p.file.Position(end)
		endLbl = fmt.Sprintf("%d:%d", lend.Line, lend.Column)
	} else {
		endLbl = "-:-"
	}
	_, p.err = fmt.Fprintf(p.w, " [%s::%s]\n", startLbl, endLbl)
}

func (p *printer) Visit(n node) visitor {
	if n == nil || p.err != nil {
		p.depth--
		return nil
	}
	p.depth++

	switch n := n.(type) {
	case *file:
		p.printMsg("file", true)
	case *fnDef:
		p.printMsg("fn", true)
	case *fnSig:
		p.printMsg("sig", true)
	case *paramDef:
		p.printMsg("param", true)
	case *block:
		p.printMsg("block", true)
	case *varDef:
		lbl := n.kw.String()
		if n.colon.IsValid() {
			lbl += ":"
		}
		if n.assign.IsValid() {
			lbl += "="
		}
		p.printMsg(lbl, true)
	case *returnStmt:
		p.printMsg("return", true)
	case *binaryExpr:
		p.printMsg(fmt.Sprintf("binary [%s]", n.op), true)
	case *unaryExpr:
		p.printMsg(fmt.Sprintf("unary [%s]", n.op), true)
	case *parenExpr:
		p.printMsg("paren", true)
	case *assignStmt:
		p.printMsg("assign", true)
	case *exprStmt:
		p.printMsg("expr", true)
	case *basicLit:
		p.printMsg(fmt.Sprintf("%s [%s]", n.literal, n.value), true)
	case *ident:
		p.printMsg(fmt.Sprintf("ident [%s]", n.name), true)
	case *badStmt:
		p.printMsg("bad stmt", true)
	case *badExpr:
		p.printMsg("bad expr", true)
	case *commentGroup:
		if !p.withComments {
			return nil
		}
		p.printMsg("comments", true)
	case *comment:
		if !p.withComments {
			return nil
		}
		p.printMsg(fmt.Sprintf("comment [%s]", n.text), true)

	default:
		panic(fmt.Sprintf("printer: unexpected node type %T", n))
	}

	if p.withPos {
		p.printPosln(n)
	} else {
		p.printMsg("\n", false)
	}

	return p
}