tasks a-s
This commit is contained in:
parent
47bf37c096
commit
b83f433714
12433 changed files with 156208 additions and 123 deletions
174
Task/S-Expressions/Go/s-expressions.go
Normal file
174
Task/S-Expressions/Go/s-expressions.go
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var input = `((data "quoted data" 123 4.5)
|
||||
(data (!@# (4.5) "(more" "data)")))`
|
||||
|
||||
func main() {
|
||||
fmt.Println("input:")
|
||||
fmt.Println(input)
|
||||
|
||||
s, err := parseSexp(input)
|
||||
if err != nil {
|
||||
fmt.Println("error:", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("\nparsed:")
|
||||
fmt.Println(s)
|
||||
|
||||
fmt.Println("\nrepresentation:")
|
||||
s.dump(0)
|
||||
}
|
||||
|
||||
// dynamic types for i are string, qString, int, float64, list, and error.
|
||||
type sexp struct {
|
||||
i interface{}
|
||||
}
|
||||
type qString string
|
||||
type list []sexp
|
||||
|
||||
func (s sexp) String() string {
|
||||
return fmt.Sprintf("%v", s.i)
|
||||
}
|
||||
|
||||
func (q qString) String() string {
|
||||
return strconv.Quote(string(q))
|
||||
}
|
||||
|
||||
func (l list) String() string {
|
||||
if len(l) == 0 {
|
||||
return "()"
|
||||
}
|
||||
b := fmt.Sprintf("(%v", l[0])
|
||||
for _, s := range l[1:] {
|
||||
b = fmt.Sprintf("%s %v", b, s)
|
||||
}
|
||||
return b + ")"
|
||||
}
|
||||
|
||||
// parseSexp parses a string into a Go representation of an s-expression.
|
||||
//
|
||||
// Quoted strings go from one " to the next. There is no escape character,
|
||||
// all characters except " are valid.
|
||||
//
|
||||
// Otherwise atoms are any string of characters between any of '(', ')',
|
||||
// '"', or white space characters. If the atom parses as a Go int type
|
||||
// using strconv.Atoi, it is taken as int; if it parses as a Go float64
|
||||
// type using strconv.ParseFloat, it is taken as float64; otherwise it is
|
||||
// taken as an unquoted string.
|
||||
//
|
||||
// Unmatched (, ), or " are errors.
|
||||
// An empty or all whitespace input string is an error.
|
||||
// Left over text after the sexp is an error.
|
||||
//
|
||||
// An empty list is a valid sexp, but there is no nil, no cons, no dot.
|
||||
func parseSexp(s string) (sexp, error) {
|
||||
s1, rem := ps2(s, -1)
|
||||
if err, isErr := s1.i.(error); isErr {
|
||||
return sexp{}, err
|
||||
}
|
||||
if rem > "" {
|
||||
return s1, errors.New("Left over text: " + rem)
|
||||
}
|
||||
return s1, nil
|
||||
}
|
||||
|
||||
// recursive. n = -1 means not parsing a list. n >= 0 means the number
|
||||
// of list elements parsed so far. string result is unparsed remainder
|
||||
// of the input string s0.
|
||||
func ps2(s0 string, n int) (x sexp, rem string) {
|
||||
tok, s1 := gettok(s0)
|
||||
switch t := tok.(type) {
|
||||
case error:
|
||||
return sexp{tok}, s1
|
||||
case nil: // this is also an error
|
||||
if n < 0 {
|
||||
return sexp{errors.New("blank input string")}, s0
|
||||
} else {
|
||||
return sexp{errors.New("unmatched (")}, ""
|
||||
}
|
||||
case byte:
|
||||
switch {
|
||||
case t == '(':
|
||||
x, s1 = ps2(s1, 0) // x is a list
|
||||
if _, isErr := x.i.(error); isErr {
|
||||
return x, s0
|
||||
}
|
||||
case n < 0:
|
||||
return sexp{errors.New("unmatched )")}, ""
|
||||
default:
|
||||
// found end of list. allocate space for it.
|
||||
return sexp{make(list, n)}, s1
|
||||
}
|
||||
default:
|
||||
x = sexp{tok} // x is an atom
|
||||
}
|
||||
if n < 0 {
|
||||
// not in a list, just return the s-expression x
|
||||
return x, s1
|
||||
}
|
||||
// in a list. hold on to x while we parse the rest of the list.
|
||||
l, s1 := ps2(s1, n+1)
|
||||
// result l is either an error or the allocated list, not completely
|
||||
// filled in yet.
|
||||
if _, isErr := l.i.(error); !isErr {
|
||||
// as long as no errors, drop x into its place in the list
|
||||
l.i.(list)[n] = x
|
||||
}
|
||||
return l, s1
|
||||
}
|
||||
|
||||
// gettok gets one token from string s.
|
||||
// return values are the token and the remainder of the string.
|
||||
// dynamic type of tok indicates result:
|
||||
// nil: no token. string was empty or all white space.
|
||||
// byte: one of '(' or ')'
|
||||
// otherwise string, qString, int, float64, or error.
|
||||
func gettok(s string) (tok interface{}, rem string) {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return nil, ""
|
||||
}
|
||||
switch s[0] {
|
||||
case '(', ')':
|
||||
return s[0], s[1:]
|
||||
case '"':
|
||||
if i := strings.Index(s[1:], `"`); i >= 0 {
|
||||
return qString(s[1 : i+1]), s[i+2:]
|
||||
}
|
||||
return errors.New(`unmatched "`), s
|
||||
}
|
||||
i := 1
|
||||
for i < len(s) && s[i] != '(' && s[i] != ')' && s[i] != '"' &&
|
||||
!unicode.IsSpace(rune(s[i])) {
|
||||
i++
|
||||
}
|
||||
if j, err := strconv.Atoi(s[:i]); err == nil {
|
||||
return j, s[i:]
|
||||
}
|
||||
if f, err := strconv.ParseFloat(s[:i], 64); err == nil {
|
||||
return f, s[i:]
|
||||
}
|
||||
return s[:i], s[i:]
|
||||
}
|
||||
|
||||
func (s sexp) dump(i int) {
|
||||
fmt.Printf("%*s%v: ", i*3, "", reflect.TypeOf(s.i))
|
||||
if l, isList := s.i.(list); isList {
|
||||
fmt.Println(len(l), "elements")
|
||||
for _, e := range l {
|
||||
e.dump(i + 1)
|
||||
}
|
||||
} else {
|
||||
fmt.Println(s.i)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue