ryansuchocki / microscheme

A Scheme subset for Atmel microcontrollers.
http://ryansuchocki.github.io/microscheme/
MIT License
300 stars 20 forks source link

Conditional compile-time settings #32

Closed technomancy closed 1 year ago

technomancy commented 4 years ago

I've got a project that would benefit from being able to pass a flag at compile-time in to embed it in the code.

I would imagine it would be difficult to do this in a way that's portable to other schemes, but maybe using get-environment-variable along the lines of SRFI-98 would make sense, with the caveat that the value is fixed at compile-time? Or would it be better to use a completely different name?

ryansuchocki commented 4 years ago

I think implementing (the 1-argument form of) get-environment-variable is a sensible way of doing this. The main caveat is that (get-environment-variable "FOO") will be equivalent to a string literal in terms of memory usage (specifically, 2n bytes on the heap which can only be recovered using free!). In usage, it will be prudent to keep the (get-environment-variable) out of any loops and keep the values as short as possible.

Another option would be to implement something like the current @if-model primitive, but this is probably more work.

technomancy commented 4 years ago

I took a very rough shot at implementing this here: https://github.com/technomancy/microscheme/commit/3a8838560c40755ef2e3d150f95d996b9008bf4c

In my patch I attempt to replace the get-environment-variable expression with a string literal. However, when I tested it, it doesn't work:

(include (get-environment-variable "LAYOUT"))
LAYOUT=layout.scm microscheme -m LEO menelaus.scm
Microscheme 0.9.3, (C) Ryan Suchocki
>> ERROR 18: First operand to INCLUDE should be STRING
Makefile:25: recipe for target 'menelaus.s' failed
make: *** [menelaus.s] Error 1

Any ideas what might be going on there?

ryansuchocki commented 4 years ago

This is because, in microscheme, include is not really a function. It's a fundemental form. Specifically, this means that the contents of the included file are parsed and embedded into the abstract syntax tree before any code generation occurs. (If this were not the case then we would require multiple cycles of parsing and code generating before all includes are resolved...)

Since get-environment-variable is implemented as a primitive function, the transformation (into a string) occurs too late for it to work as the argument to include. To have it work in this scenario we would need to make get-environment-variable itself a fundamental form, and then do something more complex in the lexer and/or parser.

If using the environment variable with include is important then the cleanest way to achieve it is probably to use some special atomic (i.e. not involving parentheses) character sequence like %VARNAME. Such a character sequence could be transformed very early on (i.e. in the lexer) into a string literal token and then it would behave exactly the same as a string literal in all fundemental forms, including (include %LAYOUT). It would be a shame not to have compatibility with other schemes but there is probably no perfect solution here because we are getting into territory where the difference between compilation and interpretation start to matter a lot.

technomancy commented 4 years ago

Ahh... I see now that it's happening inside the parser; I did not expect that.

If using the environment variable with include is important then the cleanest way to achieve it is probably to use some special atomic (i.e. not involving parentheses) character sequence

No, in my case having some means of portability is more important than making it work seamlessly with include; I can include both files and simply switch between the two at runtime. Thanks.