Skip to content

Commit 97debac

Browse files
authored
Merge pull request #11 from vilterp/bool-ops
Bool ops
2 parents f04d21b + 0522ca0 commit 97debac

File tree

13 files changed

+237
-35
lines changed

13 files changed

+237
-35
lines changed

pkg/client.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ package treesql
44
// this should pretty much be the same API as TreeSQLClient.js
55

66
import (
7+
"fmt"
78
"log"
89

10+
"encoding/json"
11+
912
"github.com/gorilla/websocket"
1013
"github.com/pkg/errors"
1114
)
@@ -100,10 +103,11 @@ func (conn *Client) handleIncoming() {
100103
defer conn.webSocketConn.Close()
101104
for {
102105
parsedMessage := &BasicChannelMessage{}
103-
err := conn.webSocketConn.ReadJSON(&parsedMessage)
106+
_, rawMsg, err := conn.webSocketConn.ReadMessage()
107+
json.Unmarshal(rawMsg, parsedMessage)
104108

105109
if err != nil {
106-
log.Println("error in handleIncoming:", err)
110+
log.Println("error in handleIncoming: ReadJSON:", err)
107111
close(conn.incomingMessages)
108112
conn.ServerClosed <- true
109113
return
@@ -140,19 +144,22 @@ func (conn *Client) LiveQuery(query string) (*basicInitialResult, *ClientChannel
140144
if update.InitialResultMessage != nil {
141145
return update.InitialResultMessage, channel, nil
142146
}
143-
return nil, nil, errors.New("query result neither error nor initial result")
147+
return nil, nil, fmt.Errorf("query result neither error nor initial result")
144148
}
145149

146150
func (conn *Client) Query(query string) (*basicInitialResult, error) {
147151
resultChan := conn.RunStatement(query)
148152
update := <-resultChan.Updates
153+
if update == nil {
154+
return nil, fmt.Errorf("update is nil")
155+
}
149156
if update.ErrorMessage != nil {
150157
return nil, errors.New(*update.ErrorMessage)
151158
}
152159
if update.InitialResultMessage != nil {
153160
return update.InitialResultMessage, nil
154161
}
155-
return nil, errors.New("query result neither error nor initial result")
162+
return nil, fmt.Errorf("query result neither error nor initial result")
156163
}
157164

158165
func (conn *Client) Exec(statement string) (string, error) {
@@ -163,5 +170,5 @@ func (conn *Client) Exec(statement string) (string, error) {
163170
} else if update.AckMessage != nil {
164171
return *update.AckMessage, nil
165172
}
166-
return "", errors.New("exec result neither error nor ack")
173+
return "", fmt.Errorf("exec result neither error nor ack")
167174
}

pkg/connection.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ func (conn *connection) writeMessagesToSocket() {
5050
bufWriter := bufio.NewWriter(writer)
5151

5252
if err := msg.toVal().WriteAsJSON(bufWriter, msg.getCaller()); err != nil {
53-
clog.Println(conn, "error writing msg to conn:", err)
53+
clog.Println(conn, "error writing msg to conn: writing value: ", err)
5454
}
5555
if err := bufWriter.Flush(); err != nil {
56-
clog.Println(conn, "error writing msg to conn:", err)
56+
clog.Println(conn, "error writing msg to conn: flusing buffer: ", err)
5757
}
5858
if err := writer.Close(); err != nil {
59-
clog.Println(conn, "error writing msg to conn:", err)
59+
clog.Println(conn, "error writing msg to conn: closing writer: ", err)
6060
}
6161
}
6262
}

pkg/lang/builtins.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,47 @@ func init() {
3636
}, nil
3737
},
3838
})
39+
BuiltinsScope.Add("filter", &VBuiltin{
40+
Name: "filter",
41+
Params: []Param{
42+
{"iter", NewTIterator(NewTVar("A"))},
43+
{"func", &tFunction{
44+
params: []Param{{"x", NewTVar("A")}},
45+
retType: TBool,
46+
}},
47+
},
48+
RetType: NewTIterator(NewTVar("A")),
49+
Impl: func(interp Caller, args []Value) (Value, error) {
50+
f := mustBeVFunction(args[1])
51+
return &VIteratorRef{
52+
iterator: &filterIterator{
53+
innerIterator: mustBeVIteratorRef(args[0]).iterator,
54+
f: f,
55+
},
56+
ofType: f.GetRetType(),
57+
}, nil
58+
},
59+
})
60+
BuiltinsScope.Add("strEq", &VBuiltin{
61+
Name: "strEq",
62+
Params: []Param{{"a", TString}, {"b", TString}},
63+
RetType: TBool,
64+
Impl: func(interp Caller, args []Value) (Value, error) {
65+
left := mustBeVString(args[0])
66+
right := mustBeVString(args[1])
67+
return NewVBool(left == right), nil
68+
},
69+
})
70+
BuiltinsScope.Add("intEq", &VBuiltin{
71+
Name: "intEq",
72+
Params: []Param{{"a", TInt}, {"b", TInt}},
73+
RetType: TBool,
74+
Impl: func(interp Caller, args []Value) (Value, error) {
75+
left := mustBeVInt(args[0])
76+
right := mustBeVInt(args[1])
77+
return NewVBool(left == right), nil
78+
},
79+
})
3980

