Browse Source

feat: Adding help function

Matthias Ladkau 3 years ago
parent
commit
9370f1dd73
8 changed files with 189 additions and 17 deletions
  1. BIN
      cli/cli
  2. 3 2
      cli/ecal.go
  3. 63 0
      cli/tool/helper.go
  4. 103 11
      cli/tool/interpret.go
  5. 16 0
      ecal.md
  6. 1 1
      go.mod
  7. 2 2
      go.sum
  8. 1 1
      stdlib/generate/generate.go

BIN
cli/cli


+ 3 - 2
cli/ecal.go

@@ -24,9 +24,10 @@ TODO:
 - CLI interpreter (show base directory when starting)
 -- console can specify a base directory
 -- console can preload code
-
+- adding external stdlib functions
+- cron trigger (async) in-build function
+- web server (sync/async) in-build function with options
 - create executable binary (pack into single binary)
-
 - debug server support (vscode)
 */
 

+ 63 - 0
cli/tool/helper.go

@@ -0,0 +1,63 @@
+/*
+ * ECAL
+ *
+ * Copyright 2020 Matthias Ladkau. All rights reserved.
+ *
+ * This Source Code Form is subject to the terms of the MIT
+ * License, If a copy of the MIT License was not distributed with this
+ * file, You can obtain one at https://opensource.org/licenses/MIT.
+ */
+
+package tool
+
+import (
+	"fmt"
+	"regexp"
+	"strings"
+
+	"devt.de/krotik/common/stringutil"
+	"devt.de/krotik/common/termutil"
+)
+
+/*
+matchesFulltextSearch checks if a given text matches a given glob expression. Returns
+true if an error occurs.
+*/
+func matchesFulltextSearch(clt termutil.ConsoleLineTerminal, text string, glob string) bool {
+	var res bool
+
+	re, err := stringutil.GlobToRegex(glob)
+
+	if err == nil {
+		res, err = regexp.MatchString(re, text)
+	}
+
+	if err != nil {
+		clt.WriteString(fmt.Sprintln("Invalid search expression:", err.Error()))
+		res = true
+	}
+
+	return res
+}
+
+/*
+fillTableRow fills a table row of a display table.
+*/
+func fillTableRow(tabData []string, key string, value string) []string {
+
+	tabData = append(tabData, key)
+
+	valSplit := stringutil.ChunkSplit(value, 80, true)
+	tabData = append(tabData, strings.TrimSpace(valSplit[0]))
+	for _, valPart := range valSplit[1:] {
+		tabData = append(tabData, "")
+		tabData = append(tabData, strings.TrimSpace(valPart))
+	}
+
+	// Insert empty rows to ease reading
+
+	tabData = append(tabData, "")
+	tabData = append(tabData, "")
+
+	return tabData
+}

+ 103 - 11
cli/tool/interpret.go

@@ -18,11 +18,13 @@ import (
 	"strings"
 
 	"devt.de/krotik/common/fileutil"
+	"devt.de/krotik/common/stringutil"
 	"devt.de/krotik/common/termutil"
 	"devt.de/krotik/ecal/config"
 	"devt.de/krotik/ecal/interpreter"
 	"devt.de/krotik/ecal/parser"
 	"devt.de/krotik/ecal/scope"
+	"devt.de/krotik/ecal/stdlib"
 	"devt.de/krotik/ecal/util"
 )
 
