ok ok ok, variables work properly again

master
Jordan Orelli 10 years ago
parent f341593477
commit 660aee96f9

@ -9,25 +9,25 @@
# the whole document is implicitly a namespace, so you can set key value pairs # the whole document is implicitly a namespace, so you can set key value pairs
# at the top level. # at the top level.
first_name: "jordan" first_name: jordan
last_name: "orelli" last_name: orelli
# lists of things should be supported # lists of things should be supported
items: [ items: [
"one" one
2 2
3.4 3.4
["five" 6 7.8] [five; 6 7.8]
] ]
# objects should be supported # objects should be supported
hash: {key: "value" other_key: "other_value"} hash: {key: value; other_key: other_value}
other_hash: { other_hash: {
key_1: "one" key_1: one
key_2: 2 key_2: 2
key_3: 3.4 key_3: 3.4
key_4: ["five" 6 7.8] key_4: [five; 6 7.8]
} }
# we may reference an item that was defined earlier using a sigil # we may reference an item that was defined earlier using a sigil
@ -37,19 +37,19 @@ repeat_hash: @hash
# intermediate values internal to the config file; they are *not* visible to # intermediate values internal to the config file; they are *not* visible to
# the host program. This is generally useful for composing larger, more # the host program. This is generally useful for composing larger, more
# complicated things. # complicated things.
@hidden_item: "it has a value" @hidden_item: it has a value
visible_item: @hidden_item visible_item: @hidden_item
@person_one: { @person_one: {
name: "the first name here" name: the first name here
age: 28 age: 28
hometown: "crooklyn" hometown: crooklyn
} }
@person_two: { @person_two: {
name: "the second name here" name: the second name here
age: 30 age: 30
hometown: "tha bronx" hometown: tha bronx
} }
people: [@person_one @person_two] people: [@person_one @person_two]

