These macros apply an operation to the current value of a variable and
then set the variable to the result of the application. They are
effectively sugar for writing `(set! <var> (<op> <var> <val>))` and
should be familiar to those who have programmed in imperative languages
like C.
In Carp, all the underlying operations these macros use are interfaces,
so one can flexibly use them for more than just numeric types.
Example usage:
```clojure
(let-do [dial 0]
;; crank it up to 11!
(while-do (dial < 12)
(++ dial))
dial)
;; expanded
(let-do [dial 0]
;; crank it up to 11!
(while-do (dial < 12)
(set! dial (inc dial)))
dial)
```
* feat: Add additional ignore macros
This commit adds two new ignore macros, ignore*, which wraps an
arbitrary number of forms in calls to `ignore` and ignore-do, which
wraps an arbitrary number of forms in ignore, then bundles the whole in
a do call, effectively executing each form only for side effects and
ignoring all results.
* docs: Update ignore* docs
Link to `ignore` doc
Co-authored-by: Veit Heller <veit@veitheller.de>
* fix: Call ignore* in ignore-do
ignore-all was an old name that no longer exists!
* test: Add test for ignore-do
Co-authored-by: Veit Heller <veit@veitheller.de>
* refactor: Tiny cleanup before we begin
* refactor: Moved module finding to its own local function
* feat: save-docs-internal is now a binary command
* feat: This seems to work
* fix: Cleaned up the code, save-docs now emit one module per file listed
* feat: generalized (and) and (or) to handle any number of parameters
* feat!: removed (and*) and (or*) macros
* chore: worked around compiler issue for unit test
* fix: unit test in ./test/macro.carp
Co-authored-by: guberatsie <gunnar.bernhardt@siemens.com>
* feat: Add support for emitting literal C
This commit adds a new Obj type, C, as well as a command for emitting C
literals (which are represented by this object).
This is necessary for circumstances in which Carp's handling of
templates and emission orders prevents interop with C. For example, the
c11 macro static_assert must only take a string literal in its second
position. However, Carp's memory management will typically assign a
string literal (in Carp) to an anonymous variable, which makes them
impossible to use with static_assert. The new emit-c command will
support this use case:
```
(static-assert 0 (Unsafe.emit-c "\"message!\""))
```
The literal string "message!" will be emitted in the compiler's C output
in the position corresponding to the macro call.
We also add a special type for c literals, CTy, to prevent conflating
them with Strings. This helps maintainers define clear boundaries and
express what interop code requires the use of literal C.
Likewise, we need to emit `c_code` to represent this type in C. This
wouldn't be necessary except that Carp sometimes auto-generates
functions that refer to Carp types in their C equivalents, so we need
this for completeness. It is typed as the void pointer.
N.B. That the command is not yet exposed in Carp in this commit.
Thanks to @TimDeve for recommending the name emit-c!
* feat: Add preproc command and add emit-c to Unsafe module
This commit adds the preproc command, which enables users to emit
arbitrary C in the compilers emitted C output.
The C emitted by calls to preproc will be appended to the output after
include directives but prior to any other emissions. One can use it to
call preprocessor directives in C, define helper functions etc.
preproc takes a C type value as an argument and must be used in
conjunction with emit-c. Both functions are added to the Unsafe module
to signal their dangerousness.
One can use `register` in combination with preproc to define code
entirely in Carp and obviate the need of additional header files. For
example:
```
(Unsafe.preproc (Unsafe.emit-c "#define FOO 0"))
(Unsafe.preproc (Unsafe.emit-c "void foo() { printf(\"%d\\n\", 1); }"))
(register FOO Int)
(register foo (Fn [] ()))
(defn main []
(do (foo)
(IO.println &(fmt "%d" FOO))))
```
The prior example emits C that defines a FOO macro and foo function,
which are both referenced in the main function emitted by Carps normal
processing. Here's what the output looks like:
```
// .. several other includes emitted by the compiler...
void foo() { printf("%d\n", 1); }
//Types:
// Depth 3
typedef struct {
union {
struct {
Long member0;
} Just;
// Nothing
char __dummy;
} u;
char _tag;
} Maybe__Long;
```
The C passed to preproc calls is emitted prior to Carps other emissions,
ensuring the user has access to these definitions before any Carp code
is called.
* docs: Add documentation on emit-c and preproc
* feat: add macros for emitting C compiler directives
This suite of macros uses the `Unsafe.emit-c` and `Unsafe.preproc`
functions to provide macros for emitting common C compiler directives,
such as #ifdef, #define, #pragma and others.
* refactor: Groups Dynamic together in Macros.carp
* fix: Fixes doc for `hidden` referring to the wrong symbol
* feat: Adds defn- & def- macros
Adding these macros as a shortand for declaring a def or defn and making
them `hidden` and `private`, useful to keep things internal to a module.
* test: Adds expected error output tests for def- & defn-
* refactor: Changes position of Module and Interface section in LanguageGuide
Trying to introduce concepts in the same order they are referred to in
the examples: structs > modules > interfaces.
* docs: Adds private & hidden section in the LanguageGuide
* refactor: move Context updates into functions
Previously, we had a lot of instances of updating Context records
directly, replacing environments where needed. This commit replaces
those hand-written record setting instances with functions, which should
allow us to more gracefully abstract over any preprocessing we should
have to do and help ensure we're updating contexts in the right way.
* refactor: replace inline context manipulation in primitives
Like the commit that altered Eval before it, this commit leverages
Context functions to remove a bunch of inline record field setting code
and direct env manipulation.
* refactor: replace generic binder lookups with contextual ones
* refactor: move true and false XObjs into Obj.hs
Previously, trueXObj and falseXObj were defined in Commands.hs, but
since they're just literal constructed XObj values, I feel Obj.hs is a
more appropriate home for them and makes them more widely accessible to
other modules without needing to import Commands.
* refactor: model symbol qualification requirements at typelevel
This commit refactors the Qualify module to express symbol qualification
at type level. In the past, all functions operated on SymPaths. In some
cases, the functions operated on paths that *were not yet qualified* and
so the functions would perform qualification inline. Contrarily, other
functions like define received XObjs (from another part of the codebase
entirely!) that were already fully qualified, and so it would be a grave
mistake to re-qualify them.
In the general case, it's difficult or impossible to tell across modules
whether or not a given SymPath is coming in qualified or unqualified,
which can easily lead to mistakes of double-qualification, e.g.
transforming `Foo.bar` into `Foo.Foo.bar`.
Modelling qualification in the type system enables us to avoid the
problem by distinguishing between unqualified and qualified paths. A
function receiving an SymPath can safely qualify it, whereas a function
receiving a QualifiedPath should not further qualify the path. This
helps better express and ensure constraints across modules.
In addition, this commit also refactors a few functions where there was
opportunity to do so.
* refactor: remove eval call from `doc`
This can lead to problems where a doc call intended to be evaluated
later (in a macro body) is evaluated *immediately* resulting in a
binding being added to the wrong scope (see the function reverse in
core).
The reason this behavior crops up now is that a special case for
evaluating module contexts was removed last commit--this special case
caused problems of its own, and the real root of things stems from the
unnecessary eval call. Generally, evaling a doc call provides no benefit
other than making evaluation of the meta set immediate in the repl,
which is easy enough for one to do on one's own by calling eval where
needed.
* refactor: use do notation to clarify case qualification
* refactor: rename runQualified to unQualified
@eriksvedang pointed out the `run` prefix typically denotes a monad. As
`Qualified` is not monadic (no monad instance defined) we drop the `r`
to ensure we don't mislead readers.
* refactor: convert a few more binds to do notation
Do notation is generally clearer in cases where we use mapM, etc. We can
also leverage liftM frequently in the qualification functions to
transform a Qualified xobj back into an xobj for further use.
* refactor: temporarily restore special case in meta set
Meta set disallows setting the meta of a prefixed, absolute path such as
`Foo.bar`. It only allows relative, unqualified paths `bar` and uses the
current context to determine the appropriate module.
If we eventually throw and error from envInsertAt, we can remove this
special case. I intend to do that later, but for now we'll keep the
special case to make for a more pleasant user experience.
* refactor: Move code out of Macros.carp into other files
* fix: Move back some macros needed in --no-core mode
* refactor: Remove weird 'evaluate' macros
* fix: Put back more macros
* fix: Remove transitive loading of Macros.carp
* refactor: Remove ArrayMacros.carp and put 'for' at top of Array.carp instead
* refactor: More splitting up
* refactor: Move back save-docs
* fix: Moved back some stuff
Co-authored-by: Erik Svedang <erik@Eriks-iMac.local>
Carp preserves tabulation and other whitespace in strings, as it should.
This sometimes results in awkward code indentation when it comes to long
doc strings that exceed 80 characters. Often, one has to continue the
string on a newline, but needs to avoid adding tabs to prevent Carp from
rendering them in the output.
This change alters the behavior of doc to take a series of strings as a
rest parameter instead, allowing for neater organization in the source,
for example, after this change the following long doc string for
compose:
~~~
"Returns the composition of two functions `f` and `g` for functions of any
arity; concretely, returns a function accepting the correct number of
arguments for `g`, applies `g` to those arguments, then applies `f` to the
result.
If you only need to compose functions that take a single argument (unary arity)
see `comp`. Comp also generates the form that corresponds to the composition,
compose contrarily evaluates 'eagerly' and returns a computed symbol.
For exmaple:
```
;; a silly composition
((compose empty take) 3 [1 2 3 4 5])
;; => []
(String.join (collect-into ((compose reverse map) Symbol.str '(p r a c)) array))
;; => 'carp'
;; comp for comparison
((comp (curry + 1) (curry + 2)) 4)
;; => (+ 1 (+ 2 4))
```"
~~~
becomes:
~~~
"Returns the composition of two functions `f` and `g` for functions of any"
"arity; concretely, returns a function accepting the correct number of"
"arguments for `g`, applies `g` to those arguments, then applies `f` to the"
"result."
""
"If you only need to compose functions that take a single argument (unary arity)"
"see `comp`. Comp also generates the form that corresponds to the composition,"
"compose contrarily evaluates 'eagerly' and returns a computed symbol."
"```"
";; a silly composition"
"((compose empty take) 3 [1 2 3 4 5])"
";; => []"
""
"(String.join (collect-into ((compose reverse map) Symbol.str '(p r a c)) array))"
";; => 'carp'"
""
";; comp for comparison"
"((comp (curry + 1) (curry + 2)) 4)"
";; => (+ 1 (+ 2 4))"
"```")
~~~
And the output remains the same; this just enables better alignment in
the source file.
The strings passed to doc are newline separated by default, but one can
circumvent this behavior by passing a bool along with the string as
follows:
~~~
(doc foo
("My amazing doc " false)
"continues on one line."
""
"And then another.")
~~~
The above doc string will result in the following output:
~~~
My amazing doc continues on one line.
And then another.
~~~
Of course, the original behavior of doc also remains valid, so if one
prefers to use the old indentation-mixed single string format, one still
can!
This change also reformats the doc strings in macro to utilize the new
rest parameter and make the source a bit neater.
gensym-local is similar to gensym-with, with it's arguments reversed.
That is, rather than allowing the user to specify a custom qualifier, it
allows the user to specify a custom counter (or any symbol) and appends
this to the default gensym `gensym-generated` symbol.
This enables one to, e.g. map over an array and generate symbols:
```
(map gensym-local (map Symbol.from [1 2 3]))
=> (gensym-generated1 gensym-generated2 gensym-generated3)
```
Theoretically, passing `gensym` as-is to `map` would accomplish this
using the global gensym-counter, but the counter is not incremented on
subsequent calls. This function gives users more flexibility as well.
I accidentally broke `curry`, what better incentive for writing some
tests than preventing my own future silly mistakes :)
I also added and-internal because we cannot perform direct comparisons
on lists--so, instead we build member-wise comparisons by zipping, then
reduce the result using and.
I accidentally committed a change to the definition of curry that
severly alters how it functions! This commit fixed that, and adds a test
so that I don't unwittingly break it again :)
Originally, curry* required double quoting function arguments in some
cases, due to an eval and lack of quotes in the function body it
produces. This is not ideal, as having to type ''(form) is quite
esoteric. Now we handle the extra quoting in the function itself, so
that only one quote is required.
I also fixed the order of filter (which was reversing results).
This commit adds several dynamic utility functions in the spirit of map,
zip, et al, including:
- Compose (composer for functions of any airty, that evaluate
immediately (unlike comp)
- empty (returns the empty value for a structure [] or ())
- unreduce (builds a list of values)
- filter (filters a list of values)
- take (returns the first x members of a list)
I also removed a quote in collect-into that's no longer necessary after
the evaluator refactor.
We currently define two dynamic functions, map and zip. Each performs
the traditional operation given by these names, however, map, emulated
clojure's map, which would selectively apply map or zip depending on the
number of lists passed as arguments. This change removes that selection,
making the execution of map more predicatable.