Skip to content

Commit

Permalink
Merge pull request #49 from JimLarson/first-cel-service
Browse files Browse the repository at this point in the history
CelService server for cel-go.

Use Go proto libraries exported from cel-spec.

Note that we use a different Go package for the google.rpc.Status message
than is specified in its .proto file.  We do this to avoid a different
and incompatible Go proto library that is brought in by the Bazel rules.
We will revert to the right package when this bug is fixed.
See bazel-contrib/rules_go#1530

Expand common.Source to subsume interpreter.Metadata, for the latter's
eventual deprecation, and to allow the checker inputs to be constructed
without access to the source text.

Introduce a specialized traits.Lister for arrays of ref.Value values.

Implement CelService server and tests.  Includes conversion between
ref.Value types and protos.
  • Loading branch information
JimLarson authored Jul 13, 2018
2 parents b5faa5c + a092cbc commit 97dcd37
Show file tree
Hide file tree
Showing 27 changed files with 1,111 additions and 101 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ if len(errors.GetErrors()) != 0 {
// package (empty string):
typeProvider := types.NewProvider()
env := checker.NewStandardEnv(packages.DefaultPackage, typeProvider, errors)
env.Add(decls.NewIdent("a", decls.Bool),
decls.NewIdent("b", decls.Bool),
decls.NewIdent("c", decls.NewListType(decls.Int)))
c := checker.Check(p, env, "")
env.Add(decls.NewIdent("a", decls.Bool, nil),
decls.NewIdent("b", decls.Bool, nil),
decls.NewIdent("c", decls.NewListType(decls.Int), nil))
c := checker.Check(p, env, "")
if len(errors.GetErrors()) != 0 {
return nil, fmt.Error(errors.ToDisplayString()))
}
Expand Down Expand Up @@ -106,4 +106,4 @@ Disclaimer: This is not an official Google product.
[3]: https://github.com/google/cel-cpp
[4]: https://github.com/google/cel-go/issues
[5]: https://bazel.build
[6]: https://godoc.org/github.com/google/cel-go
[6]: https://godoc.org/github.com/google/cel-go
46 changes: 45 additions & 1 deletion WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,50 @@ go_repository(

git_repository(
name = "com_google_cel_spec",
commit = "3769a0b59441e6a2ebe747154dd1a4c85ce65ae0",
commit = "3c25c4d4ffb504e2c24c1d84196c78ba3ac9e612",
remote = "https://github.com/google/cel-spec.git",
)

new_http_archive(
name = "com_google_googleapis",
url = "https://github.com/googleapis/googleapis/archive/common-protos-1_3_1.zip",
strip_prefix = "googleapis-common-protos-1_3_1/",
build_file_content = """
load('@io_bazel_rules_go//proto:def.bzl', 'go_proto_library')
proto_library(
name = 'rpc_status',
srcs = ['google/rpc/status.proto'],
deps = [
'@com_google_protobuf//:any_proto',
'@com_google_protobuf//:empty_proto'
],
visibility = ['//visibility:public'],
)
go_proto_library(
name = 'rpc_status_go_proto',
# TODO: Switch to the correct import path when bazel rules fixed.
#importpath = 'google.golang.org/genproto/googleapis/rpc/status',
importpath = 'github.com/googleapis/googleapis/google/rpc',
proto = ':rpc_status',
visibility = ['//visibility:public'],
)
"""
)

git_repository(
name = "org_pubref_rules_protobuf",
remote = "https://github.com/pubref/rules_protobuf",
tag = "v0.8.2",
)

load("@org_pubref_rules_protobuf//go:rules.bzl", "go_proto_repositories")
go_proto_repositories()

go_repository(
name = "org_golang_google_grpc",
importpath = "google.golang.org/grpc",
tag = "v1.11.3",
remote = "https://github.com/grpc/grpc-go.git",
)
4 changes: 2 additions & 2 deletions checker/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ go_library(
"//common/packages:go_default_library",
"//common/types:go_default_library",
"//common/types/ref:go_default_library",
"//io:checked_proto",
"//io:syntax_proto",
"//parser:go_default_library",
"@com_github_golang_protobuf//proto:go_default_library",
"@com_google_cel_spec//proto/checked/v1:checked_go_proto",
"@com_google_cel_spec//proto/v1:syntax_go_proto",
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
"@io_bazel_rules_go//proto/wkt:struct_go_proto",
],
Expand Down
4 changes: 2 additions & 2 deletions checker/decls/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ go_library(
"scopes.go",
],
deps = [
"//io:checked_proto",
"//io:syntax_proto",
"@com_google_cel_spec//proto/checked/v1:checked_go_proto",
"@com_google_cel_spec//proto/v1:syntax_go_proto",
"@io_bazel_rules_go//proto/wkt:empty_go_proto",
"@io_bazel_rules_go//proto/wkt:struct_go_proto",
],
Expand Down
3 changes: 3 additions & 0 deletions common/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ go_library(
"source.go",
],
importpath = "github.com/google/cel-go/common",
deps = [
"@com_google_cel_spec//proto/v1:syntax_go_proto",
],
visibility = ["//visibility:public"],
)

