From af694b14f527fbe57d24c803fc6594302326e19b Mon Sep 17 00:00:00 2001 From: Roman Bataev Date: Sun, 15 Jan 2017 19:32:26 -0800 Subject: [PATCH] linter: resolve standard symbols --- .gitignore | 4 ++-- core/data/linter.joke | 9 ++++++++ core/gen_data/gen_data.go | 31 +++++++++++++++++----------- core/parse.go | 43 +++++++++++++++++++++++++++++++-------- core/procs.go | 19 ++++++++++++----- main.go | 11 +++++++--- tests/symbols-lint.joke | 4 ++++ 7 files changed, 90 insertions(+), 31 deletions(-) create mode 100644 core/data/linter.joke diff --git a/.gitignore b/.gitignore index 63f120ef6..f14450a8b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,5 @@ bindata.go play .vscode .idea - -core/core_data.go +core/a_core_data.go +core/a_linter_data.go diff --git a/core/data/linter.joke b/core/data/linter.joke new file mode 100644 index 000000000..0cfd8e296 --- /dev/null +++ b/core/data/linter.joke @@ -0,0 +1,9 @@ +;; This vars are declared for the linter mode to work properly +(def ^:private defmulti) +(def ^:private defmethod) +(def ^:private defprotocol) +(def ^:private defrecord) +(def ^:private defproject) +(def ^:private derive) +(def ^:private alter-var-root) +(def ^:private Exception) diff --git a/core/gen_data/gen_data.go b/core/gen_data/gen_data.go index b46abf5d2..ef70ef7df 100644 --- a/core/gen_data/gen_data.go +++ b/core/gen_data/gen_data.go @@ -5,28 +5,35 @@ import ( "strings" ) -var coreTemplate string = `// Generated by gen_data. Don't modify manually! +var template string = `// Generated by gen_data. Don't modify manually! package core -var coreData = []byte("{content}") +var {name}Data = []byte("{content}") ` const hextable = "0123456789abcdef" func main() { - - coreContent, err := ioutil.ReadFile("data/core.joke") + files, err := ioutil.ReadDir("data") if err != nil { panic(err) } - dst := make([]byte, len(coreContent)*4) - for i, v := range coreContent { - dst[i*4] = '\\' - dst[i*4+1] = 'x' - dst[i*4+2] = hextable[v>>4] - dst[i*4+3] = hextable[v&0x0f] + for _, f := range files { + content, err := ioutil.ReadFile("data/" + f.Name()) + if err != nil { + panic(err) + } + dst := make([]byte, len(content)*4) + for i, v := range content { + dst[i*4] = '\\' + dst[i*4+1] = 'x' + dst[i*4+2] = hextable[v>>4] + dst[i*4+3] = hextable[v&0x0f] + } + name := f.Name()[0 : len(f.Name())-5] // assumes .joke extension + fileContent := strings.Replace(template, "{name}", name, 1) + fileContent = strings.Replace(fileContent, "{content}", string(dst), 1) + ioutil.WriteFile("a_"+name+"_data.go", []byte(fileContent), 0666) } - - ioutil.WriteFile("core_data.go", []byte(strings.Replace(coreTemplate, "{content}", string(dst), 1)), 0666) } diff --git a/core/parse.go b/core/parse.go index e9e248b2a..667e6a5c3 100644 --- a/core/parse.go +++ b/core/parse.go @@ -3,6 +3,7 @@ package core import ( "fmt" "os" + "strings" "unsafe" ) @@ -130,11 +131,12 @@ type ( frame int } ParseContext struct { - GlobalEnv *Env - localBindings *Bindings - loopBindings [][]Symbol - recur bool - noRecurAllowed bool + GlobalEnv *Env + localBindings *Bindings + loopBindings [][]Symbol + recur bool + noRecurAllowed bool + isUnknownCallableScope bool } ) @@ -822,6 +824,15 @@ func parseSetMacro(obj Object, ctx *ParseContext) Expr { panic(&ParseError{obj: obj, msg: "set-macro* argument must be a var"}) } +func isUnknownCallable(expr Expr) bool { + switch c := expr.(type) { + case *VarRefExpr: + return c.vr.Value == nil + default: + return false + } +} + func parseList(obj Object, ctx *ParseContext) Expr { expanded := macroexpand1(obj.(Seq), ctx) if expanded != obj { @@ -899,8 +910,16 @@ func parseList(obj Object, ctx *ParseContext) Expr { return parseTry(obj, ctx) } } + callable := Parse(first, ctx) + if isUnknownCallable(callable) { + t := ctx.isUnknownCallableScope + ctx.isUnknownCallableScope = true + defer func() { + ctx.isUnknownCallableScope = t + }() + } res := &CallExpr{ - callable: Parse(first, ctx), + callable: callable, args: parseSeq(seq.Rest(), ctx), Position: pos, name: "fn", @@ -957,6 +976,10 @@ func internFakeSymbol(sym Symbol, ctx *ParseContext) *Var { return ctx.GlobalEnv.CurrentNamespace().Intern(MakeSymbol(ns + *sym.name)) } +func isInteropSymbol(sym Symbol) bool { + return sym.ns == nil && (strings.HasPrefix(*sym.name, ".") || strings.HasSuffix(*sym.name, ".")) +} + func parseSymbol(obj Object, ctx *ParseContext) Expr { sym := obj.(Symbol) b := ctx.GetLocalBinding(sym) @@ -977,9 +1000,11 @@ func parseSymbol(obj Object, ctx *ParseContext) Expr { if !LINTER_MODE { panic(&ParseError{obj: obj, msg: "Unable to resolve symbol: " + sym.ToString(false)}) } else { - symNs := ctx.GlobalEnv.NamespaceFor(ctx.GlobalEnv.CurrentNamespace(), sym) - if symNs == nil || symNs == ctx.GlobalEnv.CurrentNamespace() { - fmt.Fprintln(os.Stderr, &ParseError{obj: obj, msg: "Unable to resolve symbol: " + sym.ToString(false)}) + if !isInteropSymbol(sym) && !ctx.isUnknownCallableScope { + symNs := ctx.GlobalEnv.NamespaceFor(ctx.GlobalEnv.CurrentNamespace(), sym) + if symNs == nil || symNs == ctx.GlobalEnv.CurrentNamespace() { + fmt.Fprintln(os.Stderr, &ParseError{obj: obj, msg: "Unable to resolve symbol: " + sym.ToString(false)}) + } } vr = internFakeSymbol(sym, ctx) } diff --git a/core/procs.go b/core/procs.go index 3386519d1..814c8a92c 100644 --- a/core/procs.go +++ b/core/procs.go @@ -1301,6 +1301,19 @@ func intern(name string, proc Proc) { vr.meta = privateMeta } +func processData(data []byte) { + currentNamespace := GLOBAL_ENV.ns.Value + GLOBAL_ENV.ns.Value = GLOBAL_ENV.CoreNamespace + reader := bytes.NewReader(data) + ProcessReader(NewReader(reader, ""), "", EVAL) + GLOBAL_ENV.ns.Value = currentNamespace +} + +func ProcessLinterData() { + reader := bytes.NewReader(linterData) + ProcessReader(NewReader(reader, ""), "", EVAL) +} + func init() { rand.Seed(time.Now().UnixNano()) GLOBAL_ENV.CoreNamespace.InternVar("*assert*", Bool{B: true}, @@ -1452,9 +1465,5 @@ func init() { intern("index-of*", procIndexOf) intern("lib-path*", procLibPath) - currentNamespace := GLOBAL_ENV.ns.Value - GLOBAL_ENV.ns.Value = GLOBAL_ENV.CoreNamespace - reader := bytes.NewReader(coreData) - ProcessReader(NewReader(reader, ""), "", EVAL) - GLOBAL_ENV.ns.Value = currentNamespace + processData(coreData) } diff --git a/main.go b/main.go index 2abb6c798..382140f1f 100644 --- a/main.go +++ b/main.go @@ -147,6 +147,13 @@ func repl(phase Phase) { } } +func configureLinterMode() { + ProcessLinterData() + LINTER_MODE = true + lm, _ := GLOBAL_ENV.Resolve(MakeSymbol("core/*linter-mode*")) + lm.Value = Bool{B: true} +} + func main() { GLOBAL_ENV.FindNamespace(MakeSymbol("user")).ReferAll(GLOBAL_ENV.FindNamespace(MakeSymbol("core"))) if len(os.Args) == 1 { @@ -165,9 +172,7 @@ func main() { case "--read": processFile(os.Args[2], READ) case "--parse": - LINTER_MODE = true - lm, _ := GLOBAL_ENV.Resolve(MakeSymbol("core/*linter-mode*")) - lm.Value = Bool{B: true} + configureLinterMode() processFile(os.Args[2], PARSE) default: processFile(os.Args[1], EVAL) diff --git a/tests/symbols-lint.joke b/tests/symbols-lint.joke index 8a681c780..e27486f8c 100644 --- a/tests/symbols-lint.joke +++ b/tests/symbols-lint.joke @@ -3,6 +3,8 @@ [tests.ext2 :as ext2] [tests.ext3 :as ext3 :refer [e3]])) + + (def f []) (f 1) @@ -16,3 +18,5 @@ (tt/g 3) (ext2/g 4) + +(defprotocol Person)