Skip to content

Commit 563bbe1

Browse files
committed
First end-to-end test (addition of numbers)
1 parent c0060af commit 563bbe1

9 files changed

+458
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*~

desugarer.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Copyright 2016 Google Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package jsonnet
18+
19+
func desugar(ast astNode, objLevel int) (astNode, error) {
20+
return ast, nil
21+
}
22+
23+
func desugarFile(ast astNode) (astNode, error) {
24+
ast, err := desugar(ast, 0)
25+
if err != nil {
26+
return nil, err
27+
}
28+
// TODO(dcunnin): wrap in std local
29+
return ast, nil
30+
}

desugarer_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
Copyright 2016 Google Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package jsonnet

interpreter.go

+234
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
/*
2+
Copyright 2016 Google Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package jsonnet
18+
19+
import (
20+
"bytes"
21+
"fmt"
22+
"math"
23+
)
24+
25+
// Misc top-level stuff
26+
27+
type vmExt struct {
28+
value string
29+
isCode bool
30+
}
31+
32+
type vmExtMap map[string]vmExt
33+
34+
type RuntimeError struct {
35+
StackTrace []traceFrame
36+
Msg string
37+
}
38+
39+
func makeRuntimeError(msg string) RuntimeError {
40+
return RuntimeError{
41+
Msg: msg,
42+
}
43+
}
44+
45+
func (err RuntimeError) Error() string {
46+
// TODO(dcunnin): Include stacktrace.
47+
return err.Msg
48+
}
49+
50+
// Values and state
51+
52+
type bindingFrame map[*identifier]thunk
53+
54+
type value interface {
55+
}
56+
57+
type valueString struct {
58+
value string
59+
}
60+
61+
func makeValueString(v string) *valueString {
62+
return &valueString{value: v}
63+
}
64+
65+
type valueBoolean struct {
66+
value bool
67+
}
68+
69+
func makeValueBoolean(v bool) *valueBoolean {
70+
return &valueBoolean{value: v}
71+
}
72+
73+
type valueNumber struct {
74+
value float64
75+
}
76+
77+
func makeValueNumber(v float64) *valueNumber {
78+
return &valueNumber{value: v}
79+
}
80+
81+
// TODO(dcunnin): Maybe intern values null, true, and false?
82+
type valueNull struct {
83+
}
84+
85+
func makeValueNull() *valueNull {
86+
return &valueNull{}
87+
}
88+
89+
type thunk struct {
90+
Content value // nil if not filled
91+
Name *identifier
92+
UpValues bindingFrame
93+
Self value
94+
Offset int
95+
Body astNode
96+
}
97+
98+
func makeThunk(name *identifier, self *value, offset int, body astNode) *thunk {
99+
return &thunk{
100+
Name: name,
101+
Self: self,
102+
Offset: offset,
103+
Body: body,
104+
}
105+
}
106+
107+
func (t *thunk) fill(v value) {
108+
t.Content = v
109+
t.Self = nil
110+
t.UpValues = make(bindingFrame) // clear the map
111+
}
112+
113+
type valueArray struct {
114+
Elements []thunk
115+
}
116+
117+
func makeValueArray(elements []thunk) *valueArray {
118+
return &valueArray{
119+
Elements: elements,
120+
}
121+
}
122+
123+
// TODO(dcunnin): SimpleObject
124+
// TODO(dcunnin): ExtendedObject
125+
// TODO(dcunnin): ComprehensionObject
126+
// TODO(dcunnin): Closure
127+
128+
// The stack
129+
130+
type traceFrame struct {
131+
Loc LocationRange
132+
Name string
133+
}
134+
135+
type callFrame struct {
136+
bindings bindingFrame
137+
}
138+
139+
type callStack struct {
140+
Calls int
141+
Limit int
142+
Stack []callFrame
143+
}
144+
145+
func makeCallStack(limit int) callStack {
146+
return callStack{
147+
Calls: 0,
148+
Limit: limit,
149+
}
150+
}
151+
152+
// TODO(dcunnin): Add import callbacks.
153+
// TODO(dcunnin): Add string output.
154+
// TODO(dcunnin): Add multi output.
155+
156+
type interpreter struct {
157+
Stack callStack
158+
ExternalVars vmExtMap
159+
}
160+
161+
func (this interpreter) execute(ast_ astNode) (value, error) {
162+
// TODO(dcunnin): All the other cases...
163+
switch ast := ast_.(type) {
164+
case *astBinary:
165+
// TODO(dcunnin): Assume it's + on numbers for now
166+
leftVal, err := this.execute(ast.left)
167+
if err != nil {
168+
return nil, err
169+
}
170+
leftNum := leftVal.(*valueNumber).value
171+
rightVal, err := this.execute(ast.right)
172+
if err != nil {
173+
return nil, err
174+
}
175+
rightNum := rightVal.(*valueNumber).value
176+
return makeValueNumber(leftNum + rightNum), nil
177+
case *astLiteralNull:
178+
return makeValueNull(), nil
179+
case *astLiteralBoolean:
180+
return makeValueBoolean(ast.value), nil
181+
case *astLiteralNumber:
182+
return makeValueNumber(ast.value), nil
183+
default:
184+
return nil, makeRuntimeError("Executing this AST type not implemented yet.")
185+
}
186+
}
187+
188+
func unparseNumber(v float64) string {
189+
if v == math.Floor(v) {
190+
return fmt.Sprintf("%.0f", v)
191+
} else {
192+
// See "What Every Computer Scientist Should Know About Floating-Point Arithmetic"
193+
// Theorem 15
194+
// http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
195+
return fmt.Sprintf("%.17g", v)
196+
}
197+
}
198+
199+
func (this interpreter) manifestJson(
200+
v_ value, multiline bool, indent string, buf *bytes.Buffer) error {
201+
// TODO(dcunnin): All the other types...
202+
switch v := v_.(type) {
203+
case *valueBoolean:
204+
if v.value {
205+
buf.WriteString("true")
206+
} else {
207+
buf.WriteString("false")
208+
}
209+
case *valueNull:
210+
buf.WriteString("null")
211+
case *valueNumber:
212+
buf.WriteString(unparseNumber(v.value))
213+
default:
214+
return makeRuntimeError("Manifesting this value not implemented yet.")
215+
}
216+
return nil
217+
}
218+
219+
func execute(ast astNode, ext vmExtMap, maxStack int) (string, error) {
220+
theInterpreter := interpreter{
221+
Stack: makeCallStack(maxStack),
222+
ExternalVars: ext,
223+
}
224+
result, err := theInterpreter.execute(ast)
225+
if err != nil {
226+
return "", err
227+
}
228+
var buffer bytes.Buffer
229+
err = theInterpreter.manifestJson(result, true, "", &buffer)
230+
if err != nil {
231+
return "", err
232+
}
233+
return buffer.String(), nil
234+
}

interpreter_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
Copyright 2016 Google Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package jsonnet

main.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
Copyright 2016 Google Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package jsonnet
18+
19+
// Note: There are no garbage collection params because we're using the native Go garbage collector.
20+
type Vm struct {
21+
maxStack int
22+
maxTrace int
23+
ext vmExtMap
24+
}
25+
26+
func MakeVm() *Vm {
27+
return &Vm{
28+
maxStack: 500,
29+
maxTrace: 20,
30+
}
31+
}
32+
33+
func (vm *Vm) ExtVar(key string, val string) {
34+
vm.ext[key] = vmExt{value: val, isCode: false}
35+
}
36+
37+
func (vm *Vm) ExtCode(key string, val string) {
38+
vm.ext[key] = vmExt{value: val, isCode: true}
39+
}
40+
41+
func (vm *Vm) EvaluateSnippet(filename string, snippet string) (string, error) {
42+
tokens, err := lex(filename, snippet)
43+
if err != nil {
44+
return "", err
45+
}
46+
ast, err := parse(tokens)
47+
if err != nil {
48+
return "", err
49+
}
50+
ast, err = desugarFile(ast)
51+
output, err := execute(ast, vm.ext, vm.maxStack)
52+
if err != nil {
53+
return "", err
54+
}
55+
return output, nil
56+
}

0 commit comments

Comments
 (0)