Expand Down
2 changes: 1 addition & 1 deletion common/debug/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//common:go_default_library",
"//io:syntax_proto",
"@com_google_cel_spec//proto/v1:syntax_go_proto",
],
)
94 changes: 70 additions & 24 deletions common/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ package common

import (
"strings"

"github.com/google/cel-spec/proto/v1/syntax"
)

// Source interface for filter source contents.
Expand All @@ -35,26 +37,37 @@ type Source interface {
// The raw character offset at which the a location exists given the
// location line and column.
// Returns the line offset and whether the location was found.
CharacterOffset(location Location) (int32, bool)
LocationOffset(location Location) (int32, bool)

// LocationFromOffset translates a raw character offset to a Location, or
// OffsetLocation translates a character offset to a Location, or
// false if the conversion was not feasible.
LocationFromOffset(offset int32) (Location, bool)
OffsetLocation(offset int32) (Location, bool)

// Return a line of content from the source and whether the line was found.
// Return a line of content and whether the line was found.
Snippet(line int) (string, bool)
}

// Ensure the StringSource implements the Source interface.
var _ Source = &StringSource{}
// IdOffset returns the raw character offset of an expression within
// the source, or false if the expression cannot be found.
IdOffset(exprId int64) (int32, bool)

// StringSource type implementation of the Source interface.
type StringSource struct {
// IdLocation returns a Location for the given expression id,
// or false if one cannot be found. It behaves as the obvious
// composition of IdOffset() and OffsetLocation().
IdLocation(exprId int64) (Location, bool)
}

// The sourceImpl type implementation of the Source interface.
type sourceImpl struct {
contents string
description string
lineOffsets []int32
idOffsets map[int64]int32
}

// TODO(jimlarson) "Character offsets" should index the code points
// within the UTF-8 encoded string. It currently indexes bytes.
// Can be accomplished by using rune[] instead of string for contents.