@ -128,6 +128,22 @@ func (l *lexer) unread(r rune) {
} }
func (l *lexer) emit(t tokenType) { func (l *lexer) emit(t tokenType) {
switch t {
case t_variable:
if !l.bufHasSpaces() {
break
}
msg := fmt.Sprintf(`invalid var name: "%s" (var names cannot contain spaces)`, string(l.buf))
l.out <- token{t_error, msg}
return
case t_name:
if !l.bufHasSpaces() {
break
}
msg := fmt.Sprintf(`invalid name: "%s" (names cannot contain spaces)`, string(l.buf))
l.out <- token{t_error, msg}
return
}
l.out <- token{t, string(l.buf)} l.out <- token{t, string(l.buf)}
l.buf = l.buf[0:0] l.buf = l.buf[0:0]
} }
@ -151,6 +167,15 @@ func (l *lexer) acceptRun(chars string) bool {
return !none return !none
} }
func (l *lexer) bufHasSpaces() bool {
for _, r := range l.buf {
if unicode.IsSpace(r) {
return true
}
}
return false
}
func lexString(in string) chan token { func lexString(in string) chan token {
r := strings.NewReader(in) r := strings.NewReader(in)
return lex(r) return lex(r)
@ -288,59 +313,62 @@ func lexQuotedString(delim rune) stateFn {
func lexNameOrString(l *lexer) stateFn { func lexNameOrString(l *lexer) stateFn {
r := l.next() r := l.next()
switch r { switch {
case '\n', ';': case r == '\n', r == ';':
l.emit(t_string) l.emit(t_string)
return lexRoot return lexRoot
case ':': case r == ':':
l.emit(t_name) l.emit(t_name)
l.keep(r) l.keep(r)
l.emit(t_object_separator) l.emit(t_object_separator)
return lexRoot return lexRoot
case '\\': case isSpecial(r):
l.emit(t_string)
l.unread(r)
return lexRoot
case r == '\\':
rr := l.next() rr := l.next()
if rr == eof { if rr == eof {
return lexErrorf("unexpected eof in string or name") return lexErrorf("unexpected eof in string or name")
} }
l.keep(rr) l.keep(rr)
return lexNameOrString return lexNameOrString
case '#': case r == eof:
return lexComment
case eof:
l.emit(t_string) l.emit(t_string)
return nil return nil
default: case unicode.IsGraphic(r):
l.keep(r) l.keep(r)
return lexNameOrString return lexNameOrString
default:
return lexErrorf("unexpected rune in string or name: %c", r)
} }
} }
func lexVariable(l *lexer) stateFn { func lexVariable(l *lexer) stateFn {
r := l.next() r := l.next()
switch r { switch {
case '\n', ';': case unicode.IsSpace(r), r == ';':
l.emit(t_variable) l.emit(t_variable)
return lexRoot return lexRoot
case ':': case r == '\\':
l.emit(t_variable)
l.keep(r)
l.emit(t_object_separator)
return lexRoot
case '\\':
rr := l.next() rr := l.next()
if rr == eof { if rr == eof {
return lexErrorf("unexpected eof in variable name") return lexErrorf("unexpected eof in variable name")
} }
l.keep(rr) l.keep(rr)
return lexVariable return lexVariable
case '#': case isSpecial(r):
return lexComment l.emit(t_variable)
case eof: l.unread(r)
return lexRoot
case r == eof:
l.emit(t_variable) l.emit(t_variable)
return nil return nil
default: case unicode.IsGraphic(r):
l.keep(r) l.keep(r)
return lexVariable return lexVariable
default:
return lexErrorf("unexpected rune in var name: %c", r)
} }
} }
@ -379,3 +407,7 @@ func lexNumber(l *lexer) stateFn {
func isAlphaNumeric(r rune) bool { func isAlphaNumeric(r rune) bool {
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
} }
func isSpecial(r rune) bool {
return strings.ContainsRune("[]{}:;#", r)
}

@ -26,13 +26,30 @@ const (
var indent = " " var indent = " "
type context map[string]interface{} 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 node interface {
Type() nodeType Type() nodeType
parse(*parser) error parse(*parser) error
pretty(io.Writer, string) error pretty(io.Writer, string) error
eval(context) (interface{}, error) eval(*context) (interface{}, error)
} }
type rootNode struct { type rootNode struct {
@ -57,12 +74,18 @@ func (n *rootNode) parse(p *parser) error {
return nil return nil
case t_comment: case t_comment:
n.addChild(&commentNode{t.s}) n.addChild(&commentNode{t.s})
case t_name, t_variable: case t_name:
nn := &assignmentNode{name: t.s} nn := &assignmentNode{name: t.s}
if err := nn.parse(p); err != nil { if err := nn.parse(p); err != nil {
return err return err
} }
n.addChild(nn) 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: default:
return fmt.Errorf("parse error: unexpected token type %v while parsing root node", t.t) return fmt.Errorf("parse error: unexpected token type %v while parsing root node", t.t)
} }
@ -99,7 +122,7 @@ func (n *rootNode) pretty(w io.Writer, prefix string) error {
return nil return nil
} }
func (n *rootNode) eval(ctx context) (interface{}, error) { func (n *rootNode) eval(ctx *context) (interface{}, error) {
for _, child := range n.children { for _, child := range n.children {
if _, err := child.eval(ctx); err != nil { if _, err := child.eval(ctx); err != nil {
return nil, err return nil, err
@ -143,13 +166,14 @@ func (n *commentNode) pretty(w io.Writer, prefix string) error {
return nil return nil
} }
func (n *commentNode) eval(ctx context) (interface{}, error) { func (n *commentNode) eval(ctx *context) (interface{}, error) {
return nil, nil return nil, nil
} }
type assignmentNode struct { type assignmentNode struct {
name string name string
value node value node
unexported bool
} }
func (n *assignmentNode) Type() nodeType { func (n *assignmentNode) Type() nodeType {
@ -191,15 +215,19 @@ func (n *assignmentNode) pretty(w io.Writer, prefix string) error {
return nil return nil
} }
func (n *assignmentNode) eval(ctx context) (interface{}, error) { func (n *assignmentNode) eval(ctx *context) (interface{}, error) {
if _, ok := ctx[n.name]; ok { if _, ok := ctx.get(n.name); ok {
return nil, fmt.Errorf("invalid re-declaration: %s", n.name) return nil, fmt.Errorf("invalid re-declaration: %s", n.name)
} }
v, err := n.value.eval(ctx) v, err := n.value.eval(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctx[n.name] = v if n.unexported {
ctx.private[n.name] = v
} else {
ctx.public[n.name] = v
}
return nil, nil return nil, nil
} }
@ -228,7 +256,7 @@ func (s *stringNode) pretty(w io.Writer, prefix string) error {
return err return err
} }
func (s *stringNode) eval(ctx context) (interface{}, error) { func (s *stringNode) eval(ctx *context) (interface{}, error) {
return string(*s), nil return string(*s), nil
} }
@ -292,7 +320,7 @@ func (n *numberNode) pretty(w io.Writer, prefix string) error {
return nil return nil
} }
func (n *numberNode) eval(ctx context) (interface{}, error) { func (n *numberNode) eval(ctx *context) (interface{}, error) {
switch n.t { switch n.t {
case num_int: case num_int:
return n.i, nil return n.i, nil
@ -342,7 +370,7 @@ func (l *listNode) pretty(w io.Writer, prefix string) error {
return nil return nil
} }
func (l *listNode) eval(ctx context) (interface{}, error) { func (l *listNode) eval(ctx *context) (interface{}, error) {
out := make([]interface{}, 0, len(*l)) out := make([]interface{}, 0, len(*l))
for _, n := range *l { for _, n := range *l {
v, err := n.eval(ctx) v, err := n.eval(ctx)
@ -406,7 +434,7 @@ func (o *objectNode) pretty(w io.Writer, prefix string) error {
return nil return nil
} }
func (o *objectNode) eval(ctx context) (interface{}, error) { func (o *objectNode) eval(ctx *context) (interface{}, error) {
out := make(map[string]interface{}, len(*o)) out := make(map[string]interface{}, len(*o))
for name, node := range *o { for name, node := range *o {
v, err := node.eval(ctx) v, err := node.eval(ctx)
@ -441,8 +469,8 @@ func (v *variableNode) pretty(w io.Writer, prefix string) error {
return nil return nil
} }
func (v *variableNode) eval(ctx context) (interface{}, error) { func (v *variableNode) eval(ctx *context) (interface{}, error) {
value, ok := ctx[v.name] value, ok := ctx.get(v.name)
if !ok { if !ok {
return nil, fmt.Errorf("undefined variable: %s", *v) return nil, fmt.Errorf("undefined variable: %s", *v)
} }

@ -19,16 +19,11 @@ func Read(r io.Reader) (*Doc, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
ctx := make(map[string]interface{}) ctx := newContext()
if _, err := tree.eval(ctx); err != nil { if _, err := tree.eval(ctx); err != nil {
return nil, fmt.Errorf("eval error: %s\n", err) return nil, fmt.Errorf("eval error: %s\n", err)
} }
for name, _ := range ctx { return &Doc{items: ctx.public}, nil
if strings.HasPrefix(name, ".") {
delete(ctx, name)
}
}
return &Doc{items: ctx}, nil
} }
// Reads a moon document from a string. This is purely a convenience method; // Reads a moon document from a string. This is purely a convenience method;

@ -0,0 +1 @@
people: [@person_one]

@ -0,0 +1,5 @@
{t_name people}
{t_object_separator :}
{t_list_start [}
{t_variable person_one}
{t_list_end ]}

@ -4,4 +4,3 @@
quoted_string: "this is a quoted string" quoted_string: "this is a quoted string"
bare_string: this is a bare string bare_string: this is a bare string
name with space: that name has spaces in it, and you know what? that's ok!

@ -11,9 +11,3 @@ root:
value: value:
string: string:
this is a bare string this is a bare string
assign:
name:
name with space
value:
string:
that name has spaces in it, and you know what? that's ok!

Loading…
Cancel
Save