| // run | 
 |  | 
 | // Copyright 2009 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | // Test general operation using s-list. | 
 | // First Go program ever run (although not in this exact form). | 
 |  | 
 | package main | 
 |  | 
 | import "fmt" | 
 |  | 
 | const nilchar = 0 | 
 |  | 
 | type Atom struct { | 
 | 	str     string | 
 | 	integer int | 
 | 	next    *Slist /* in hash bucket */ | 
 | } | 
 |  | 
 | type List struct { | 
 | 	car *Slist | 
 | 	cdr *Slist | 
 | } | 
 |  | 
 | type Slist struct { | 
 | 	isatom   bool | 
 | 	isstring bool | 
 | 	//union { | 
 | 	atom Atom | 
 | 	list List | 
 | 	//} u; | 
 |  | 
 | } | 
 |  | 
 | func (this *Slist) Car() *Slist { | 
 | 	return this.list.car | 
 | } | 
 |  | 
 | func (this *Slist) Cdr() *Slist { | 
 | 	return this.list.cdr | 
 | } | 
 |  | 
 | func (this *Slist) String() string { | 
 | 	return this.atom.str | 
 | } | 
 |  | 
 | func (this *Slist) Integer() int { | 
 | 	return this.atom.integer | 
 | } | 
 |  | 
 | func (slist *Slist) Free() { | 
 | 	if slist == nil { | 
 | 		return | 
 | 	} | 
 | 	if slist.isatom { | 
 | 		//		free(slist.String()); | 
 | 	} else { | 
 | 		slist.Car().Free() | 
 | 		slist.Cdr().Free() | 
 | 	} | 
 | 	//	free(slist); | 
 | } | 
 |  | 
 | //Slist* atom(byte *s, int i); | 
 |  | 
 | var token int | 
 | var peekc int = -1 | 
 | var lineno int32 = 1 | 
 |  | 
 | var input string | 
 | var inputindex int = 0 | 
 | var tokenbuf [100]byte | 
 | var tokenlen int = 0 | 
 |  | 
 | const EOF int = -1 | 
 |  | 
 | func main() { | 
 | 	var list *Slist | 
 |  | 
 | 	OpenFile() | 
 | 	for { | 
 | 		list = Parse() | 
 | 		if list == nil { | 
 | 			break | 
 | 		} | 
 | 		r := list.Print() | 
 | 		list.Free() | 
 | 		if r != "(defn foo (add 12 34))" { | 
 | 			panic(r) | 
 | 		} | 
 | 		break | 
 | 	} | 
 | } | 
 |  | 
 | func (slist *Slist) PrintOne(doparen bool) string { | 
 | 	if slist == nil { | 
 | 		return "" | 
 | 	} | 
 | 	var r string | 
 | 	if slist.isatom { | 
 | 		if slist.isstring { | 
 | 			r = slist.String() | 
 | 		} else { | 
 | 			r = fmt.Sprintf("%v", slist.Integer()) | 
 | 		} | 
 | 	} else { | 
 | 		if doparen { | 
 | 			r += "(" | 
 | 		} | 
 | 		r += slist.Car().PrintOne(true) | 
 | 		if slist.Cdr() != nil { | 
 | 			r += " " | 
 | 			r += slist.Cdr().PrintOne(false) | 
 | 		} | 
 | 		if doparen { | 
 | 			r += ")" | 
 | 		} | 
 | 	} | 
 | 	return r | 
 | } | 
 |  | 
 | func (slist *Slist) Print() string { | 
 | 	return slist.PrintOne(true) | 
 | } | 
 |  | 
 | func Get() int { | 
 | 	var c int | 
 |  | 
 | 	if peekc >= 0 { | 
 | 		c = peekc | 
 | 		peekc = -1 | 
 | 	} else { | 
 | 		c = int(input[inputindex]) | 
 | 		inputindex++ | 
 | 		if c == '\n' { | 
 | 			lineno = lineno + 1 | 
 | 		} | 
 | 		if c == nilchar { | 
 | 			inputindex = inputindex - 1 | 
 | 			c = EOF | 
 | 		} | 
 | 	} | 
 | 	return c | 
 | } | 
 |  | 
 | func WhiteSpace(c int) bool { | 
 | 	return c == ' ' || c == '\t' || c == '\r' || c == '\n' | 
 | } | 
 |  | 
 | func NextToken() { | 
 | 	var i, c int | 
 |  | 
 | 	tokenbuf[0] = nilchar // clear previous token | 
 | 	c = Get() | 
 | 	for WhiteSpace(c) { | 
 | 		c = Get() | 
 | 	} | 
 | 	switch c { | 
 | 	case EOF: | 
 | 		token = EOF | 
 | 	case '(', ')': | 
 | 		token = c | 
 | 		break | 
 | 	default: | 
 | 		for i = 0; i < 100-1; { // sizeof tokenbuf - 1 | 
 | 			tokenbuf[i] = byte(c) | 
 | 			i = i + 1 | 
 | 			c = Get() | 
 | 			if c == EOF { | 
 | 				break | 
 | 			} | 
 | 			if WhiteSpace(c) || c == ')' { | 
 | 				peekc = c | 
 | 				break | 
 | 			} | 
 | 		} | 
 | 		if i >= 100-1 { // sizeof tokenbuf - 1 | 
 | 			panic("atom too long\n") | 
 | 		} | 
 | 		tokenlen = i | 
 | 		tokenbuf[i] = nilchar | 
 | 		if '0' <= tokenbuf[0] && tokenbuf[0] <= '9' { | 
 | 			token = '0' | 
 | 		} else { | 
 | 			token = 'A' | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func Expect(c int) { | 
 | 	if token != c { | 
 | 		print("parse error: expected ", c, "\n") | 
 | 		panic("parse") | 
 | 	} | 
 | 	NextToken() | 
 | } | 
 |  | 
 | // Parse a non-parenthesized list up to a closing paren or EOF | 
 | func ParseList() *Slist { | 
 | 	var slist, retval *Slist | 
 |  | 
 | 	slist = new(Slist) | 
 | 	slist.list.car = nil | 
 | 	slist.list.cdr = nil | 
 | 	slist.isatom = false | 
 | 	slist.isstring = false | 
 |  | 
 | 	retval = slist | 
 | 	for { | 
 | 		slist.list.car = Parse() | 
 | 		if token == ')' || token == EOF { // empty cdr | 
 | 			break | 
 | 		} | 
 | 		slist.list.cdr = new(Slist) | 
 | 		slist = slist.list.cdr | 
 | 	} | 
 | 	return retval | 
 | } | 
 |  | 
 | func atom(i int) *Slist { // BUG: uses tokenbuf; should take argument) | 
 | 	var slist *Slist | 
 |  | 
 | 	slist = new(Slist) | 
 | 	if token == '0' { | 
 | 		slist.atom.integer = i | 
 | 		slist.isstring = false | 
 | 	} else { | 
 | 		slist.atom.str = string(tokenbuf[0:tokenlen]) | 
 | 		slist.isstring = true | 
 | 	} | 
 | 	slist.isatom = true | 
 | 	return slist | 
 | } | 
 |  | 
 | func atoi() int { // BUG: uses tokenbuf; should take argument) | 
 | 	var v int = 0 | 
 | 	for i := 0; i < tokenlen && '0' <= tokenbuf[i] && tokenbuf[i] <= '9'; i = i + 1 { | 
 | 		v = 10*v + int(tokenbuf[i]-'0') | 
 | 	} | 
 | 	return v | 
 | } | 
 |  | 
 | func Parse() *Slist { | 
 | 	var slist *Slist | 
 |  | 
 | 	if token == EOF || token == ')' { | 
 | 		return nil | 
 | 	} | 
 | 	if token == '(' { | 
 | 		NextToken() | 
 | 		slist = ParseList() | 
 | 		Expect(')') | 
 | 		return slist | 
 | 	} else { | 
 | 		// Atom | 
 | 		switch token { | 
 | 		case EOF: | 
 | 			return nil | 
 | 		case '0': | 
 | 			slist = atom(atoi()) | 
 | 		case '"', 'A': | 
 | 			slist = atom(0) | 
 | 		default: | 
 | 			slist = nil | 
 | 			print("unknown token: ", token, "\n") | 
 | 		} | 
 | 		NextToken() | 
 | 		return slist | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | func OpenFile() { | 
 | 	input = "(defn foo (add 12 34))\n\x00" | 
 | 	inputindex = 0 | 
 | 	peekc = -1 // BUG | 
 | 	NextToken() | 
 | } |