4081
BuiltinsTypeScope = BuiltinsScope.toTypeScope()
4182
}

pkg/lang/expr.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,9 @@ func (ma *EMemberAccess) Evaluate(interp *interpreter) (Value, error) {
360360
}
361361
return val, nil
362362
default:
363-
return nil, fmt.Errorf("member access on a non-record: %s", ma.Format())
363+
return nil, fmt.Errorf(
364+
"member access on a non-record: %s value: %s", ma.Format(), recVal,
365+
)
364366
}
365367
}
366368

pkg/lang/interpreter_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,29 @@ func TestInterpreter(t *testing.T) {
108108
typ: TInt,
109109
val: "7",
110110
},
111+
// Equality functions
112+
{
113+
expr: &EFuncCall{
114+
funcName: "strEq",
115+
args: []Expr{
116+
NewStringLit("foo"),
117+
NewStringLit("foo"),
118+
},
119+
},
120+
typ: TBool,
121+
val: "true",
122+
},
123+
{
124+
expr: &EFuncCall{
125+
funcName: "intEq",
126+
args: []Expr{
127+
NewIntLit(4),
128+
NewIntLit(5),
129+
},
130+
},
131+
typ: TBool,
132+
val: "false",
133+
},
111134
}
112135

113136
typeScope := userRootScope.toTypeScope()

pkg/lang/iterator.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,46 @@ func (mi *mapIterator) Close() error {
3030
return mi.innerIterator.Close()
3131
}
3232

33+
// Filter iterator
34+
35+
type filterIterator struct {
36+
innerIterator Iterator
37+
f vFunction
38+
}
39+
40+
func (fi *filterIterator) Next(c Caller) (Value, error) {
41+
for {
42+
// Get the next value.
43+
next, err := fi.innerIterator.Next(c)
44+
var isEOE bool
45+
switch err.(type) {
46+
case *endOfIteration:
47+
isEOE = true
48+
default:
49+
if err != nil {
50+
return nil, err
51+
}
52+
}
53+
// Check for end of iteration.
54+
if isEOE {
55+
return nil, EndOfIteration
56+
}
57+
// Call the func.
58+
res, err := c.Call(fi.f, []Value{next})
59+
if err != nil {
60+
return nil, err
61+
}
62+
// Return the val if true.
63+
if *mustBeVBool(res) {
64+
return next, nil
65+
}
66+
}
67+
}
68+
69+
func (fi *filterIterator) Close() error {
70+
return fi.innerIterator.Close()
71+
}
72+
3373
// Array iterator
3474