@@ -102,7 +104,7 @@ func Interpret(interactive bool) error {
 
 	if err == nil {
 
-		name := "ECAL console"
+		name := "console"
 
 		// Create interpreter
 
@@ -158,19 +160,16 @@ func Interpret(interactive bool) error {
 								clt.WriteString(fmt.Sprintf("\n"))
 								clt.WriteString(fmt.Sprintf("Console supports all normal ECAL statements and the following special commands:\n"))
 								clt.WriteString(fmt.Sprintf("\n"))
-								clt.WriteString(fmt.Sprintf("    @syms - List all available inbuild functions and available stdlib packages of ECAL.\n"))
-								clt.WriteString(fmt.Sprintf("    @stdl - List all available constants and functions of a stdlib package.\n"))
-								clt.WriteString(fmt.Sprintf("    @lk   - Do a full text search through all docstrings.\n"))
+								clt.WriteString(fmt.Sprintf("    @sym [glob] - List all available inbuild functions and available stdlib packages of ECAL.\n"))
+								clt.WriteString(fmt.Sprintf("    @std <package> [glob] - List all available constants and functions of a stdlib package.\n"))
 								clt.WriteString(fmt.Sprintf("\n"))
+								clt.WriteString(fmt.Sprintf("Add an argument after a list command to do a full text search. The search string should be in glob format.\n"))
 
-							} else if strings.HasPrefix(trimmedLine, "@syms") {
-								args := strings.Split(trimmedLine, " ")[1:]
+							} else if strings.HasPrefix(trimmedLine, "@sym") {
+								displaySymbols(clt, strings.Split(trimmedLine, " ")[1:])
 
-								// TODO Implement
-
-								clt.WriteString(fmt.Sprint("syms:", args))
-
-							} else if line == "!reset" {
+							} else if strings.HasPrefix(trimmedLine, "@std") {
+								displayPackage(clt, strings.Split(trimmedLine, " ")[1:])
 
 							} else {
 								var ierr error
@@ -202,3 +201,96 @@ func Interpret(interactive bool) error {
 
 	return err
 }
+
+/*
+displaySymbols lists all available inbuild functions and available stdlib packages of ECAL.
+*/
+func displaySymbols(clt termutil.ConsoleLineTerminal, args []string) {
+
+	tabData := []string{"Inbuild function", "Description"}
+
+	for name, f := range interpreter.InbuildFuncMap {
+		ds, _ := f.DocString()
+
+		if len(args) > 0 && !matchesFulltextSearch(clt, fmt.Sprintf("%v %v", name, ds), args[0]) {
+			continue
+		}
+
+		tabData = fillTableRow(tabData, name, ds)
+	}
+
+	if len(tabData) > 2 {
+		clt.WriteString(stringutil.PrintGraphicStringTable(tabData, 2, 1,
+			stringutil.SingleDoubleLineTable))
+	}
+
+	packageNames, _, _ := stdlib.GetStdlibSymbols()
+
+	tabData = []string{"Package name", "Description"}
+
+	for _, p := range packageNames {
+		ps, _ := stdlib.GetPkgDocString(p)
+
+		if len(args) > 0 && !matchesFulltextSearch(clt, fmt.Sprintf("%v %v", p, ps), args[0]) {
+			continue
+		}
+
+		tabData = fillTableRow(tabData, p, ps)
+	}
+
+	if len(tabData) > 2 {
+		clt.WriteString(stringutil.PrintGraphicStringTable(tabData, 2, 1,
+			stringutil.SingleDoubleLineTable))
+	}
+}
+
+/*
+displayPackage list all available constants and functions of a stdlib package.
+*/
+func displayPackage(clt termutil.ConsoleLineTerminal, args []string) {
+
+	_, constSymbols, funcSymbols := stdlib.GetStdlibSymbols()
+
+	tabData := []string{"Constant", "Value"}
+
+	for _, s := range constSymbols {
+
+		if len(args) > 0 && !strings.HasPrefix(s, args[0]) {
+			continue
+		}
+
+		val, _ := stdlib.GetStdlibConst(s)
+
+		tabData = fillTableRow(tabData, s, fmt.Sprint(val))
+	}
+
+	if len(tabData) > 2 {
+		clt.WriteString(stringutil.PrintGraphicStringTable(tabData, 2, 1,
+			stringutil.SingleDoubleLineTable))
+	}
+
+	tabData = []string{"Function", "Description"}
+
+	for _, f := range funcSymbols {
+		if len(args) > 0 && !strings.HasPrefix(f, args[0]) {
+			continue
+		}
+
+		fObj, _ := stdlib.GetStdlibFunc(f)
+		fDoc, _ := fObj.DocString()
+
+		fDoc = strings.Replace(fDoc, "\n", " ", -1)
+		fDoc = strings.Replace(fDoc, "\t", " ", -1)
+
+		if len(args) > 1 && !matchesFulltextSearch(clt, fmt.Sprintf("%v %v", f, fDoc), args[1]) {
+			continue
+		}
+
+		tabData = fillTableRow(tabData, f, fDoc)
+	}
+
+	if len(tabData) > 2 {
+		clt.WriteString(stringutil.PrintGraphicStringTable(tabData, 2, 1,
+			stringutil.SingleDoubleLineTable))
+	}
+}

+ 16 - 0
ecal.md

@@ -336,6 +336,22 @@ try {
     log(e)
 }
 ```
+The variable `e` has the following structure:
+```
+{
+  "data": [
+    1,
+    2,
+    3
+  ],
+  "detail": "My error message",
+  "error": "ECAL error in console: MyError (My error message) (Line:1 Pos:7)",
+  "line": 1,
+  "pos": 7,
+  "source": "console",
+  "type": "MyError"
+}
+```
 
 Build-in Functions
 --

+ 1 - 1
go.mod

@@ -2,4 +2,4 @@ module devt.de/krotik/ecal
 
 go 1.12
 
-require devt.de/krotik/common v1.3.6
+require devt.de/krotik/common v1.3.8

+ 2 - 2
go.sum

@@ -1,2 +1,2 @@
-devt.de/krotik/common v1.3.6 h1:qj8F65QDUCLGcD+gJgitoFIkXbVwVOghQ9NuqOfFJPI=
-devt.de/krotik/common v1.3.6/go.mod h1:X4nsS85DAxyHkwSg/Tc6+XC2zfmGeaVz+37F61+eSaI=
+devt.de/krotik/common v1.3.8 h1:+O4CLiELyJsQISagQeSrfxUQnD49fFjfz1sWpZDihco=
+devt.de/krotik/common v1.3.8/go.mod h1:X4nsS85DAxyHkwSg/Tc6+XC2zfmGeaVz+37F61+eSaI=

+ 1 - 1
stdlib/generate/generate.go

@@ -49,7 +49,7 @@ go list std | grep -v internal | grep -v '\.' | grep -v unsafe | grep -v syscall
 // =============EDIT HERE START=============
 
 var pkgNames = map[string][]string{
-	//	"math": {"Pi"},
+	//	"math": {"Pi", "E", "Phi", "Pow"},
 	//	"fmt":  {"Println", "Sprint"},
 }