How to resolve the algorithm S-expressions step by step in the Go programming language
How to resolve the algorithm S-expressions step by step in the Go programming language
Table of Contents
Problem Statement
S-Expressions are one convenient way to parse and store data.
Write a simple reader and writer for S-Expressions that handles quoted and unquoted strings, integers and floats. The reader should read a single but nested S-Expression from a string and store it in a suitable datastructure (list, array, etc). Newlines and other whitespace may be ignored unless contained within a quoted string. “()” inside quoted strings are not interpreted, but treated as part of the string. Handling escaped quotes inside a string is optional; thus “(foo"bar)” maybe treated as a string “foo"bar”, or as an error. For this, the reader need not recognize “\” for escaping, but should, in addition, recognize numbers if the language has appropriate datatypes. Languages that support it may treat unquoted strings as symbols. Note that with the exception of “()"” (“\” if escaping is supported) and whitespace there are no special characters. Anything else is allowed without quotes. The reader should be able to read the following input and turn it into a native datastructure. (see the Pike, Python and Ruby implementations for examples of native data structures.) The writer should be able to take the produced list and turn it into a new S-Expression. Strings that don't contain whitespace or parentheses () don't need to be quoted in the resulting S-Expression, but as a simplification, any string may be quoted.
Let the writer produce pretty printed output with indenting and line-breaks.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm S-expressions step by step in the Go programming language
The Go program parses an S-expression, it is a data structure consisting of nested lists that can contain strings, numbers, lists, or s-expressions in a recursive manner.
It is a Lisp-like language that uses the parenthesis syntax to represent data structures.
The program first parses the input string into a Go representation of an s-expression using the parseSexp
function.
The parseSexp
function uses the ps2
function, which is a recursive function that parses the s-expression string into a Go representation.
The gettok
function is used by the ps2
function to get one token from the s-expression string.
The program then prints the parsed s-expression and its Go representation using the dump
function.
The program uses the following types to represent the s-expression:
sexp
: This is the main type that represents the s-expression. It can contain any of the following types as its value:- string
- qString
- int
- float64
- list
- error
qString
: This type is used to represent a quoted string.list
: This is a slice ofsexp
values and represents a list in the s-expression.
The program uses the following functions to parse the s-expression:
parseSexp
: This function parses the input string into a Go representation of an s-expression.ps2
: This is a recursive function that parses the s-expression string into a Go representation.gettok
: This function is used by theps2
function to get one token from the s-expression string.
The program uses the following functions to print the s-expression and its Go representation:
String
: This function is used to print the s-expression.dump
: This function is used to print the s-expression and its Go representation.
The program reads the input string from the input
variable and parses it using the parseSexp
function.
If the parsing is successful, it prints the parsed s-expression and its Go representation.
If the parsing fails, it prints the error message.
Source code in the go programming language
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)
}
}
You may also check:How to resolve the algorithm Closures/Value capture step by step in the J programming language
You may also check:How to resolve the algorithm Secure temporary file step by step in the Nanoquery programming language
You may also check:How to resolve the algorithm Search a list step by step in the ALGOL 68 programming language
You may also check:How to resolve the algorithm Perfect totient numbers step by step in the bc programming language
You may also check:How to resolve the algorithm Queue/Definition step by step in the AArch64 Assembly programming language