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
# at the top level.
first_name: "jordan"
last_name: "orelli"
first_name: jordan
last_name: orelli
# lists of things should be supported
items: [
"one"
one
2
3.4
["five" 6 7.8]
[five; 6 7.8]
]
# objects should be supported
hash: {key: "value" other_key: "other_value"}
hash: {key: value; other_key: other_value}
other_hash: {
key_1: "one"
key_1: one
key_2: 2
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
@ -37,19 +37,19 @@ repeat_hash: @hash
# intermediate values internal to the config file; they are *not* visible to
# the host program. This is generally useful for composing larger, more
# complicated things.
@hidden_item: "it has a value"
@hidden_item: it has a value
visible_item: @hidden_item
@person_one: {
name: "the first name here"
name: the first name here
age: 28
hometown: "crooklyn"
hometown: crooklyn
}
@person_two: {
name: "the second name here"
name: the second name here
age: 30
hometown: "tha bronx"
hometown: tha bronx
}
people: [@person_one @person_two]

@ -128,6 +128,22 @@ func (l *lexer) unread(r rune) {
}
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.buf = l.buf[0:0]
}
@ -151,6 +167,15 @@ func (l *lexer) acceptRun(chars string) bool {
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 {
r := strings.NewReader(in)
return lex(r)
@ -288,59 +313,62 @@ func lexQuotedString(delim rune) stateFn {
func lexNameOrString(l *lexer) stateFn {
r := l.next()
switch r {
case '\n', ';':
switch {
case r == '\n', r == ';':
l.emit(t_string)
return lexRoot
case ':':
case r == ':':
l.emit(t_name)
l.keep(r)
l.emit(t_object_separator)
return lexRoot
case '\\':
case isSpecial(r):
l.emit(t_string)
l.unread(r)
return lexRoot
case r == '\\':
rr := l.next()
if rr == eof {
return lexErrorf("unexpected eof in string or name")
}
l.keep(rr)
return lexNameOrString
case '#':
return lexComment
case eof:
case r == eof:
l.emit(t_string)
return nil
default:
case unicode.IsGraphic(r):
l.keep(r)
return lexNameOrString
default:
return lexErrorf("unexpected rune in string or name: %c", r)
}
}
func lexVariable(l *lexer) stateFn {
r := l.next()
switch r {
case '\n', ';':
switch {
case unicode.IsSpace(r), r == ';':
l.emit(t_variable)
return lexRoot
case ':':
l.emit(t_variable)
l.keep(r)
l.emit(t_object_separator)
return lexRoot
case '\\':
case r == '\\':
rr := l.next()
if rr == eof {
return lexErrorf("unexpected eof in variable name")
}
l.keep(rr)
return lexVariable
case '#':
return lexComment
case eof:
case isSpecial(r):
l.emit(t_variable)
l.unread(r)
return lexRoot
case r == eof:
l.emit(t_variable)
return nil
default:
case unicode.IsGraphic(r):
l.keep(r)
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 {
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
}
func isSpecial(r rune) bool {
return strings.ContainsRune("[]{}:;#", r)
}

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

@ -19,16 +19,11 @@ func Read(r io.Reader) (*Doc, error) {
if err != nil {
return nil, err
}
ctx := make(map[string]interface{})
ctx := newContext()
if _, err := tree.eval(ctx); err != nil {
return nil, fmt.Errorf("eval error: %s\n", err)
}
for name, _ := range ctx {
if strings.HasPrefix(name, ".") {
delete(ctx, name)
}
}
return &Doc{items: ctx}, nil
return &Doc{items: ctx.public}, nil
}
// 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"
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:
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