// Create a new Source given the string contents and description.
func NewStringSource(contents string, description string) Source {
// Compute line offsets up front as they are referred to frequently.
Expand All @@ -65,49 +78,78 @@ func NewStringSource(contents string, description string) Source {
offset = offset + int32(len(line)) + 1
offsets[int32(i)] = offset
}
return &StringSource{
return &sourceImpl{
contents: contents,
description: description,
lineOffsets: offsets,
idOffsets: map[int64]int32{},
}
}

func (s *StringSource) Content() string {
func NewInfoSource(info *syntax.SourceInfo) Source {
return &sourceImpl{
contents: "",
description: info.Location,
lineOffsets: info.LineOffsets,
idOffsets: info.Positions,
}
}

func (s *sourceImpl) Content() string {
return s.contents
}

func (s *StringSource) Description() string {
func (s *sourceImpl) Description() string {
return s.description
}

func (s *StringSource) LineOffsets() []int32 {
func (s *sourceImpl) LineOffsets() []int32 {
return s.lineOffsets
}

func (s *StringSource) CharacterOffset(location Location) (int32, bool) {
func (s *sourceImpl) LocationOffset(location Location) (int32, bool) {
if lineOffset, found := s.findLineOffset(location.Line()); found {
return lineOffset + int32(location.Column()), true
}
return -1, false
}

func (s *StringSource) LocationFromOffset(offset int32) (Location, bool) {
func (s *sourceImpl) OffsetLocation(offset int32) (Location, bool) {
line, lineOffset := s.findLine(offset)
return NewLocation(int(line), int(offset-lineOffset)), true
}

func (s *StringSource) Snippet(line int) (string, bool) {
if charStart, found := s.findLineOffset(line); found {
charEnd, found := s.findLineOffset(line + 1)
if found {
return s.contents[charStart : charEnd-1], true
func (s *sourceImpl) Snippet(line int) (string, bool) {
charStart, found := s.findLineOffset(line)
if !found || len(s.contents) == 0 {
return "", false
}
charEnd, found := s.findLineOffset(line + 1)
if found {
return s.contents[charStart : charEnd-1], true
}
return s.contents[charStart:], true
}

func (s *sourceImpl) IdOffset(exprId int64) (int32, bool) {
if offset, found := s.idOffsets[exprId]; found {
return offset, true
}
return -1, false
}

func (s *sourceImpl) IdLocation(exprId int64) (Location, bool) {
if offset, found := s.IdOffset(exprId); found {
if location, found := s.OffsetLocation(offset); found {
return location, true
}
return s.contents[charStart:], true
}
return "", false
return NewLocation(1, 0), false
}

func (s *StringSource) findLineOffset(line int) (int32, bool) {
// findLineOffset returns the offset where the (1-indexed) line begins,
// or false if line doesn't exist.
func (s *sourceImpl) findLineOffset(line int) (int32, bool) {
if line == 1 {
return 0, true
} else if line > 1 && line <= int(len(s.lineOffsets)) {
Expand All @@ -117,7 +159,11 @@ func (s *StringSource) findLineOffset(line int) (int32, bool) {
return -1, false
}

func (s *StringSource) findLine(characterOffset int32) (int32, int32) {
// findLine finds the line that contains the given character offset and
// returns the line number and offset of the beginning of that line.
// Note that the last line is treated as if it contains all offsets
// beyond the end of the actual source.
func (s *sourceImpl) findLine(characterOffset int32) (int32, int32) {
var line int32 = 1
for _, lineOffset := range s.lineOffsets {
if lineOffset > characterOffset {
Expand Down
10 changes: 5 additions & 5 deletions common/source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ func TestStringSource_Description(t *testing.T) {
}
}

// TestStringSource_CharacterOffset make sure that the offsets accurately reflect
// TestStringSource_LocationOffset make sure that the offsets accurately reflect
// the location of a character in source.
func TestStringSource_CharacterOffset(t *testing.T) {
func TestStringSource_LocationOffset(t *testing.T) {
contents := "c.d &&\n\t b.c.arg(10) &&\n\t test(10)"
source := NewStringSource(contents, "offset-test")
expectedLineOffsets := []int32{7, 24, 35}
Expand All @@ -73,14 +73,14 @@ func TestStringSource_CharacterOffset(t *testing.T) {
}
// Ensure that selecting a set of characters across multiple lines works as
// expected.
charStart, _ := source.CharacterOffset(NewLocation(1, 2))
charEnd, _ := source.CharacterOffset(NewLocation(3, 2))
charStart, _ := source.LocationOffset(NewLocation(1, 2))
charEnd, _ := source.LocationOffset(NewLocation(3, 2))
if "d &&\n\t b.c.arg(10) &&\n\t " != string(contents[charStart:charEnd]) {
t.Errorf(unexpectedValue, t.Name(),
string(contents[charStart:charEnd]),
"d &&\n\t b.c.arg(10) &&\n\t ")
}
if _, found := source.CharacterOffset(NewLocation(4, 0)); found {
if _, found := source.LocationOffset(NewLocation(4, 0)); found {
t.Error("Character offset was out of range of source, but still found.")
}
}
Expand Down
6 changes: 3 additions & 3 deletions common/types/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ go_library(
"//common/types/ref:go_default_library",
"//common/types/pb:go_default_library",
"//common/types/traits:go_default_library",
"//io:checked_proto",
"@com_google_cel_spec//proto/checked/v1:checked_go_proto",
"@com_github_golang_protobuf//proto:go_default_library",
"@com_github_golang_protobuf//ptypes:go_default_library",
"@io_bazel_rules_go//proto/wkt:any_go_proto",
Expand Down Expand Up @@ -68,11 +68,11 @@ go_test(
embed = [":go_default_library"],
deps = [
"//common/types/ref:go_default_library",
"//io:syntax_proto",
"//test:go_default_library",
"@com_google_cel_spec//proto/v1:syntax_go_proto",
"@com_github_golang_protobuf//jsonpb:go_default_library",
"@io_bazel_rules_go//proto/wkt:any_go_proto",
"@io_bazel_rules_go//proto/wkt:duration_go_proto",
"@io_bazel_rules_go//proto/wkt:timestamp_go_proto",
],
)
)
Loading

0 comments on commit 97dcd37

Please sign in to comment.