3575
type arrayIterator struct {

pkg/lang/type.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@ func (tInt) matches(other Type) (bool, typeVarBindings) {
6060

6161
func (ti *tInt) substitute(typeVarBindings) (Type, bool, error) { return ti, true, nil }
6262

63+
// Bool
64+
65+
type tBool struct{}
66+
67+
var TBool = &tBool{}
68+
var _ Type = TBool
69+
70+
func (tBool) Format() pp.Doc {
71+
return pp.Text("bool")
72+
}
73+
74+
func (tBool) matches(other Type) (bool, typeVarBindings) {
75+
return other == TBool, nil
76+
}
77+
78+
func (tb *tBool) substitute(typeVarBindings) (Type, bool, error) { return tb, true, nil }
79+
6380
// String
6481

6582
type tString struct{}

pkg/lang/value.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,41 @@ func mustBeVInt(v Value) *VInt {
5151
return i
5252
}
5353

54+
// Bool
55+
56+
type VBool bool
57+
58+
var _ Value = NewVBool(false)
59+
60+
func NewVBool(b bool) *VBool {
61+
val := VBool(b)
62+
return &val
63+
}
64+
65+
func (v *VBool) Format() pp.Doc {
66+
if *v {
67+
return pp.Text("true")
68+
}
69+
return pp.Text("false")
70+
}
71+
72+
func (v *VBool) GetType() Type {
73+
return TBool
74+
}
75+
76+
func (v *VBool) WriteAsJSON(w *bufio.Writer, _ Caller) error {
77+
_, err := w.WriteString(v.Format().String())
78+
return err
79+
}
80+
81+
func mustBeVBool(v Value) *VBool {
82+
b, ok := v.(*VBool)
83+
if !ok {
84+
panic(fmt.Sprintf("not a bool: %s", v.Format()))
85+
}
86+
return b
87+
}
88+
5489
// String
5590

5691
type VString string
@@ -143,7 +178,9 @@ func (v *VRecord) WriteAsJSON(w *bufio.Writer, c Caller) error {
143178
w.WriteString(",")
144179
}
145180
w.WriteString(fmt.Sprintf("%#v:", name))
146-
val.WriteAsJSON(w, c)
181+
if err := val.WriteAsJSON(w, c); err != nil {
182+
return err
183+
}
147184
idx++
148185
}
149186
w.WriteString("}")

pkg/lang/value_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,19 @@ func TestWriteAsJSON(t *testing.T) {
5757
"[2,3,4]",
5858
"",
5959
},
60+
{
61+
&VIteratorRef{
62+
ofType: TInt,
63+
iterator: NewArrayIterator([]Value{}),
64+
},
65+
"[]",
66+
"",
67+
},
68+
{
69+
NewVBool(true),
70+
"true",
71+
"",
72+
},
6073
{
6174
&VBuiltin{},
6275
"",

pkg/plan.go

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,44 @@ import (
77
)
88

99
func (s *schema) planSelect(query *Select) (lang.Expr, error) {
10-
if query.Where != nil {
11-
return nil, fmt.Errorf("don't know how to plan queries with WHERE yet")
12-
}
13-
1410
tableDesc, ok := s.tables[query.Table]
1511
if !ok {
1612
return nil, fmt.Errorf("no such table: %s", query.Table)
1713
}
1814

15+
// Scan the table.
16+
var innermostExpr lang.Expr
17+
innermostExpr = lang.NewMemberAccess(
18+
lang.NewMemberAccess(
19+
lang.NewMemberAccess(
20+
lang.NewVar("tables"),
21+
query.Table,
22+
),
23+
tableDesc.primaryKey,
24+
),
25+
"scan",
26+
)
27+
28+
if query.Where != nil {
29+
innermostExpr = lang.NewFuncCall("filter", []lang.Expr{
30+
innermostExpr,
31+
lang.NewELambda(
32+
[]lang.Param{
33+
{
34+
Name: "row",
35+
Typ: tableDesc.getType(),
36+
},
37+
},
38+
// TODO: use intEq if it's not a string...
39+
lang.NewFuncCall("strEq", []lang.Expr{
40+
lang.NewMemberAccess(lang.NewVar("row"), query.Where.ColumnName),
41+
lang.NewStringLit(query.Where.Value),
42+
}),
43+
lang.TBool,
44+
),
45+
})
46+
}
47+
1948
// Get types and expressions for selectinos.
2049
types := map[string]lang.Type{}
2150
exprs := map[string]lang.Expr{}
@@ -36,16 +65,7 @@ func (s *schema) planSelect(query *Select) (lang.Expr, error) {
3665

3766
// Build expression: a scan on the primary key.
3867
return lang.NewFuncCall("map", []lang.Expr{
39-
lang.NewMemberAccess(
40-
lang.NewMemberAccess(
41-
lang.NewMemberAccess(
42-
lang.NewVar("tables"),
43-
query.Table,
44-
),
45-
tableDesc.primaryKey,
46-
),
47-
"scan",
48-
),
68+
innermostExpr,
4969
lang.NewELambda(
5070
[]lang.Param{
5171
{

0 commit comments

Comments
 (0)