func mustParse(t *testing.T, name, text string) *parse.Chunk { n, err := parse.Parse(text) if err != nil { t.Fatalf("Parser(%q) error: %s", text, err) } return n }
// SourceText evaluates a chunk of elvish source. func (ev *Evaler) SourceText(src string) error { n, err := parse.Parse(src) if err != nil { return err } return ev.EvalInteractive(src, n) }
// SourceText evaluates a chunk of elvish source. func (ev *Evaler) SourceText(name, src, dir string) error { n, err := parse.Parse(name, src) if err != nil { return err } return ev.Eval(name, src, dir, n) }
func mustParse(name, text string) *parse.Chunk { n, e := parse.Parse(name, text) if e != nil { panic("parser error") } return n }
// TODO(xiaq): Currently only the editor deals with signals. func interact() { ev, st := newEvalerAndStore() datadir, err := store.EnsureDataDir() printError(err) if err == nil { // XXX err := ev.Source(datadir + "/rc.elv") if err != nil && !os.IsNotExist(err) { printError(err) } } cmdNum := 0 username := "******" user, err := user.Current() if err == nil { username = user.Username } hostname, err := os.Hostname() if err != nil { hostname = "???" } rpromptStr := username + "@" + hostname sigch := make(chan os.Signal, sigchSize) signal.Notify(sigch) ed := edit.NewEditor(os.Stdin, sigch, ev, st) for { cmdNum++ name := fmt.Sprintf("<tty %d>", cmdNum) prompt := func() string { return osutil.Getwd() + "> " } rprompt := func() string { return rpromptStr } lr := ed.ReadLine(prompt, rprompt) // signal.Stop(sigch) if lr.EOF { break } else if lr.Err != nil { fmt.Println("Editor error:", lr.Err) fmt.Println("My pid is", os.Getpid()) } n, err := parse.Parse(name, lr.Line) printError(err) if err == nil { err := ev.Eval(name, lr.Line, n) printError(err) } } }
func tokenize(src string) ([]Token, error) { lastEnd := 0 n, err := parse.Parse("[interactive code]", src) if n == nil { return []Token{{ParserError, src, nil, ""}}, err } tokenCh := make(chan Token, tokensBufferSize) tokens := []Token{} tokensDone := make(chan bool) go func() { for token := range tokenCh { begin := token.Node.Begin() if begin > lastEnd { tokens = append(tokens, parserError(src[lastEnd:begin])) } tokens = append(tokens, token) lastEnd = token.Node.End() } tokensDone <- true }() produceTokens(n, tokenCh) close(tokenCh) <-tokensDone if lastEnd != len(src) { tokens = append(tokens, parserError(src[lastEnd:])) } return tokens, err }
func interact(ev *eval.Evaler, st *store.Store) { // Build Editor. sigch := make(chan os.Signal) signal.Notify(sigch) ed := edit.NewEditor(os.Stdin, sigch, ev, st) // Source rc.elv. datadir, err := store.EnsureDataDir() if err != nil { fmt.Fprintln(os.Stderr, err) } else { source(ev, datadir+"/rc.elv", true) } // Build readLine function. readLine := func() (string, error) { return ed.ReadLine() } cooldown := time.Second usingBasic := false cmdNum := 0 for { cmdNum++ // name := fmt.Sprintf("<tty %d>", cmdNum) line, err := readLine() if err == io.EOF { break } else if err != nil { fmt.Println("Editor error:", err) if !usingBasic { fmt.Println("Falling back to basic line editor") readLine = basicReadLine usingBasic = true } else { fmt.Println("Don't know what to do, pid is", os.Getpid()) fmt.Println("Restarting editor in", cooldown) time.Sleep(cooldown) if cooldown < time.Minute { cooldown *= 2 } } continue } // No error; reset cooldown. cooldown = time.Second n, err := parse.Parse(line) printError(err, "<interact>", "parse error", line) if err == nil { err := ev.EvalInteractive(line, n) printError(err, "<interact>", "eval error", line) } } }
func (ed *Editor) refresh(fullRefresh bool) error { // Re-lex the line, unless we are in modeCompletion name := "[interacitve]" src := ed.line if ed.mode != modeCompletion { n, _ /*err*/ := parse.Parse(src) if n == nil { ed.tokens = []Token{{ParserError, src, nil, ""}} } else { ed.tokens = tokenize(src, n) _, err := ed.evaler.Compile(name, src, n) if err != nil { if err, ok := err.(*errutil.ContextualError); ok { ed.addTip("compiler error highlighted") p := err.Pos() for i, token := range ed.tokens { if token.Node.Begin() <= p && p < token.Node.End() { ed.tokens[i].MoreStyle += styleForCompilerError break } } } } } for i, t := range ed.tokens { for _, stylist := range stylists { ed.tokens[i].MoreStyle += stylist(t.Node, ed) } } } return ed.writer.refresh(&ed.editorState, fullRefresh) }
func use(ec *EvalCtx, modname string, pfilename *string) { if _, ok := ec.Evaler.Modules[modname]; ok { // Module already loaded. return } // Load the source. var filename, source string if pfilename != nil { filename = *pfilename var err error source, err = readFileUTF8(filename) maybeThrow(err) } else { // No filename; defaulting to $datadir/$modname.elv. dataDir, err := store.DataDir() maybeThrow(err) filename = dataDir + "/" + modname + ".elv" if _, err := os.Stat(filename); os.IsNotExist(err) { // File does not exist. Try loading from the table of builtin // modules. var ok bool if source, ok = builtinModules[modname]; ok { // Source is loaded. Do nothing more. filename = "<builtin module>" } else { throw(fmt.Errorf("cannot load %s: %s does not exist", modname, filename)) } } else { // File exists. Load it. source, err = readFileUTF8(filename) maybeThrow(err) } } // TODO(xiaq): Should handle failures when evaluting the module newEc := &EvalCtx{ ec.Evaler, filename, source, "module " + modname, Namespace{}, Namespace{}, ec.ports, nil, 0, len(source), } n, err := parse.Parse(source) maybeThrow(err) op, err := newEc.Compile(n) // TODO the err originates in another source, should add appropriate information. maybeThrow(err) op.Exec(newEc) ec.Evaler.Modules[modname] = newEc.local }
// SourceText evaluates a chunk of elvish source. func (ev *Evaler) SourceText(name, src string) error { n, err := parse.Parse(name, src) if err != nil { return err } op, err := ev.Compile(n, name, src) if err != nil { return err } return ev.Eval(op, name, src) }
func mustParseAndCompile(t *testing.T, ev *Evaler, name, text string) Op { n, err := parse.Parse(name, text) if err != nil { t.Fatalf("Parse(%q) error: %s", text, err) } op, err := ev.Compile(n, name, text) if err != nil { t.Fatalf("Compile(Parse(%q)) error: %s", text, err) } return op }
// evalText is like eval.Evaler.SourceText except that it reports errors. func evalText(ev *eval.Evaler, name, src string) bool { n, err := parse.Parse(name, src) if err != nil { printError(err, "Parse error") return false } op, err := ev.Compile(n, name, src) if err != nil { printError(err, "Compile error") return false } err = ev.Eval(op, name, src) if err != nil { printError(err, "Exception") return false } return true }
func (ed *Editor) refresh(fullRefresh bool, tips bool) error { // Re-lex the line, unless we are in modeCompletion src := ed.line if ed.mode.Mode() != modeCompletion { n, err := parse.Parse("[interactive]", src) ed.parseErrorAtEnd = err != nil && atEnd(err, len(src)) if err != nil { // If all the errors happen at the end, it is liekly complaining // about missing texts that will eventually be inserted. Don't show // such errors. // XXX We may need a more reliable criteria. if tips && !ed.parseErrorAtEnd { ed.addTip("parser error: %s", err) } } if n == nil { ed.tokens = []Token{parserError(src, 0, len(src))} } else { ed.tokens = tokenize(src, n) _, err := ed.evaler.Compile(n, "[interactive]", src) if err != nil { if tips && !atEnd(err, len(src)) { ed.addTip("compiler error: %s", err) } if err, ok := err.(*util.PosError); ok { p := err.Begin for i, token := range ed.tokens { if token.Node.Begin() <= p && p < token.Node.End() { ed.tokens[i].MoreStyle = joinStyles(ed.tokens[i].MoreStyle, styleForCompilerError) break } } } } } stylist := &Stylist{ed.tokens, ed, nil} stylist.do(n) } return ed.writer.refresh(&ed.editorState, fullRefresh) }
// UseForm = 'use' StringPrimary.modname Primary.fname // = 'use' StringPrimary.fname func compileUse(cc *compileCtx, fn *parse.Form) exitusOp { var fnameNode *parse.Compound var fname, modname string switch len(fn.Args.Nodes) { case 0: cc.errorf(fn.Args.Pos, "expect module name or file name") case 1, 2: fnameNode = fn.Args.Nodes[0] _, fname = ensureStringPrimary(cc, fnameNode, "expect string literal") if len(fn.Args.Nodes) == 2 { modnameNode := fn.Args.Nodes[1] _, modname = ensureStringPrimary( cc, modnameNode, "expect string literal") if modname == "" { cc.errorf(modnameNode.Pos, "module name is empty") } } else { modname = stem(fname) if modname == "" { cc.errorf(fnameNode.Pos, "stem of file name is empty") } } default: cc.errorf(fn.Args.Nodes[2].Pos, "superfluous argument") } switch { case strings.HasPrefix(fname, "/"): // Absolute file name, do nothing case strings.HasPrefix(fname, "./") || strings.HasPrefix(fname, "../"): // File name relative to current source fname = path.Clean(path.Join(cc.dir, fname)) default: // File name relative to data dir fname = path.Clean(path.Join(cc.dataDir, fname)) } src, err := readFileUTF8(fname) if err != nil { cc.errorf(fnameNode.Pos, "cannot read module: %s", err.Error()) } cn, err := parse.Parse(fname, src) if err != nil { // TODO(xiaq): Pretty print cc.errorf(fnameNode.Pos, "cannot parse module: %s", err.Error()) } newCc := &compileCtx{ cc.Compiler, fname, src, path.Dir(fname), []staticNS{staticNS{}}, staticNS{}, } op, err := newCc.compile(cn) if err != nil { // TODO(xiaq): Pretty print cc.errorf(fnameNode.Pos, "cannot compile module: %s", err.Error()) } cc.mod[modname] = newCc.scopes[0] return func(ec *evalCtx) exitus { // TODO(xiaq): Should install a failHandler that fails the use call newEc := &evalCtx{ ec.Evaler, fname, src, "module " + modname, ns{}, ns{}, ec.ports, nil, } op(newEc) ec.mod[modname] = newEc.local return success } }
// TODO(xiaq): Currently only the editor deals with signals. func interact() { ev, st := newEvalerAndStore() sigch := make(chan os.Signal, sigchSize) signal.Notify(sigch) ed := edit.NewEditor(os.Stdin, sigch, ev, st) datadir, err := store.EnsureDataDir() printError(err) if err == nil { // XXX err := ev.Source(datadir + "/rc.elv") if err != nil && !os.IsNotExist(err) { printError(err) } } cmdNum := 0 username := "******" user, err := user.Current() if err == nil { username = user.Username } hostname, err := os.Hostname() if err != nil { hostname = "???" } rpromptStr := username + "@" + hostname prompt := func() string { return osutil.Getwd() + "> " } rprompt := func() string { return rpromptStr } readLine := func() edit.LineRead { return ed.ReadLine(prompt, rprompt) } usingBasic := false if !sys.IsATTY(0) { readLine = basicReadLine usingBasic = true } for { cmdNum++ // name := fmt.Sprintf("<tty %d>", cmdNum) lr := readLine() // signal.Stop(sigch) if lr.EOF { break } else if lr.Err != nil { fmt.Println("Editor error:", lr.Err) if !usingBasic { fmt.Println("Falling back to basic line editor") readLine = basicReadLine usingBasic = true } continue } n, err := parse.Parse(lr.Line) printError(err) if err == nil { err := ev.EvalInteractive(lr.Line, n) printError(err) } } }
func use(ec *EvalCtx, modname string, pfilename *string) { if _, ok := ec.Evaler.Modules[modname]; ok { // Module already loaded. return } // Load the source. var filename, source string if pfilename != nil { filename = *pfilename var err error source, err = readFileUTF8(filename) maybeThrow(err) } else { // No filename; defaulting to $datadir/$modname.elv. dataDir, err := store.DataDir() maybeThrow(err) filename = dataDir + "/" + strings.Replace(modname, ":", "/", -1) + ".elv" if _, err := os.Stat(filename); os.IsNotExist(err) { // File does not exist. Try loading from the table of builtin // modules. var ok bool if source, ok = builtinModules[modname]; ok { // Source is loaded. Do nothing more. filename = "<builtin module>" } else { throw(fmt.Errorf("cannot load %s: %s does not exist", modname, filename)) } } else { // File exists. Load it. source, err = readFileUTF8(filename) maybeThrow(err) } } n, err := parse.Parse(filename, source) maybeThrow(err) // Make an empty namespace. local := Namespace{} // TODO(xiaq): Should handle failures when evaluting the module newEc := &EvalCtx{ ec.Evaler, "module " + modname, filename, source, local, Namespace{}, ec.ports, nil, true, 0, len(source), ec.addTraceback(), false, } op, err := newEc.Compile(n, filename, source) // TODO the err originates in another source, should add appropriate information. maybeThrow(err) // Load the namespace before executing. This avoids mutual and self use's to // result in an infinite recursion. ec.Evaler.Modules[modname] = local err = newEc.PEval(op) if err != nil { // Unload the namespace. delete(ec.Modules, modname) throw(err) } }