aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/query
diff options
context:
space:
mode:
authorJP Appel <jeanpierre.appel01@gmail.com>2025-06-24 19:14:00 -0400
committerJP Appel <jeanpierre.appel01@gmail.com>2025-06-24 19:14:00 -0400
commit173788333e6b14a1b2bdbb127874988bb62bce8d (patch)
treeab3dfbf095c7f6cff0e163ccb5b3a071a8af39a6 /pkg/query
parente477518bd00b238808ea41b3f2fe829dbcac3b0c (diff)
Add first pass of query compiler
Diffstat (limited to 'pkg/query')
-rw-r--r--pkg/query/compiler.go147
1 files changed, 147 insertions, 0 deletions
diff --git a/pkg/query/compiler.go b/pkg/query/compiler.go
new file mode 100644
index 0000000..d566b05
--- /dev/null
+++ b/pkg/query/compiler.go
@@ -0,0 +1,147 @@
+package query
+
+import (
+ "fmt"
+ "strings"
+)
+
+const MAX_CLAUSE_DEPTH int = 16
+
+func (stmt Statement) Compile(b *strings.Builder) (*string, error) {
+ if stmt.Negated {
+ b.WriteString("NOT ")
+ }
+
+ switch stmt.Category {
+ case CAT_TITLE:
+ b.WriteString("title ")
+ case CAT_AUTHOR:
+ b.WriteString("author ")
+ case CAT_DATE:
+ b.WriteString("date ")
+ case CAT_FILETIME:
+ b.WriteString("fileTime ")
+ case CAT_TAGS:
+ b.WriteString("tags ")
+ case CAT_LINKS:
+ b.WriteString("links ")
+ default:
+ return nil, &CompileError{
+ fmt.Sprint("unknown or invalid category ", stmt.Category.String()),
+ }
+ }
+ switch stmt.Operator {
+ case OP_EQ:
+ if stmt.Category.IsSet() {
+ b.WriteString("IN ")
+ } else {
+ b.WriteString("= ")
+ }
+ case OP_AP:
+ b.WriteString("LIKE ")
+ case OP_NE:
+ b.WriteString("!= ")
+ case OP_LT:
+ b.WriteString("< ")
+ case OP_LE:
+ b.WriteString("<= ")
+ case OP_GE:
+ b.WriteString(">= ")
+ case OP_GT:
+ b.WriteString("> ")
+ default:
+ return nil, &CompileError{
+ fmt.Sprint("unknown or invalid operand ", stmt.Operator.String()),
+ }
+ }
+
+ switch stmt.Value.Type() {
+ case VAL_STR:
+ s, ok := stmt.Value.(StringValue)
+ if !ok {
+ panic(CompileError{"type corruption in string value"})
+ }
+ b.WriteString("(?) ")
+ return &s.S, nil
+ case VAL_DATETIME:
+ dt, ok := stmt.Value.(DatetimeValue)
+ if !ok {
+ panic(CompileError{"type corruption in datetime value"})
+ }
+ fmt.Fprint(b, dt.D.Unix(), " ")
+ default:
+ return nil, &CompileError{
+ fmt.Sprint("unknown or invalid value type ", stmt.Value.Type()),
+ }
+ }
+ return nil, nil
+}
+
+func (stmts Statements) Compile(b *strings.Builder, delim string) ([]string, error) {
+ var args []string
+
+ // TODO: handle meta category
+ for i, stmt := range stmts {
+ if i != 0 {
+ b.WriteString(delim)
+ }
+ b.WriteByte(' ')
+
+ arg, err := stmt.Compile(b)
+ if err != nil {
+ return nil, err
+ } else if arg != nil {
+ args = append(args, *arg)
+ }
+ }
+
+ return args, nil
+}
+
+func (root Clause) Compile() (string, []string, error) {
+ if d := root.Depth(); d > MAX_CLAUSE_DEPTH {
+ return "", nil, &CompileError{
+ fmt.Sprint("exceeded maximum clause depth of 8: ", d),
+ }
+ }
+
+ b := strings.Builder{}
+ args, err := root.buildCompile(&b)
+ if err != nil {
+ return "", nil, err
+ }
+ return b.String(), args, nil
+}
+
+func (c Clause) buildCompile(b *strings.Builder) ([]string, error) {
+ b.WriteString("( ")
+
+ var delim string
+ switch cop := c.Operator; cop {
+ case COP_AND:
+ delim = "AND"
+ case COP_OR:
+ delim = "OR"
+ default:
+ return nil, &CompileError{fmt.Sprint("invalid clause operator ", cop)}
+ }
+
+ args, err := c.Statements.Compile(b, delim)
+ if err != nil {
+ return nil, err
+ }
+ for _, clause := range c.Clauses {
+ b.WriteString(delim)
+ b.WriteByte(' ')
+
+ newArgs, err := clause.buildCompile(b)
+ if err != nil {
+ return nil, err
+ } else if newArgs != nil {
+ args = append(args, newArgs...)
+ }
+ }
+ b.WriteString(") ")
+
+ return args, nil
+}