Skip to content

Commit 9317db0

Browse files
committed
added NULL token for missing fields like function name, start of Serialize() for several ast types
1 parent 287b052 commit 9317db0

File tree

11 files changed

+136
-31
lines changed

11 files changed

+136
-31
lines changed

Diff for: ast/ast.go

+82-10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package ast
55

66
import (
7+
"fmt"
78
"io"
89
"strconv"
910
"strings"
@@ -24,6 +25,7 @@ type PrintState struct {
2425

2526
func DebugString(n Node) string {
2627
ps := NewPrintState()
28+
ps.Compact = true
2729
n.PrettyPrint(ps)
2830
return ps.String()
2931
}
@@ -68,6 +70,7 @@ func (ps *PrintState) Print(str ...string) *PrintState {
6870
type Node interface {
6971
Value() *token.Token
7072
PrettyPrint(ps *PrintState) *PrintState
73+
Serialize(out io.Writer)
7174
}
7275

7376
// Common to all nodes that have a token and avoids repeating the same TokenLiteral() methods.
@@ -79,6 +82,17 @@ func (b Base) Value() *token.Token {
7982
return b.Token
8083
}
8184

85+
func (b Base) Serialize(out io.Writer) {
86+
if b.Token == nil {
87+
panic(fmt.Sprintf("Serialize called on nil token in base %#v", b))
88+
}
89+
log.Debugf("Serializing %#v", b.Token)
90+
_, _ = out.Write([]byte{b.Code()})
91+
if b.HasContent() {
92+
_, _ = out.Write([]byte(b.Literal()))
93+
}
94+
}
95+
8296
func (b Base) PrettyPrint(ps *PrintState) *PrintState {
8397
// In theory should only be called for literals.
8498
// log.Debugf("PrettyPrint on base called for %T", b.Value())
@@ -99,6 +113,13 @@ func (rs ReturnStatement) PrettyPrint(ps *PrintState) *PrintState {
99113
return ps
100114
}
101115

116+
func (rs ReturnStatement) Serialize(out io.Writer) {
117+
rs.Base.Serialize(out)
118+
if rs.ReturnValue != nil {
119+
rs.ReturnValue.Serialize(out)
120+
}
121+
}
122+
102123
type Statements struct {
103124
Base
104125
Statements []Node
@@ -185,13 +206,14 @@ func (p Statements) PrettyPrint(ps *PrintState) *PrintState {
185206
return ps
186207
}
187208

188-
type Identifier struct {
189-
Base
209+
func (p Statements) Serialize(out io.Writer) {
210+
for _, s := range p.Statements {
211+
s.Serialize(out)
212+
}
190213
}
191214

192-
func (i Identifier) PrettyPrint(out *PrintState) *PrintState {
193-
out.Print(i.Literal())
194-
return out
215+
type Identifier struct {
216+
Base
195217
}
196218

197219
type Comment struct {
@@ -200,11 +222,6 @@ type Comment struct {
200222
SameLineAsNext bool
201223
}
202224

203-
func (c Comment) PrettyPrint(out *PrintState) *PrintState {
204-
out.Print(c.Literal())
205-
return out
206-
}
207-
208225
type IntegerLiteral struct {
209226
Base
210227
Val int64
@@ -244,6 +261,11 @@ func (p PrefixExpression) PrettyPrint(out *PrintState) *PrintState {
244261
return out
245262
}
246263

264+
func (p PrefixExpression) Serialize(out io.Writer) {
265+
p.Base.Serialize(out)
266+
p.Right.Serialize(out)
267+
}
268+
247269
type PostfixExpression struct {
248270
Base
249271
Prev *token.Token
@@ -291,6 +313,12 @@ func (i InfixExpression) PrettyPrint(out *PrintState) *PrintState {
291313
return out
292314
}
293315

316+
func (i InfixExpression) Serialize(out io.Writer) {
317+
i.Base.Serialize(out)
318+
i.Left.Serialize(out)
319+
i.Right.Serialize(out)
320+
}
321+
294322
type Boolean struct {
295323
Base
296324
Val bool
@@ -333,6 +361,16 @@ func (ie IfExpression) PrettyPrint(out *PrintState) *PrintState {
333361
return out
334362
}
335363

364+
func (ie IfExpression) Serialize(out io.Writer) {
365+
ie.Base.Serialize(out)
366+
ie.Condition.Serialize(out)
367+
ie.Consequence.Serialize(out)
368+
if ie.Alternative != nil {
369+
_, _ = out.Write([]byte{token.ELSET.Code()})
370+
ie.Alternative.Serialize(out)
371+
}
372+
}
373+
336374
func PrintList(out *PrintState, list []Node, sep string) {
337375
for i, p := range list {
338376
if i > 0 {
@@ -356,6 +394,13 @@ func (b Builtin) PrettyPrint(out *PrintState) *PrintState {
356394
return out
357395
}
358396

397+
func (b Builtin) Serialize(out io.Writer) {
398+
b.Base.Serialize(out)
399+
for _, p := range b.Parameters {
400+
p.Serialize(out)
401+
}
402+
}
403+
359404
type FunctionLiteral struct {
360405
Base // The 'func' token
361406
Name *Identifier
@@ -380,6 +425,25 @@ func (fl FunctionLiteral) PrettyPrint(out *PrintState) *PrintState {
380425
return out
381426
}
382427

428+
var nullCode = []byte{token.NULLT.Code()}
429+
430+
func writeNull(out io.Writer) {
431+
_, _ = out.Write(nullCode)
432+
}
433+
434+
func (fl FunctionLiteral) Serialize(out io.Writer) {
435+
fl.Base.Serialize(out)
436+
if fl.Name != nil {
437+
fl.Name.Serialize(out)
438+
} else {
439+
writeNull(out)
440+
}
441+
for _, p := range fl.Parameters {
442+
p.Serialize(out)
443+
}
444+
fl.Body.Serialize(out)
445+
}
446+
383447
type CallExpression struct {
384448
Base // The '(' token
385449
Function Node // Identifier or FunctionLiteral
@@ -397,6 +461,14 @@ func (ce CallExpression) PrettyPrint(out *PrintState) *PrintState {
397461
return out
398462
}
399463

464+
func (ce CallExpression) Serialize(out io.Writer) {
465+
ce.Base.Serialize(out)
466+
ce.Function.Serialize(out)
467+
for _, p := range ce.Arguments {
468+
p.Serialize(out)
469+
}
470+
}
471+
400472
type ArrayLiteral struct {
401473
Base // The [ token
402474
Elements []Node

Diff for: eval/eval_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ func TestFunctionObject(t *testing.T) {
337337
if ast.DebugString(fn.Parameters[0]) != "x" {
338338
t.Fatalf("parameter is not 'x'. got=%q", fn.Parameters[0])
339339
}
340-
expectedBody := "x + 2\n"
340+
expectedBody := "x+2"
341341
got := ast.DebugString(fn.Body)
342342
if got != expectedBody {
343343
t.Fatalf("body is not %q. got=%q", expectedBody, got)
@@ -614,15 +614,15 @@ func TestQuote(t *testing.T) {
614614
},
615615
{
616616
`quote(5 + 8)`,
617-
`5 + 8`,
617+
`5+8`,
618618
},
619619
{
620620
`quote(foobar)`,
621621
`foobar`,
622622
},
623623
{
624624
`quote(foobar + barfoo)`,
625-
`foobar + barfoo`,
625+
`foobar+barfoo`,
626626
},
627627
}
628628

@@ -706,7 +706,7 @@ func TestQuoteUnquote(t *testing.T) {
706706
t.Fatalf("quote.Node is nil")
707707
}
708708

709-
got := ast.DebugString(quote.Node)
709+
got := quote.Node.PrettyPrint(ast.NewPrintState()).String()
710710
if got != tt.expected {
711711
t.Errorf("not equal. got=%q, want=%q", got, tt.expected)
712712
}

Diff for: eval/macro_expension_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func TestDefineMacros(t *testing.T) {
5757
t.Fatalf("parameter is not 'y'. got=%q", macro.Parameters[1])
5858
}
5959

60-
expectedBody := "x + y\n" // or should have {}?
60+
expectedBody := "x+y" // or should have {}?
6161
got := ast.DebugString(macro.Body)
6262
if got != expectedBody {
6363
t.Fatalf("body is not %q. got=%q", expectedBody, got)

Diff for: examples/fib.gr

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* fib.gr - compute the 35th Fibonacci number */
12
func fib(x) {
23
if x <= 1 {
34
x

Diff for: main.go

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ func Main() int {
2121
showParse := flag.Bool("parse", false, "show parse tree")
2222
format := flag.Bool("format", false, "don't execute, just parse and re format the input")
2323
compact := flag.Bool("compact", false, "When printing code, use no indentation and most compact form")
24+
save := flag.Bool("save", false, "Emit the serialized AST")
2425
showEval := flag.Bool("eval", true, "show eval results")
2526
sharedState := flag.Bool("shared-state", false, "All files share same interpreter state (default is new state for each)")
2627
cli.ArgsHelp = "*.gr files to interpret or `-` for stdin without prompt or no arguments for stdin repl..."
@@ -32,6 +33,7 @@ func Main() int {
3233
ShowEval: *showEval,
3334
FormatOnly: *format,
3435
Compact: *compact,
36+
Serialize: *save,
3537
}
3638
nArgs := len(flag.Args())
3739
if *commandFlag != "" {

Diff for: parser/parser_book_test.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,7 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
344344
p := New(l)
345345
program := p.ParseProgram()
346346
checkParserErrors(t, p)
347-
348-
actual := ast.DebugString(program)
347+
actual := program.PrettyPrint(ast.NewPrintState()).String()
349348
last := actual[len(actual)-1]
350349
if actual[len(actual)-1] != '\n' {
351350
t.Errorf("expecting newline at end of program output, not found, got %q", last)
@@ -630,7 +629,7 @@ func TestCallExpressionParameterParsing(t *testing.T) {
630629
}
631630

632631
for i, arg := range tt.expectedArgs {
633-
got := ast.DebugString(exp.Arguments[i])
632+
got := exp.Arguments[i].PrettyPrint(ast.NewPrintState()).String()
634633
if got != arg {
635634
t.Errorf("argument %d wrong. want=%q, got=%q", i,
636635
arg, got)

Diff for: parser/parser_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ func Test_OperatorPrecedenceParsing(t *testing.T) {
197197
program := p.ParseProgram()
198198
checkParserErrors(t, tt.input, p)
199199

200-
actual := ast.DebugString(program)
200+
actual := program.PrettyPrint(ast.NewPrintState()).String()
201201
last := actual[len(actual)-1]
202202
if actual[len(actual)-1] != '\n' {
203203
t.Errorf("expecting newline at end of program output, not found, got %q", last)

Diff for: repl/repl.go

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type Options struct {
3939
NoColor bool // color controlled by log package, unless this is set to true.
4040
FormatOnly bool
4141
Compact bool
42+
Serialize bool
4243
}
4344

4445
func EvalAll(s, macroState *eval.State, in io.Reader, out io.Writer, options Options) {
@@ -167,6 +168,10 @@ func EvalOne(s, macroState *eval.State, what string, out io.Writer, options Opti
167168
if p.ContinuationNeeded() {
168169
return true, nil, what
169170
}
171+
if options.Serialize {
172+
program.Serialize(out)
173+
return false, nil, what
174+
}
170175
printer := ast.NewPrintState()
171176
printer.Compact = options.Compact
172177
formatted := program.PrettyPrint(printer).String()

Diff for: token/token.go

+25-9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type Token struct {
2525
_ noCopy
2626
tokenType Type
2727
charCode byte // 1 byte serialization printable code.
28+
valued bool // does literal contains the value (for IDENT, INT, FLOAT, STRING, *COMMENT) or is the code enough.
2829
literal string
2930
}
3031

@@ -47,7 +48,7 @@ func Intern(t Type, literal string) *Token {
4748
if code == 0 {
4849
panic("no code for " + t.String())
4950
}
50-
return InternToken(&Token{tokenType: t, literal: literal, charCode: code})
51+
return InternToken(&Token{tokenType: t, literal: literal, charCode: code, valued: true})
5152
}
5253

5354
func ResetInterning() {
@@ -133,14 +134,17 @@ const (
133134

134135
endIdentityTokens
135136

137+
NULL // used for serializing absent fields like function name.
136138
EOF
137139
)
138140

139141
var (
140142
EOLT = &Token{tokenType: EOL}
141143
EOFT = &Token{tokenType: EOF}
144+
NULLT *Token
142145
TRUET *Token
143146
FALSET *Token
147+
ELSET *Token
144148
)
145149

146150
var (
@@ -158,6 +162,10 @@ func init() {
158162
Init()
159163
}
160164

165+
func assocValued(t Type, code byte) {
166+
assocCodeAndToken(t, code)
167+
}
168+
161169
func assocCodeAndToken(t Type, code byte) {
162170
if tToCode[t] != 0 {
163171
panic("duplicate code for " + t.String() + " " + string(tToCode[t]) + " vs " + string(code))
@@ -237,6 +245,7 @@ func Init() { //nolint:funlen // we need all this.
237245
}
238246
TRUET = tToT[TRUE]
239247
FALSET = tToT[FALSE]
248+
ELSET = tToT[ELSE]
240249
// Single character tokens:
241250
assoc(ASSIGN, '=')
242251
assoc(PLUS, '+')
@@ -291,13 +300,15 @@ func Init() { //nolint:funlen // we need all this.
291300
// Special alias for := to be same as ASSIGN.
292301
c2Tokens[[2]byte{':', '='}] = cTokens['=']
293302
// Valued tokens.:
294-
assocCodeAndToken(IDENT, ' ')
295-
assocCodeAndToken(INT, '0')
296-
assocCodeAndToken(FLOAT, '.')
297-
assocCodeAndToken(STRING, 's')
298-
assocCodeAndToken(LINECOMMENT, 'c')
299-
assocCodeAndToken(BLOCKCOMMENT, 'b')
300-
303+
assocValued(IDENT, ' ')
304+
assocValued(INT, '0')
305+
assocValued(FLOAT, '.')
306+
assocValued(STRING, 's')
307+
assocValued(LINECOMMENT, 'c')
308+
assocValued(BLOCKCOMMENT, 'b')
309+
310+
assocCodeAndToken(NULL, 'Z') // absence of value
311+
NULLT = &Token{tokenType: NULL, literal: "", charCode: tToCode[NULL], valued: false}
301312
assocCodeAndToken(ILLEGAL, 1) // doesn't need to be printable.
302313
assocCodeAndToken(EOL, 10) // nl but not printed anyway
303314
assocCodeAndToken(EOF, 'z') // also not used/visible
@@ -311,7 +322,7 @@ func LookupIdent(ident string) *Token {
311322
if t, ok := keywords[ident]; ok {
312323
return t
313324
}
314-
return InternToken(&Token{tokenType: IDENT, literal: ident, charCode: ' '})
325+
return InternToken(&Token{tokenType: IDENT, literal: ident, charCode: ' ', valued: true})
315326
}
316327

317328
// ByType is the cheapest lookup for all the tokens whose type
@@ -347,6 +358,11 @@ func (t *Token) DebugString() string {
347358
return t.Type().String() + ":" + strconv.Quote(t.Literal())
348359
}
349360

361+
// Is this one of the valued type, where the literal is content and not just the code.
362+
func (t *Token) HasContent() bool {
363+
return t.valued
364+
}
365+
350366
func NumTokens() int {
351367
return tokensCount
352368
}

0 commit comments

Comments
 (0)