/* SHS: Syntactically Homogeneous Shell * Copyright (C) 2019 Aidan Hahn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package main import ( "os" "fmt" "strconv" "github.com/peterh/liner" "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/log" "gitlab.com/whom/shs/util" "gitlab.com/whom/shs/stdlib" "gitlab.com/whom/shs/config" ) const ( def_prompt string = "λ " ) // useful for when input contains escape sequences // not checking delims cause thats up to the user who defines their prompts func parseString(in string) string { in = "\"" + in + "\"" out, err := strconv.Unquote(in) if err != nil { log.Log(log.ERR, "Couldnt parse (pre?)prompt", "init") return "" } return out } func setLogLvl(vars ast.VarTable) { var loglvl string loglvl_t := ast.GetVar("SH_LOGGING", vars) if loglvl_t != nil { loglvl = loglvl_t.Value() } if loglvl != "" { llvl, err := strconv.ParseInt(loglvl, 10, 8) if err != nil { log.Log(log.ERR, "couldnt parse log level: " + err.Error(), "init") } else { log.SetLogLvl(llvl) } } } func main() { var prompt string var debug string var hist string no_hist := false ast.SyncTablesWithOSEnviron = true ast.ExecWhenFuncUndef = true stdlib.InitShellFeatures() defer stdlib.TeardownShell() vars, funcs := config.InitFromConfig(".shsrc") debug_t := ast.GetVar("SH_DEBUG_MODE", vars) if debug_t != nil { debug = debug_t.Value() } hist_t := ast.GetVar("SH_HIST_FILE", vars) if hist_t != nil { hist = hist_t.Value() } else { no_hist = true } dyn_prompt := ast.GetFunction("_SH_PROMPT", funcs) if dyn_prompt == nil || dyn_prompt.Args != 0 { dyn_prompt = nil } prompt_t := ast.GetVar("SHS_STATIC_PROMPT", vars) if prompt_t != nil { prompt = parseString(prompt_t.Value()) } else { prompt = def_prompt } line := liner.NewLiner() defer line.Close() line.SetCtrlCAborts(true) line.SetCompleter(func(line string) (c []string) { return util.ShellCompleter(line, vars, funcs) }) var histFile *os.File var err error if !no_hist { histFile, err = os.Open(hist) if err == nil { line.ReadHistory(histFile) } else { log.Log(log.ERR, "couldnt read history: " + err.Error(), "repl") } defer histFile.Close() } for { setLogLvl(vars) var prePrompt string if dyn_prompt != nil { p_tok := dyn_prompt.CallFunction(nil, vars, funcs) if p_tok != nil && p_tok.Tag == ast.STRING { prePrompt = parseString(p_tok.Value()) } } fmt.Printf(prePrompt) text, err := line.Prompt(prompt) if err != nil && err != liner.ErrPromptAborted{ log.Log(log.ERR, "couldnt read user input: " + err.Error(), "repl") continue } line.AppendHistory(text) userInput := ast.Lex(text) if userInput == nil { // errors handled in Lex // TODO: return an error instead continue } if debug != "" { ast.PrintSExprsIndividually(userInput) } result := userInput.Eval(funcs, vars, false) if result != nil { for i := result; i != nil; i = i.Next { fmt.Printf(i.String() + " ") } } fmt.Printf("\n") } }