package moon import ( "bufio" "bytes" "fmt" "io" "sort" "strconv" "strings" "time" ) type nodeType int const ( n_error nodeType = iota n_root n_comment n_assignment n_string n_number n_list n_object n_variable n_bool n_duration ) var indent = " " type context struct { public map[string]interface{} private map[string]interface{} } func newContext() *context { return &context{make(map[string]interface{}), make(map[string]interface{})} } func (c *context) get(name string) (interface{}, bool) { if v, ok := c.public[name]; ok { return v, true } if v, ok := c.private[name]; ok { return v, true } return nil, false } type node interface { Type() nodeType parse(*parser) error pretty(io.Writer, string) error eval(*context) (interface{}, error) } type rootNode struct { children []node } func newRootNode() node { return &rootNode{children: make([]node, 0, 8)} } func (n *rootNode) Type() nodeType { return n_root } func (n *rootNode) parse(p *parser) error { for { t := p.next() switch t.t { case t_error: return fmt.Errorf("parse error: saw lex error while parsing root node: %v", t) case t_eof: return nil case t_comment: n.addChild(&commentNode{t.s}) case t_name: nn := &assignmentNode{name: t.s} if err := nn.parse(p); err != nil { return err } n.addChild(nn) case t_variable: nn := &assignmentNode{name: t.s, unexported: true} if err := nn.parse(p); err != nil { return err } n.addChild(nn) default: return fmt.Errorf("parse error: unexpected token type %v while parsing root node", t.t) } } } func (n *rootNode) addChild(child node) { if n.children == nil { n.children = make([]node, 0, 8) } n.children = append(n.children, child) } func (n *rootNode) String() string { var buf bytes.Buffer buf.WriteString("{") for _, child := range n.children { fmt.Fprintf(&buf, "%s, ", child) } if buf.Len() > 1 { buf.Truncate(buf.Len() - 2) } buf.WriteString("}") return buf.String() } func (n *rootNode) pretty(w io.Writer, prefix string) error { fmt.Fprintf(w, "%sroot:\n", prefix) for _, child := range n.children { if err := child.pretty(w, prefix+indent); err != nil { return err } } return nil } func (n *rootNode) eval(ctx *context) (interface{}, error) { for _, child := range n.children { if _, err := child.eval(ctx); err != nil { return nil, err } } return nil, nil } type commentNode struct { body string } func (n *commentNode) Type() nodeType { return n_comment } func (n *commentNode) parse(p *parser) error { return nil } func (n *commentNode) String() string { return fmt.Sprintf("{comment: %s}", n.body) } func (n *commentNode) pretty(w io.Writer, prefix string) error { fmt.Fprintf(w, "%scomment:\n", prefix) r := bufio.NewReader(strings.NewReader(n.body)) for { line, err := r.ReadString('\n') if err == io.EOF { if line != "" { fmt.Fprintf(w, "%s%s%s\n", prefix, indent, line) } break } if err != nil { return err } fmt.Fprintf(w, "%s%s%s\n", prefix, indent, line) } return nil } func (n *commentNode) eval(ctx *context) (interface{}, error) { return nil, nil } type assignmentNode struct { name string value node unexported bool } func (n *assignmentNode) Type() nodeType { return n_assignment } func (n *assignmentNode) parse(p *parser) error { t := p.next() switch t.t { case t_error: return fmt.Errorf("parse error: saw lex error while parsing assignment node: %v", t.s) case t_eof: return fmt.Errorf("parse error: unexpected eof in assignment node") case t_object_separator: default: return fmt.Errorf("parse error: unexpected %v token after name, expected =", t.t) } v, err := p.parseValue() if err != nil { return err } n.value = v return nil } func (n *assignmentNode) String() string { return fmt.Sprintf("{assign: name=%s, val=%v}", n.name, n.value) } func (n *assignmentNode) pretty(w io.Writer, prefix string) error { fmt.Fprintf(w, "%sassign:\n", prefix) fmt.Fprintf(w, "%s%sname:\n", prefix, indent) fmt.Fprintf(w, "%s%s%s%s\n", prefix, indent, indent, n.name) fmt.Fprintf(w, "%s%svalue:\n", prefix, indent) if err := n.value.pretty(w, prefix+indent+indent); err != nil { return err } return nil } func (n *assignmentNode) eval(ctx *context) (interface{}, error) { if _, ok := ctx.get(n.name); ok { return nil, fmt.Errorf("invalid re-declaration: %s", n.name) } v, err := n.value.eval(ctx) if err != nil { return nil, err } if n.unexported { ctx.private[n.name] = v } else { ctx.public[n.name] = v } return nil, nil } func (n *assignmentNode) isHidden() bool { return strings.HasPrefix(n.name, ".") } type stringNode string func (s *stringNode) Type() nodeType { return n_string } func (s *stringNode) parse(p *parser) error { t := p.next() if t.t != t_string { return fmt.Errorf("unexpected %s while looking for string token", t.t) } *s = stringNode(t.s) return nil } func (s *stringNode) pretty(w io.Writer, prefix string) error { fmt.Fprintf(w, "%sstring:\n", prefix) _, err := fmt.Fprintf(w, "%s%s%s\n", prefix, indent, string(*s)) return err } func (s *stringNode) eval(ctx *context) (interface{}, error) { return string(*s), nil } type numberType int const ( num_int numberType = iota num_float num_complex ) type numberNode struct { t numberType c complex128 i int f float64 } func (n *numberNode) Type() nodeType { return n_number } func (n *numberNode) parse(p *parser) error { t := p.next() if t.t != t_real_number { return fmt.Errorf("unexpected %s token while parsing number", t.t) } if p.peek().t == t_imaginary_number { n.t = num_complex s := t.s + p.next().s if _, err := fmt.Sscan(s, &n.c); err != nil { return fmt.Errorf("ungood imaginary number format %s: %s", s, err) } return nil } i, err := strconv.ParseInt(t.s, 0, 64) if err == nil { n.t = num_int n.i = int(i) return nil } f, err := strconv.ParseFloat(t.s, 64) if err == nil { n.t = num_float n.f = f return nil } return fmt.Errorf("this token broke the number parser: %s", t) } func (n *numberNode) pretty(w io.Writer, prefix string) error { v, err := n.eval(nil) if err != nil { return err } switch n.t { case num_int: fmt.Fprintf(w, "%sint:\n%s%s%v\n", prefix, prefix, indent, v) case num_float: fmt.Fprintf(w, "%sfloat:\n%s%s%v\n", prefix, prefix, indent, v) case num_complex: fmt.Fprintf(w, "%scomplex:\n%s%s%v\n", prefix, prefix, indent, v) } return nil } func (n *numberNode) eval(ctx *context) (interface{}, error) { switch n.t { case num_int: return n.i, nil case num_float: return n.f, nil case num_complex: return n.c, nil default: panic("whoerps") } } type listNode []node func (l *listNode) Type() nodeType { return n_list } func (l *listNode) parse(p *parser) error { if p.peek().t == t_list_end { p.next() return nil } if n, err := p.parseValue(); err != nil { return err } else { *l = append(*l, n) } switch t := p.peek(); t.t { case t_list_end: p.next() return nil default: return l.parse(p) } } func (l *listNode) pretty(w io.Writer, prefix string) error { fmt.Fprintf(w, "%slist:\n", prefix) for _, n := range *l { if err := n.pretty(w, prefix+indent); err != nil { return err } } return nil } func (l *listNode) eval(ctx *context) (interface{}, error) { out := make([]interface{}, 0, len(*l)) for _, n := range *l { v, err := n.eval(ctx) if err != nil { return nil, err } out = append(out, v) } return out, nil } type objectNode map[string]node func (o *objectNode) Type() nodeType { return n_object } func (o *objectNode) parse(p *parser) error { if p.peek().t == t_object_end { p.next() return nil } if err := p.ensureNext(t_name, "looking for object field name in parseObject"); err != nil { return err } field_name := p.next().s if err := p.ensureNext(t_object_separator, "looking for object separator in parseObject"); err != nil { return err } p.next() if n, err := p.parseValue(); err != nil { return err } else { (*o)[field_name] = n } switch t := p.peek(); t.t { case t_object_end: p.next() return nil default: return o.parse(p) } } func (o *objectNode) pretty(w io.Writer, prefix string) error { fmt.Fprintf(w, "%sobject:\n", prefix) keys := make([]string, 0, len(*o)) for key := range *o { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { fmt.Fprintf(w, "%s%s:\n", prefix+indent, key) err := (*o)[key].pretty(w, prefix+indent+indent) if err != nil { return err } } return nil } func (o *objectNode) eval(ctx *context) (interface{}, error) { out := make(map[string]interface{}, len(*o)) for name, node := range *o { v, err := node.eval(ctx) if err != nil { return nil, err } out[name] = v } return out, nil } type variableNode struct { name string } func (v *variableNode) Type() nodeType { return n_variable } func (v *variableNode) parse(p *parser) error { t := p.next() if t.t != t_variable { return fmt.Errorf("unexpected %s token when parsing variable", t.t) } v.name = t.s return nil } func (v *variableNode) pretty(w io.Writer, prefix string) error { fmt.Fprintf(w, "%svariable:\n", prefix) fmt.Fprintf(w, "%s%s\n", prefix+indent, v.name) return nil } func (v *variableNode) eval(ctx *context) (interface{}, error) { value, ok := ctx.get(v.name) if !ok { return nil, fmt.Errorf("undefined variable: %s", *v) } return value, nil } type boolNode bool func (b *boolNode) Type() nodeType { return n_bool } func (b *boolNode) parse(p *parser) error { t := p.next() if t.t != t_bool { return fmt.Errorf("unexpected %s token while parsing bool", t.t) } switch t.s { case "true": *b = true case "false": default: return fmt.Errorf("illegal lexeme for bool token: %s", t.s) } return nil } func (b *boolNode) pretty(w io.Writer, prefix string) error { fmt.Fprintf(w, "%sbool:\n", prefix) fmt.Fprintf(w, "%s%t\n", prefix+indent, *b) return nil } func (b *boolNode) eval(ctx *context) (interface{}, error) { return bool(*b), nil } type durationNode time.Duration func (d *durationNode) Type() nodeType { return n_duration } func (d *durationNode) parse(p *parser) error { t := p.next() if t.t != t_duration { return fmt.Errorf("unexpected %s token while parsing duration", t.t) } v, err := time.ParseDuration(t.s) if err != nil { return fmt.Errorf("unable to parse duration: %s", err) } *d = durationNode(v) return nil } func (d *durationNode) pretty(w io.Writer, prefix string) error { fmt.Fprintf(w, "%sdur:\n", prefix) fmt.Fprintf(w, "%s%s\n", prefix+indent, time.Duration(*d).String()) return nil } func (d *durationNode) eval(ctx *context) (interface{}, error) { return time.Duration(*d), nil }