Browse Source

feat: Custom stdlib function

Matthias Ladkau 3 years ago
parent
commit
bb87c53245

+ 7 - 3
cli/tool/interpret.go

@@ -70,7 +70,9 @@ func Interpret(interactive bool) error {
 
 	clt, err = termutil.NewConsoleLineTerminal(os.Stdout)
 
-	fmt.Println(fmt.Sprintf("ECAL %v", config.ProductVersion))
+	if interactive {
+		fmt.Println(fmt.Sprintf("ECAL %v", config.ProductVersion))
+	}
 
 	// Create the logger
 
@@ -95,7 +97,7 @@ func Interpret(interactive bool) error {
 
 		if err == nil {
 			if ilogLevel != nil && *ilogLevel != "" {
-				if logger, err = util.NewLogLevelLogger(logger, *ilogLevel); err == nil {
+				if logger, err = util.NewLogLevelLogger(logger, *ilogLevel); err == nil && interactive {
 					fmt.Print(fmt.Sprintf("Log level: %v - ", logger.(*util.LogLevelLogger).Level()))
 				}
 			}
@@ -106,7 +108,9 @@ func Interpret(interactive bool) error {
 
 		// Get the import locator
 
-		fmt.Println(fmt.Sprintf("Root directory: %v", *idir))
+		if interactive {
+			fmt.Println(fmt.Sprintf("Root directory: %v", *idir))
+		}
 
 		importLocator := &util.FileImportLocator{Root: *idir}
 

+ 11 - 2
interpreter/func_provider_test.go

@@ -12,11 +12,16 @@ package interpreter
 
 import (
 	"fmt"
-	"strings"
+	"reflect"
 	"testing"
+
+	"devt.de/krotik/ecal/stdlib"
 )
 
 func TestStdlib(t *testing.T) {
+	stdlib.AddStdlibPkg("fmt", "fmt package")
+	stdlib.AddStdlibFunc("fmt", "Sprint",
+		stdlib.NewECALFunctionAdapter(reflect.ValueOf(fmt.Sprint), "foo"))
 
 	res, err := UnitTestEvalAndAST(
 		`fmt.Sprint([1,2,3])`, nil,
@@ -262,10 +267,14 @@ doc(foo)`, nil)
 		return
 	}
 
+	stdlib.AddStdlibPkg("fmt", "fmt package")
+	stdlib.AddStdlibFunc("fmt", "Println",
+		stdlib.NewECALFunctionAdapter(reflect.ValueOf(fmt.Sprint), "foo"))
+
 	res, err = UnitTestEval(
 		`doc(fmt.Println)`, nil)
 
-	if err != nil || !strings.HasPrefix(fmt.Sprint(res), "Println") {
+	if err != nil || res != "foo" {
 		t.Error("Unexpected result: ", res, err)
 		return
 	}

+ 14 - 9
parser/parser.go

@@ -288,18 +288,23 @@ func (p *parser) next() (*ASTNode, error) {
 
 	token, more := p.tokens.Next()
 
-	// Skip over pre comment token
+	for more && (token.ID == TokenPRECOMMENT || token.ID == TokenPOSTCOMMENT) {
 
-	for more && token.ID == TokenPRECOMMENT {
-		preComments = append(preComments, NewLexTokenInstance(token))
-		token, more = p.tokens.Next()
-	}
+		if token.ID == TokenPRECOMMENT {
+
+			// Skip over pre comment token
+
+			preComments = append(preComments, NewLexTokenInstance(token))
+			token, more = p.tokens.Next()
+		}
+
+		if token.ID == TokenPOSTCOMMENT {
 
-	// Skip over post comment token
+			// Skip over post comment token
 
-	for more && token.ID == TokenPOSTCOMMENT {
-		postComments = append(postComments, NewLexTokenInstance(token))
-		token, more = p.tokens.Next()
+			postComments = append(postComments, NewLexTokenInstance(token))
+			token, more = p.tokens.Next()
+		}
 	}
 
 	if !more {

+ 11 - 0
parser/parser_main_test.go

@@ -119,6 +119,17 @@ func TestCommentParsing(t *testing.T) {
 	input = `/* foo */ 1 # foo bar`
 	expectedOutput = `
 number: 1 #  foo   foo bar
+`[1:]
+
+	if res, err := UnitTestParse("mytest", input); err != nil || fmt.Sprint(res) != expectedOutput {
+		t.Error("Unexpected parser output:\n", res, "expected was:\n", expectedOutput, "Error:", err)
+		return
+	}
+
+	input = `# 123 
+/* foo */ 1 # foo bar`
+	expectedOutput = `
+number: 1 #  foo   foo bar
 `[1:]
 
 	if res, err := UnitTestParse("mytest", input); err != nil || fmt.Sprint(res) != expectedOutput {

+ 7 - 0
stdlib/adapter.go

@@ -25,6 +25,13 @@ type ECALFunctionAdapter struct {
 	docstring string
 }
 
+/*
+NewECALFunctionAdapter creates a new ECALFunctionAdapter.
+*/
+func NewECALFunctionAdapter(funcval reflect.Value, docstring string) *ECALFunctionAdapter {
+	return &ECALFunctionAdapter{funcval, docstring}
+}
+
 /*
 Run executes this function.
 */

+ 1 - 1
stdlib/adapter_test.go

@@ -239,7 +239,7 @@ func TestECALFunctionAdapter(t *testing.T) {
 
 	// Get documentation
 
-	afuncEcal := &ECALFunctionAdapter{reflect.ValueOf(fmt.Sprint), "test123"}
+	afuncEcal := NewECALFunctionAdapter(reflect.ValueOf(fmt.Sprint), "test123")
 
 	if s, err := afuncEcal.DocString(); s == "" || err != nil {
 		t.Error("Docstring should return something")

+ 1 - 1
stdlib/generate/generate.go

@@ -53,7 +53,7 @@ var pkgNames = map[string][]string{
 	//	"fmt":  {"Println", "Sprint"},
 }
 
-var generateDoc = true
+var generateDoc = false
 
 // ==============EDIT HERE END==============
 

+ 52 - 0
stdlib/stdlib.go

@@ -17,6 +17,42 @@ import (
 	"devt.de/krotik/ecal/util"
 )
 
+var internalStdlibFuncMap = make(map[string]util.ECALFunction)
+var internalStdlibDocMap = make(map[string]string)
+
+/*
+AddStdlibPkg adds a package to stdlib. A package needs to be added before functions
+can be added.
+*/
+func AddStdlibPkg(pkg string, docstring string) error {
+	_, ok1 := GetPkgDocString(pkg)
+	_, ok2 := internalStdlibDocMap[pkg]
+
+	if ok1 || ok2 {
+		return fmt.Errorf("Package %v already exists", pkg)
+	}
+
+	internalStdlibDocMap[pkg] = docstring
+
+	return nil
+}
+
+/*
+AddStdlibFunc adds a function to stdlib.
+*/
+func AddStdlibFunc(pkg string, name string, funcObj util.ECALFunction) error {
+	_, ok1 := GetPkgDocString(pkg)
+	_, ok2 := internalStdlibDocMap[pkg]
+
+	if !ok1 && !ok2 {
+		return fmt.Errorf("Package %v does not exist", pkg)
+	}
+
+	internalStdlibFuncMap[fmt.Sprintf("%v.%v", pkg, name)] = funcObj
+
+	return nil
+}
+
 /*
 GetStdlibSymbols returns all available packages of stdlib and their constant
 and function symbols.
@@ -49,10 +85,20 @@ func GetStdlibSymbols() ([]string, []string, []string) {
 			funcSymbols = addSym(sym, "-func", symMap, funcSymbols)
 		}
 	}
+
 	for k := range packageSet {
 		packageNames = append(packageNames, k)
 	}
 
+	// Add internal stuff
+
+	for k := range internalStdlibDocMap {
+		packageNames = append(packageNames, k)
+	}
+	for k := range internalStdlibFuncMap {
+		funcSymbols = append(funcSymbols, k)
+	}
+
 	return packageNames, constSymbols, funcSymbols
 }
 
@@ -88,6 +134,10 @@ func GetStdlibFunc(name string) (util.ECALFunction, bool) {
 		}
 	}
 
+	if !resok {
+		res, resok = internalStdlibFuncMap[name]
+	}
+
 	return res, resok
 }
 
@@ -99,6 +149,8 @@ func GetPkgDocString(name string) (string, bool) {
 	s, ok := genStdlib[fmt.Sprintf("%v-synopsis", name)]
 	if ok {
 		res = fmt.Sprint(s)
+	} else {
+		res, ok = internalStdlibDocMap[name]
 	}
 
 	return res, ok

+ 39 - 1
stdlib/stdlib_test.go

@@ -18,6 +18,7 @@ import (
 )
 
 func TestGetPkgDocString(t *testing.T) {
+	AddStdlibPkg("foo", "foo doc")
 
 	mathFuncMap["Println"] = &ECALFunctionAdapter{reflect.ValueOf(fmt.Println), "foo"}
 
@@ -34,9 +35,24 @@ func TestGetPkgDocString(t *testing.T) {
 		t.Error("Unexpected result:", doc)
 		return
 	}
+
+	doc, _ = GetPkgDocString("foo")
+
+	if doc != "foo doc" {
+		t.Error("Unexpected result:", doc)
+		return
+	}
+
+	if err := AddStdlibPkg("foo", "foo doc"); err == nil || err.Error() != "Package foo already exists" {
+		t.Error("Unexpected error:", err)
+		return
+	}
 }
 
 func TestSymbols(t *testing.T) {
+	AddStdlibPkg("foo", "foo doc")
+	AddStdlibFunc("foo", "bar", nil)
+
 	p, c, f := GetStdlibSymbols()
 	if len(p) == 0 || len(c) == 0 || len(f) == 0 {
 		t.Error("Should have some entries in symbol lists:", p, c, f)
@@ -68,6 +84,8 @@ func TestSplitModuleAndName(t *testing.T) {
 }
 
 func TestGetStdLibItems(t *testing.T) {
+	dummyFunc := &ECALFunctionAdapter{}
+	AddStdlibFunc("foo", "bar", dummyFunc)
 
 	mathFuncMap["Println"] = &ECALFunctionAdapter{reflect.ValueOf(fmt.Println), "foo"}
 
@@ -76,8 +94,13 @@ func TestGetStdLibItems(t *testing.T) {
 		return
 	}
 
+	if f, _ := GetStdlibFunc("foo.bar"); f != dummyFunc {
+		t.Error("Unexpected resutl: functions should lookup correctly")
+		return
+	}
+
 	if c, ok := GetStdlibFunc("foo"); c != nil || ok {
-		t.Error("Unexpected resutl: constants should lookup correctly")
+		t.Error("Unexpected resutl: func should lookup correctly")
 		return
 	}
 
@@ -91,3 +114,18 @@ func TestGetStdLibItems(t *testing.T) {
 		return
 	}
 }
+
+func TestAddStdLibFunc(t *testing.T) {
+	dummyFunc := &ECALFunctionAdapter{}
+	AddStdlibFunc("foo", "bar", dummyFunc)
+
+	if f, _ := GetStdlibFunc("foo.bar"); f != dummyFunc {
+		t.Error("Unexpected resutl: functions should lookup correctly")
+		return
+	}
+
+	if err := AddStdlibFunc("foo2", "bar", dummyFunc); err == nil || err.Error() != "Package foo2 does not exist" {
+		t.Error("Unexpected error:", err)
+		return
+	}
+}