/* * 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 main import ( "flag" "fmt" "io" "os" "devt.de/krotik/common/fileutil" "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/util" ) /* interpret starts the ECAL code interpreter. */ func interpret(interactive bool) error { var err error wd, _ := os.Getwd() idir := flag.String("dir", wd, "Root directory for ECAL interpreter") ilogFile := flag.String("logfile", "", "Log to a file") ilogLevel := flag.String("loglevel", "Info", "Logging level (Debug, Info, Error)") showHelp := flag.Bool("help", false, "Show this help message") flag.Usage = func() { fmt.Println() if !interactive { fmt.Println(fmt.Sprintf("Usage of %s run [options] ", os.Args[0])) } else { fmt.Println(fmt.Sprintf("Usage of %s [options]", os.Args[0])) } fmt.Println() flag.PrintDefaults() fmt.Println() } if len(os.Args) > 2 { flag.CommandLine.Parse(os.Args[2:]) if *showHelp { flag.Usage() return nil } } var clt termutil.ConsoleLineTerminal var logger util.Logger clt, err = termutil.NewConsoleLineTerminal(os.Stdout) // Create the logger if err == nil { // Check if we should log to a file if ilogFile != nil && *ilogFile != "" { var logWriter io.Writer logFileRollover := fileutil.SizeBasedRolloverCondition(1000000) // Each file can be up to a megabyte logWriter, err = fileutil.NewMultiFileBuffer(*ilogFile, fileutil.ConsecutiveNumberIterator(10), logFileRollover) logger = util.NewBufferLogger(logWriter) } else { // Log to the console by default logger = util.NewStdOutLogger() } // Set the log level if err == nil { if ilogLevel != nil && *ilogLevel != "" { logger, err = util.NewLogLevelLogger(logger, *ilogLevel) } } } // Get the import locator importLocator := &util.FileImportLocator{Root: *idir} if err == nil { name := "ECAL console" // Create interpreter erp := interpreter.NewECALRuntimeProvider(name, importLocator, logger) // Create global variable scope vs := scope.NewScope(scope.GlobalScope) // TODO Execute file if interactive { // Drop into interactive shell if err == nil { isExitLine := func(s string) bool { return s == "exit" || s == "q" || s == "quit" || s == "bye" || s == "\x04" } // Add history functionality without file persistence clt, err = termutil.AddHistoryMixin(clt, "", func(s string) bool { return isExitLine(s) }) if err == nil { if err = clt.StartTerm(); err == nil { var line string defer clt.StopTerm() fmt.Println(fmt.Sprintf("ECAL %v", config.ProductVersion)) fmt.Println("Type 'q' or 'quit' to exit the shell and '?' to get help") line, err = clt.NextLine() for err == nil && !isExitLine(line) { // Process the entered line if line == "?" { // Show help clt.WriteString(fmt.Sprintf("ECAL %v\n", config.ProductVersion)) 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("\n")) } else if line == "!funcs" { } else if line == "!reset" { } else { var ierr error var ast *parser.ASTNode var res interface{} if ast, ierr = parser.ParseWithRuntime("console input", line, erp); ierr == nil { if ierr = ast.Runtime.Validate(); ierr == nil { if res, ierr = ast.Runtime.Eval(vs, make(map[string]interface{})); ierr == nil && res != nil { clt.WriteString(fmt.Sprintln(res)) } } } if ierr != nil { clt.WriteString(fmt.Sprintln(ierr.Error())) } } line, err = clt.NextLine() } } } } } } return err }