-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to automatically import macros file? #75
Comments
Either the https://fennel-lang.org/api, see I think the example fennel plugin does something quite similar around missing syms, so that would be another route. You can hook onto parse-error I think and inject the symbol. Just be sure to return nil (I think) so other plugins continue to run. You should be able to just add your plugin to the compiler I can't see the option immediately in the docs but I do just pass a hotpot.nvim/fnl/hotpot/searcher/module.fnl Lines 73 to 76 in 64b6470
I would try to make that work with the fennel CLI then post back and we can look at how to configure hotpot to use it and any possible fixes needed. Probably the Fennel chat would have some insights too, but primarily get a POC working with the CLI env - to be sure it's actually possible - and then it should be clear how to integrate that into Hotpot. |
This could be a good reference then |
I'd be curious how you go with a plugin or compileEnv option first as I think that is cleanest and most "fennel"-ly, but it could also be possible to expose a hook, before I call compile, where you could get say, I actually thought about this a while a long while ago (#37) but no one else ever gave a reason to do the work, perhaps its worth revisiting. I can't promise a timeline on this though. Diagnostics might be a burden too. If the plugin doesn't work, or is too difficult you could always fork for now until (or if?) hooks appear. Just patch hotpot.nvim/fnl/hotpot/compiler.fnl Line 46 in 64b6470
with something like fnl-code (read-file! fnl-path)
fnl-code (if (string.match fnl-path "some-path-i-know-is-my-code")
(.. "(require-macros macro-file)" fnl-code)
fnl-code) |
I thank you so much for your insight. Honestly there is no rush on my side so please take your time for the implementation. I believe this worths it and really love your approach ❤️❤️❤️ |
I have a question as I am also interested on at least trying to implement something like this.
Where can I find that default plugin, and where is the plugins documentation for Fennel? I have only found this, and it doesn't show any example. |
I think I was mixing linter.fnl with perhaps some chatter on matrix. I poked at it for a minute or two but not 100% sure it's possible to return code that the compiler should insert, vs just inspecting code the compiler saw. I would think the best chance is with I think andreyorst has some plugin that lets you use This doesn't work but might be useful, as well as grepping for ;; file.fnl
;; fennel --plugin plug.fnl -c file.fnl
(fn a-scope-fn [] (print "this exists to help see the scope"))
(map-seq [1 2 3] #(print &1)) ;; plug.fnl
(fn map-seq-fn [seq f]
(icollect [_ v (ipairs seq)] (f v)))
(macro map-seq-macro [seq f]
`(icollect [_ v (ipairs ,seq)] (,f v)))
(fn parse-error [...]
;; not the way, our code is valid, just unrunnable/uncompilable
(print :parse-error (view [...])))
(fn assert-compile [cond msg ast something-called-reset]
;; seems this cant modifiy scope, see compiler.fnl:50, utils.fnl:358
(print :assert-compile ast))
(fn symbol-to-expression [ast scope]
;; Doing this will get you a "tried to ref macro at runtime", possible pathway here.
; (print (view ast {:metamethod? false}))
(match ast
[:map-seq] (do
(tset scope :macros :map-seq (fn [seq f] (list)))
(print :map-seq-post-scope (view scope))))
(values nil))
(fn call [ast scope ...]
; (print :call (view [ast] {:metamethod? false}))
(match ast
[[:map-seq]] (do
(tset scope :macros :map-seq (fn [seq f] (list)))
(print :map-seq-post-scope (view scope))))
(values nil))
(fn chunk [ast? scope]
; undocumented, compiler.fnl compile-stream
; (print :xy (view ast?) (view scope))
(match ast?
[[:map-seq]] (do
(tset scope :macros :map-seq (fn [seq f] (list)))
(print :map-seq-post-scope (view scope)))))
{:name :magic-map-seq
; :call call
:chunk chunk
; :symbol-to-expression symbol-to-expression
; :parse-error parse-error
; :assert-compile assert-compile
:versions [:1.1.0]} |
Did you get anywhere with the plugin idea? |
No, I couldn't find any way to make it work using plugins, so I scrapped the idea. |
;; plugin.fnl
;; must define as function that returns a list
(fn map-seq-fn [seq f]
`(icollect [_# v# (ipairs ,seq)] (,f v#)))
(fn call [ast scope ...]
(match ast
;; match against symbol and capture arguments
[[:map-seq] & other]
;; written as do for comment clarity
(do
;; expand our macro as compiler would do, passing in capture arguments
(local macro-ast (map-seq-fn (unpack other)))
;; now expand that ast again (this expands icollect etc, *other* macros)
(local true-ast (macroexpand macro-ast))
;; change ast to match macro ast, note that we must
;; **modifiy** the ast, not return a new one, as we're
;; actually modifying the ast back in the compiler call-site.
(each [i ex-ast (ipairs true-ast)]
(tset ast i ex-ast))))
;; nil to continue other plugins
(values nil))
{:name :magic-map-seq
:call call
:versions [:1.2.1]} ;; file.fnl
;; fennel --plugin plug.fnl -c file.fnl
(map-seq [1 2 3] #(print $)) -- fennel --plugin plugin.fnl -c file.fnl
local tbl_17_auto = {}
local i_18_auto = #tbl_17_auto
for __1_auto, v_2_auto in ipairs({1, 2, 3}) do
local val_19_auto
local function _1_(_241)
return print(_241)
end
val_19_auto = _1_(v_2_auto)
if (nil ~= val_19_auto) then
i_18_auto = (i_18_auto + 1)
do end (tbl_17_auto)[i_18_auto] = val_19_auto
else
end
end
return tbl_17_auto # fennel --plugin plugin.fnl file.fnl
1
2
3 Will patch Hotpot to support custom plugins (should not be hard, just needs to pass through a |
Going to close this as IMO the plugin method is more precise (and probably the intended path from upstream "if you really had to") than a hook that jams text everywhere but still open to that idea in the future. See cookbook for more details. |
I have a question regarding this (finally got time to test this). I got your example working but when I try to import my macros Fennel cannot find the macros. See this example: ; Originally I tried with `themis.var` and setting `--add-fennel-path 'fnl/?.fnl'`, but it didn't work
(local {: let!} (require :fnl.themis.var))
(fn call [ast scope ...]
(match ast
[[:let!] & args] (do
(local macro-ast (let! (unpack args)))
(local true-ast (macroexpand macro-ast))
(each [i ex-ast (ipairs true-ast)]
(tset ast i ex-ast))))
(values nil))
{:name :themis
:call call
:versions [:1.2.1]} When I run
Do you have any idea about how to fix this? |
Not sure... Try dumping out Possibly (probably?) plugins cant require any fennel code as ... the compiler is still bootstrapping? I admit I only tried embedded macros in the file, not a require - but that would make sense to me. I would try to load a lua file that's in the same dir as the plugin (bet that works), then try to load a normal fnl module too (bet that fails). Perhaps the hook idea is worth returning to in the end. Probably it would look similar to the make api where you match on a pattern and get the file contents, alter it however you want and return the string. |
Here is the result of
Here is the result of
A Lua file can be required without any problems but when I try to |
Hi, I wanted to do something similar and was following the cookbook in (fn map-seq-fn [seq f]
`(icollect [_# v# (ipairs ,seq)] (,f v#)))
(fn call [ast scope ...]
(match ast
;; match against symbol and capture arguments
[[:map-seq] & other]
;; written as do for comment clarity
(do
;; expand our macro as compiler would do, passing in capture arguments
(local macro-ast (map-seq-fn (unpack other)))
;; now expand that ast again (this expands icollect etc, *other* macros)
(local true-ast (macroexpand macro-ast))
;; change ast to match macro ast, note that we must
;; **modifiy** the ast, not return a new one, as we're
;; actually modifying the ast back in the compiler call-site.
(each [i ex-ast (ipairs true-ast)]
(tset ast i ex-ast))))
;; nil to continue other plugins
(values nil))
{:name :nyoom : call :versions [:1.2.1]} And then I load the plugin as follows; if pcall(require, "hotpot") then
require("hotpot").setup({
provide_require_fennel = true,
enable_hotpot_diagnostics = false,
compiler = {
modules = {
correlate = true,
},
macros = {
env = "_COMPILER",
compilerEnv = _G,
allowGlobals = true,
plugins = { "core.patch" },
},
},
})
require("core")
else
print("Unable to require hotpot")
end However it errors out on compilation with a stack overflow (even if I don't use Clearing cacheError detected while processing /Users/shauryasingh/.config/nvim/init.lua:
E5113: Error while calling lua chunk: /Users/shauryasingh/.config/nvim/init.lua:69: module 'core' not found:Compile error in /Users/shauryasingh/.config/nvim/fnl/core/init.fnl:24:6
stack overflow
^[[7m(import-macros {: command! : let! : set!} :macros)^[[0m |
Yeah I think it's a dead end for the idea, it's not really upstream supported and only useful for adding small forms probably. Probably I will add a hook interface that is similar to the The handler would accept at least the code as a string and should return a string or ... (nil err)? or throw error? Probably hooks can only be set at "setup" and not adjusted dynamically unless there is a good reason to support that. (setup
{...
:hooks ["config.stuff" (fn [code {:macro? true|false : some : helpers?}]
(.. "(print :hi)" code))]}) (setup
{...
:hooks [(fn [modname code {:macro? : some : helpers?}]
(if (= modname "config.stuff")
(.. "(print :hi)" code))]}) Not sure if the key should be something more like Any input on the actual usecase (eg vague pseudo code or descriptions) would be helpful. There is some muck involved as the compiler has a few entry points that need to keep options separate (e.g the "main" compiler, api.make compiler, api.compile-string) but I have been meaning to clean that up anyway. |
Looks good to me
I think just having
I personally would like my configuration macros to feel more "native", it's just a matter of convenience not having to import an Similar to how aniseed appends |
So there is a very beta option It should be a function that accepts Since modname may be nil, its probably best to match on the path, which should generally be a full path, but also may be nil if a blank buffer has its Not super happy with how its put together, but any changes will be posted here with some period of deprecation.
|
@rktjmp I'm wondering how to make macros file available without using
import-macros
in each module/file?For example, I have a
:core.macros
which defines some macros such asif-let
,when-let
.... I want those available through all modules without usingimport-macros
. Is it possible?The text was updated successfully, but these errors were encountered: