Skip to content

Commit

Permalink
Add references to the grammar
Browse files Browse the repository at this point in the history
The special iterator variable ("_") is not supported yet. The _ variable will
be handled by mangling the variable name while parsing the rules (which are
still to come).

Also refactored terms to use type declaration and type switches. All terms are
now represented by underlying Go types without relying on extra structs. The
Kind attribute on Term has been removed in favour of type switches.

Lastly, removing the generated parser from the repository for now. Once the
grammar has stabilized, we can add the generated code back. The diffs were
unpleasant.
  • Loading branch information
tsandall committed Apr 2, 2016
1 parent a2ff423 commit 157e4f0
Show file tree
Hide file tree
Showing 13 changed files with 443 additions and 2,265 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.vscode
opa
opalog/parser.go
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ language: go
go:
- 1.5
- 1.6
install: make deps
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ GO := go
GO15VENDOREXPERIMENT := 1
export GO15VENDOREXPERIMENT

.PHONY: all generate build test clean
.PHONY: all deps generate build test clean

all: build test

deps:
$(GO) install ./vendor/github.com/PuerkitoBio/pigeon
$(GO) install ./vendor/golang.org/x/tools/cmd/goimports

generate:
$(GO) generate

build:
build: generate
$(GO) build -o opa $(LDFLAGS)

test:
test: generate
$(GO) test -v $(PACKAGES)

clean:
Expand Down
17 changes: 5 additions & 12 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ Requirements:

## Getting Started

After cloning the repository, you can run `make all` to build the project and
execute all of the tests. If this succeeds, there should be a binary
in the top directory (opa).
After cloning the repository, run `make deps` to install the parser generator ("pigeon") into your workspace.

Next, run `make all` to build the project and execute all of the tests. If
this succeeds, there should be a new binary in the top level directory ("opa").

Verify the build was successful by running `opa version`.

Expand Down Expand Up @@ -101,12 +102,4 @@ If you need to update the dependencies:
## Opalog
If you need to modify the Opalog syntax you must update opalog/parser.peg
and run `make generate` to re-generate the parser code.
> If you encounter an error because "pigeon" is not installed, run `glide
> rebuild` to build and install the vendored dependencies (which include the
> parser generator). Note, you will need to have [Glide](https://github.com/Masterminds/glide)
> installed for this.
Commit the changes to the parser.peg and parser.go files.
If you need to modify the Opalog syntax you must update opalog/opalog.peg. Both `make build` and `make test` will re-generate the parser but if you want to test the parser generation explicitly you can run `make generate`.
124 changes: 71 additions & 53 deletions opalog/opalog.peg
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ package opalog
//
// BUGS: the escaped forward solidus (`\/`) is not currently handled for strings.
//

func currentLocation(c *current) *Location {
// TODO: Is it possible to propagate file names into the parser?
return NewLocation(c.text, "", c.pos.line, c.pos.col)
}

}

Prog <- _ head:Term tail:( ws Term )* EOF {
Expand All @@ -14,100 +20,112 @@ Prog <- _ head:Term tail:( ws Term )* EOF {
return append([]interface{}{head}, tailSlice...), nil
}

Term <- val:( Composite / Scalar / Var ) {
Term <- val:( Composite / Scalar / Ref / Var ) {
return val, nil
}

Composite <- Object / Array

Scalar <- Number / String / Bool / Null

Key <- Scalar / Var
Key <- Scalar / Ref / Var

Object <- '{' _ head:(Key _ ':' _ Term)? tail:( _ ',' _ Key _ ':' _ Term )* _ '}' {
set := NewKeyValueSet()
var buf [][2]*Term

// Empty object.
if head == nil {
return ObjectTermWithLoc(buf, currentLocation(c)), nil
}

// Non-empty object, first key/value pair.
// The "head" variable is a slice containing exactly 5 elements (see rule definition above):
// [key whitespace colon whitespace key] where the whitespace elements may be nil.
headSlice := head.([]interface{})
buf = append(buf, Item(headSlice[0].(*Term), headSlice[len(headSlice) - 1].(*Term)))

// Non-empty object, remaining key/value pairs.
tailSlice := tail.([]interface{})
for _, v := range tailSlice {
s := v.([]interface{})
// The "s" variable is a slice containing exactly 8 elements (see rule definition above).
// This is similar to the "head" variable."
buf = append(buf, Item(s[3].(*Term), s[len(s) - 1].(*Term)))
}

return ObjectTermWithLoc(buf, currentLocation(c)), nil
}

// Empty object.
if head == nil {
return NewTerm(set, OBJECT, c.text, "", c.pos.line, c.pos.col), nil
}
Array <- '[' _ head:Term? tail:(_ ',' _ Term)* _ ']' {

// Non-empty object, first key/value pair.
// The "head" variable is a slice containing exactly 5 elements (see rule definition above):
// [key whitespace colon whitespace key] where the whitespace elements may be nil.
headSlice := head.([]interface{})
set.Add(NewKeyValue(headSlice[0].(*Term), headSlice[len(headSlice) - 1].(*Term)))
var buf []*Term

// Non-empty object, remaining key/value pairs.
tailSlice := tail.([]interface{})
for _, v := range tailSlice {
s := v.([]interface{})
// The "s" variable is a slice containing exactly 8 elements (see rule definition above).
// This is similar to the "head" variable."
set.Add(NewKeyValue(s[3].(*Term), s[len(s) - 1].(*Term)))
}
// Empty array.
if head == nil {
return ArrayTermWithLoc(buf, currentLocation(c)), nil
}

// Non-empty array, first element.
buf = append(buf, head.(*Term))

result := NewTerm(set, OBJECT, c.text, "", c.pos.line, c.pos.col)
return result, nil
// Non-empty array, remaining elements.
tailSlice := tail.([]interface{})
for _, v := range tailSlice {
s := v.([]interface{})
// The "s" is a slice containing exactly 4 elements (see rule definition above).
// [whitespace comma whitespace value] where the whitespace elements may be nil.
buf = append(buf, s[len(s) - 1].(*Term))
}

return ArrayTermWithLoc(buf, currentLocation(c)), nil
}

Array <- '[' _ head:Term? tail:(_ ',' _ Term)* _ ']' {
Ref <- head:Var tail:( RefDot / RefBracket )+ {
buf := []*Term{head.(*Term)}

// Empty array.
if head == nil {
return NewTerm([]*Term{}, ARRAY, c.text, "", c.pos.line, c.pos.col), nil
}
tailSlice := tail.([]interface{})
for _, v := range tailSlice {
buf = append(buf, v.(*Term))
}

// Non-empty array, first element.
var arr []*Term
arr = append(arr, head.(*Term))
return RefTermWithLoc(buf, currentLocation(c)), nil
}

// Non-empty array, remaining elements.
tailSlice := tail.([]interface{})
for _, v := range tailSlice {
s := v.([]interface{})
// The "s" is a slice containing exactly 4 elements (see rule definition above).
// [whitespace comma whitespace value] where the whitespace elements may be nil.
arr = append(arr, s[len(s) - 1].(*Term))
}
RefDot <- "." val:Var {
// Convert the Var into a string because 'foo.bar.baz' is equivalent to 'foo["bar"]["baz"]'.
return StringTermWithLoc(string(val.(*Term).Value.(Var)), currentLocation(c)), nil
}

result := NewTerm(arr, ARRAY, c.text, "", c.pos.line, c.pos.col)
return result, nil
RefBracket <- "[" val:(Scalar / Var) "]" {
return val, nil
}

Var <- vals:( AsciiLetter (AsciiLetter / DecimalDigit)* ) {
v := &Var{string(c.text)}
t := NewTerm(v, VAR, c.text, "", c.pos.line, c.pos.col)
return t, nil
return VarTermWithLoc(string(c.text), currentLocation(c)), nil
}

Number <- '-'? Integer ( '.' DecimalDigit+ )? Exponent? {
// JSON numbers have the same syntax as Go's, and are parseable using
// strconv.
v, err := strconv.ParseFloat(string(c.text), 64)
t := NewTerm(v, NUMBER, c.text, "", c.pos.line, c.pos.col)
return t, err
return NumberTermWithLoc(v, currentLocation(c)), err
}

String <- '"' ( !EscapedChar . / '\\' EscapeSequence )* '"' {
// TODO : the forward slash (solidus) is not a valid escape in Go, it will
// fail if there's one in the string
v, err := strconv.Unquote(string(c.text))
t := NewTerm(v, STRING, c.text, "", c.pos.line, c.pos.col)
return t, err // v, err
return StringTermWithLoc(v, currentLocation(c)), err
}

Bool <- "true" {
t := NewTerm(true, BOOLEAN, c.text, "", c.pos.line, c.pos.col)
return t, nil
return BooleanTermWithLoc(true, currentLocation(c)), nil
} / "false" {
t := NewTerm(false, BOOLEAN, c.text, "", c.pos.line, c.pos.col)
return t, nil
return BooleanTermWithLoc(false, currentLocation(c)), nil
}

Null <- "null" {
t := NewTerm(nil, NULL, c.text, "", c.pos.line, c.pos.col)
return t, nil
return NullTermWithLoc(currentLocation(c)), nil
}

Integer <- '0' / NonZeroDecimalDigit DecimalDigit*
Expand Down
Loading

0 comments on commit 157e4f0

Please sign in to comment.