Skip to content

Commit ac2ab8f

Browse files
committed
Initial commit. Just has variable resolution.
1 parent f1364c2 commit ac2ab8f

File tree

3 files changed

+149
-2
lines changed

3 files changed

+149
-2
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
1-
# SimpleExpressionParser
2-
A parser for parsing short expressions which can be placed within Discord Bot commands or for incorporating a minimal amount of programming logic in another context.
1+
# Simple Expression Parser
2+
3+
A parser for parsing simple expressions which can be placed within Discord Bot commands or for incorporating a minimal amount of programming logic in another context. The underlying data is stored as an untyped string ready for it's body to be lazily evaluated.
4+
5+
This fits with Discord's model where the data provided could be anything, and the algorithm might not know what the type is until a specific command or function requests a specific type and it can check to see whether the data satisfies those requirements.
6+
7+
I'm currently in the process of porting this over from Azareal/SajuukBot and turning it into a dependency on that end.

arbitraryFunctions.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package sep
2+
3+
var arbitraryFunctions map[string]func(...string)(string,error) = map[string]func(...string)(string,error){
4+
"unixtime": func(_ ...string) (string, error) {
5+
return strconv.FormatInt(time.Now().Unix(),10), nil
6+
},
7+
}
8+
var arbitraryFunctionParams map[string]int = map[string]int{"unixtime":0}
9+
10+
func GetArbitraryFunction(name string) (func(...string)(string,error),bool) {
11+
res, ok := arbitraryFunctions[name]
12+
return res, ok
13+
}
14+
15+
func SetArbitraryFunction(name string, callback func(...string)(string,error), paramCount int) {
16+
arbitraryFunctions[name] = callback
17+
arbitraryFunctionParams[name] = paramCount
18+
}
19+
20+
func HasArbitraryFunction(name string) bool {
21+
_, ok := arbitraryFunctions[name]
22+
return ok
23+
}

sep.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package sep
2+
3+
type Datastore interface {
4+
SetVar(name, value string) error
5+
GetVar(name string) (string, bool)
6+
DeleteVar(name string) error
7+
}
8+
9+
func ResolveVariable(data string, server_data Datastore) (result string, err error) {
10+
if len(data) < 3 {
11+
return "", errors.New("variable name too short x.x")
12+
}
13+
if data[0] == '*' {
14+
data = data[1:]
15+
}
16+
17+
// Validate the variable and break it up into chunks...
18+
var parts []string
19+
var buffer string
20+
var in_brackets bool
21+
for _, char := range data {
22+
if !('a' <= char && char <= 'z') && !('A' <= char && char <= 'Z') && !('0' <= char && char <= '9') && char != '[' && char != ']' && char != '.' && char != '_' && char != '-' {
23+
return "", errors.New("invalid character in variable name")
24+
}
25+
26+
if in_brackets {
27+
if char == ']' {
28+
if buffer != "" {
29+
parts = append(parts,buffer)
30+
buffer = ""
31+
}
32+
in_brackets = false
33+
continue
34+
}
35+
}
36+
37+
if char == '[' {
38+
if buffer != "" {
39+
parts = append(parts,buffer)
40+
buffer = ""
41+
}
42+
in_brackets = true
43+
} else if char == '.' {
44+
if buffer != "" {
45+
parts = append(parts,buffer)
46+
buffer = ""
47+
}
48+
} else {
49+
buffer += string(char)
50+
}
51+
}
52+
if buffer != "" {
53+
parts = append(parts,buffer)
54+
}
55+
56+
partCount := len(parts)
57+
data_value, ok := server_data.GetVar(parts[0])
58+
if !ok {
59+
return "", errors.New("this variable doesn't exist o.o")
60+
}
61+
data_type := detectType(data_value)
62+
if data_type == "string" && partCount == 2 {
63+
conv_data, err := strconv.Atoi(parts[1])
64+
if err != nil {
65+
return "", errors.New("indices can only be integers x.x")
66+
}
67+
if len(data_value) <= conv_data {
68+
return "", errors.New("index out of range")
69+
}
70+
return string(data_value[conv_data]), nil
71+
}
72+
73+
if partCount > 1 && data_type != "map" && data_type != "list" && data_type != "variable" {
74+
return "", errors.New("type " + data_type + " cannot have subelements")
75+
} else {
76+
return data_value, nil
77+
}
78+
79+
var n int
80+
for _, part := range parts {
81+
if n > 5 {
82+
return "", errors.New("too many nested subelements")
83+
}
84+
switch(data_type) {
85+
case "map":
86+
pmap, err := mapParser(data_value)
87+
if err != nil {
88+
return "", errors.New("invalid map x.x")
89+
}
90+
pdata, ok := pmap[part]
91+
if !ok {
92+
return "", errors.New("field does not exist in map")
93+
}
94+
data_value = pdata
95+
case "list":
96+
plist, err := listParser(data_value)
97+
if err != nil {
98+
return "", errors.New("invalid list x.x")
99+
}
100+
list_index, err := strconv.Atoi(part)
101+
if err != nil {
102+
return "", errors.New("list indices must be integers o.o")
103+
}
104+
if len(plist) <= list_index {
105+
return "", errors.New("index out of range o.o")
106+
}
107+
data_value = plist[list_index]
108+
case "variable":
109+
data_value, ok := server_data.GetVar(parts[0])
110+
if !ok {
111+
return "", errors.New("subvariable doesn't exist o.o")
112+
}
113+
data_type = detectType(data_value)
114+
}
115+
n++
116+
}
117+
118+
return data_value, nil
119+
}

0 commit comments

Comments
 (0)