mirror of
https://github.com/google/go-jsonnet.git
synced 2025-09-28 17:01:02 +02:00
First end-to-end test (addition of numbers)
This commit is contained in:
parent
c0060affd2
commit
563bbe12f4
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*~
|
30
desugarer.go
Normal file
30
desugarer.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jsonnet
|
||||||
|
|
||||||
|
func desugar(ast astNode, objLevel int) (astNode, error) {
|
||||||
|
return ast, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func desugarFile(ast astNode) (astNode, error) {
|
||||||
|
ast, err := desugar(ast, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// TODO(dcunnin): wrap in std local
|
||||||
|
return ast, nil
|
||||||
|
}
|
17
desugarer_test.go
Normal file
17
desugarer_test.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jsonnet
|
234
interpreter.go
Normal file
234
interpreter.go
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jsonnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Misc top-level stuff
|
||||||
|
|
||||||
|
type vmExt struct {
|
||||||
|
value string
|
||||||
|
isCode bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type vmExtMap map[string]vmExt
|
||||||
|
|
||||||
|
type RuntimeError struct {
|
||||||
|
StackTrace []traceFrame
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRuntimeError(msg string) RuntimeError {
|
||||||
|
return RuntimeError{
|
||||||
|
Msg: msg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err RuntimeError) Error() string {
|
||||||
|
// TODO(dcunnin): Include stacktrace.
|
||||||
|
return err.Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values and state
|
||||||
|
|
||||||
|
type bindingFrame map[*identifier]thunk
|
||||||
|
|
||||||
|
type value interface {
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueString struct {
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeValueString(v string) *valueString {
|
||||||
|
return &valueString{value: v}
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueBoolean struct {
|
||||||
|
value bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeValueBoolean(v bool) *valueBoolean {
|
||||||
|
return &valueBoolean{value: v}
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueNumber struct {
|
||||||
|
value float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeValueNumber(v float64) *valueNumber {
|
||||||
|
return &valueNumber{value: v}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(dcunnin): Maybe intern values null, true, and false?
|
||||||
|
type valueNull struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeValueNull() *valueNull {
|
||||||
|
return &valueNull{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type thunk struct {
|
||||||
|
Content value // nil if not filled
|
||||||
|
Name *identifier
|
||||||
|
UpValues bindingFrame
|
||||||
|
Self value
|
||||||
|
Offset int
|
||||||
|
Body astNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeThunk(name *identifier, self *value, offset int, body astNode) *thunk {
|
||||||
|
return &thunk{
|
||||||
|
Name: name,
|
||||||
|
Self: self,
|
||||||
|
Offset: offset,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *thunk) fill(v value) {
|
||||||
|
t.Content = v
|
||||||
|
t.Self = nil
|
||||||
|
t.UpValues = make(bindingFrame) // clear the map
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueArray struct {
|
||||||
|
Elements []thunk
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeValueArray(elements []thunk) *valueArray {
|
||||||
|
return &valueArray{
|
||||||
|
Elements: elements,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(dcunnin): SimpleObject
|
||||||
|
// TODO(dcunnin): ExtendedObject
|
||||||
|
// TODO(dcunnin): ComprehensionObject
|
||||||
|
// TODO(dcunnin): Closure
|
||||||
|
|
||||||
|
// The stack
|
||||||
|
|
||||||
|
type traceFrame struct {
|
||||||
|
Loc LocationRange
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type callFrame struct {
|
||||||
|
bindings bindingFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
type callStack struct {
|
||||||
|
Calls int
|
||||||
|
Limit int
|
||||||
|
Stack []callFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCallStack(limit int) callStack {
|
||||||
|
return callStack{
|
||||||
|
Calls: 0,
|
||||||
|
Limit: limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(dcunnin): Add import callbacks.
|
||||||
|
// TODO(dcunnin): Add string output.
|
||||||
|
// TODO(dcunnin): Add multi output.
|
||||||
|
|
||||||
|
type interpreter struct {
|
||||||
|
Stack callStack
|
||||||
|
ExternalVars vmExtMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this interpreter) execute(ast_ astNode) (value, error) {
|
||||||
|
// TODO(dcunnin): All the other cases...
|
||||||
|
switch ast := ast_.(type) {
|
||||||
|
case *astBinary:
|
||||||
|
// TODO(dcunnin): Assume it's + on numbers for now
|
||||||
|
leftVal, err := this.execute(ast.left)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
leftNum := leftVal.(*valueNumber).value
|
||||||
|
rightVal, err := this.execute(ast.right)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rightNum := rightVal.(*valueNumber).value
|
||||||
|
return makeValueNumber(leftNum + rightNum), nil
|
||||||
|
case *astLiteralNull:
|
||||||
|
return makeValueNull(), nil
|
||||||
|
case *astLiteralBoolean:
|
||||||
|
return makeValueBoolean(ast.value), nil
|
||||||
|
case *astLiteralNumber:
|
||||||
|
return makeValueNumber(ast.value), nil
|
||||||
|
default:
|
||||||
|
return nil, makeRuntimeError("Executing this AST type not implemented yet.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unparseNumber(v float64) string {
|
||||||
|
if v == math.Floor(v) {
|
||||||
|
return fmt.Sprintf("%.0f", v)
|
||||||
|
} else {
|
||||||
|
// See "What Every Computer Scientist Should Know About Floating-Point Arithmetic"
|
||||||
|
// Theorem 15
|
||||||
|
// http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
|
||||||
|
return fmt.Sprintf("%.17g", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this interpreter) manifestJson(
|
||||||
|
v_ value, multiline bool, indent string, buf *bytes.Buffer) error {
|
||||||
|
// TODO(dcunnin): All the other types...
|
||||||
|
switch v := v_.(type) {
|
||||||
|
case *valueBoolean:
|
||||||
|
if v.value {
|
||||||
|
buf.WriteString("true")
|
||||||
|
} else {
|
||||||
|
buf.WriteString("false")
|
||||||
|
}
|
||||||
|
case *valueNull:
|
||||||
|
buf.WriteString("null")
|
||||||
|
case *valueNumber:
|
||||||
|
buf.WriteString(unparseNumber(v.value))
|
||||||
|
default:
|
||||||
|
return makeRuntimeError("Manifesting this value not implemented yet.")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func execute(ast astNode, ext vmExtMap, maxStack int) (string, error) {
|
||||||
|
theInterpreter := interpreter{
|
||||||
|
Stack: makeCallStack(maxStack),
|
||||||
|
ExternalVars: ext,
|
||||||
|
}
|
||||||
|
result, err := theInterpreter.execute(ast)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
err = theInterpreter.manifestJson(result, true, "", &buffer)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return buffer.String(), nil
|
||||||
|
}
|
17
interpreter_test.go
Normal file
17
interpreter_test.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jsonnet
|
56
main.go
Normal file
56
main.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jsonnet
|
||||||
|
|
||||||
|
// Note: There are no garbage collection params because we're using the native Go garbage collector.
|
||||||
|
type Vm struct {
|
||||||
|
maxStack int
|
||||||
|
maxTrace int
|
||||||
|
ext vmExtMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeVm() *Vm {
|
||||||
|
return &Vm{
|
||||||
|
maxStack: 500,
|
||||||
|
maxTrace: 20,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *Vm) ExtVar(key string, val string) {
|
||||||
|
vm.ext[key] = vmExt{value: val, isCode: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *Vm) ExtCode(key string, val string) {
|
||||||
|
vm.ext[key] = vmExt{value: val, isCode: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *Vm) EvaluateSnippet(filename string, snippet string) (string, error) {
|
||||||
|
tokens, err := lex(filename, snippet)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ast, err := parse(tokens)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ast, err = desugarFile(ast)
|
||||||
|
output, err := execute(ast, vm.ext, vm.maxStack)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return output, nil
|
||||||
|
}
|
57
main_test.go
Normal file
57
main_test.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jsonnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Just a few simple sanity tests for now. Eventually we'll share end-to-end tests with the C++
|
||||||
|
// implementation but unsure if that should be done here or via some external framework.
|
||||||
|
|
||||||
|
type mainTest struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
golden string
|
||||||
|
errString string
|
||||||
|
}
|
||||||
|
|
||||||
|
var mainTests = []mainTest{
|
||||||
|
{"numeric_literal", "100", "100", ""},
|
||||||
|
{"boolean_literal", "true", "true", ""},
|
||||||
|
{"simple_arith1", "3 + 3", "6", ""},
|
||||||
|
{"simple_arith2", "3 + 3 + 3", "9", ""},
|
||||||
|
{"simple_arith3", "(3 + 3) + (3 + 3)", "12", ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(t *testing.T) {
|
||||||
|
for _, test := range mainTests {
|
||||||
|
vm := MakeVm()
|
||||||
|
output, err := vm.EvaluateSnippet(test.name, test.input)
|
||||||
|
var errString string
|
||||||
|
if err != nil {
|
||||||
|
errString = err.Error()
|
||||||
|
}
|
||||||
|
if errString != test.errString {
|
||||||
|
t.Errorf("%s: error result does not match. got\n\t%+v\nexpected\n\t%+v",
|
||||||
|
test.input, errString, test.errString)
|
||||||
|
}
|
||||||
|
if err == nil && output != test.golden {
|
||||||
|
t.Errorf("%s: got\n\t%+v\nexpected\n\t%+v", test.name, output, test.golden)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
static_analyzer.go
Normal file
29
static_analyzer.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jsonnet
|
||||||
|
|
||||||
|
// TODO(dcunnin): Check for invalid use of self, super, and bound variables.
|
||||||
|
// TODO(dcunnin): Compute free variables at each AST.
|
||||||
|
func analyseVisit(ast astNode, inObject bool, vars identifierSet) (identifierSet, error) {
|
||||||
|
var r identifierSet
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func analyse(ast astNode) error {
|
||||||
|
_, err := analyseVisit(ast, false, NewidentifierSet())
|
||||||
|
return err
|
||||||
|
}
|
17
static_analyzer_test.go
Normal file
17
static_analyzer_test.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jsonnet
|
Loading…
x
Reference in New Issue
Block a user