Skip to content
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

[CPP LISP] Need a helping hand implementing double #684

Closed
emanuel4you opened this issue Sep 29, 2024 · 9 comments
Closed

[CPP LISP] Need a helping hand implementing double #684

emanuel4you opened this issue Sep 29, 2024 · 9 comments

Comments

@emanuel4you
Copy link

emanuel4you commented Sep 29, 2024

Hi,
I think the LISP interpreter examples are great!

I tried to incorporate double into the cpp example.
I managed it, but I don't like it. :-(
Your code is so beautifully written,
maybe you could take a quick look at my code
and give me a few tips.

Thanks in advance

Best regards

Emanuel ;-)

$ ./step2_eval
user> (+ 4 5.5)
9.500000
user> (+ 4.5 5)
9.500000
user> (+ 4 5)
9
user>

This also doesn't work anymore if I want to do something like this later:

(+ 1 2 3.14 4 5)

cpp.patch.txt

@emanuel4you
Copy link
Author

emanuel4you commented Sep 29, 2024

Next attempt, long source text:

static malValuePtr builtIn_add(const String& name,
    malValueIter argsBegin, malValueIter argsEnd)
{
#ifdef FLOAT
    CHECK_ARGS_AT_LEAST(2);

    bool hasFloat = false;
    double floatValue = 0;
    int16_t intValue = 0;

    do
    {
        if (argsBegin->ptr()->isFloat())
        {
            hasFloat = true;
            malDouble *floatVal = dynamic_cast<malDouble*>(argsBegin->ptr());
            floatValue += floatVal->value();
        }
        else
        {
            malInteger *intVal = dynamic_cast<malInteger*>(argsBegin->ptr());
            intValue  += intVal->value();
        }
        argsBegin++;
    } while (argsBegin != argsEnd);

    if (hasFloat)
        return mal::mdouble(floatValue + intValue);
    else
        return mal::integer(intValue);
#else
        CHECK_ARGS_IS(2);
        ARG(malInteger, lhs);
        ARG(malInteger, rhs);
        return mal::integer(lhs->value() + rhs->value());
#endif
}
user> (+ 5 3 5 12.5)
(+ 5 3 5 12.500000)
25.500000
user> 

@kanaka
Copy link
Owner

kanaka commented Oct 1, 2024

That looks like a reasonable approach. You could always pass an operation argument and then share the same multiple argument code among different numeric ops.

@kanaka
Copy link
Owner

kanaka commented Oct 1, 2024

@emanuel4you Oh, I should note that I'm not the author of the C++ implementation. That would be @sdt all the way back in 2015.

@OldLiu001
Copy link
Contributor

(def! + (let* (add +) (fn* (a & b) (if (empty? b) a (add a (apply + b))))))

:)

@sdt
Copy link
Contributor

sdt commented Oct 1, 2024

Hello!

It would seem like the common pattern here is you have a sequence of values that might be integers, or doubles, or something else.

Would it help to make some sort of utility function that walks over that sequence and checks what you have, and then return a normalised sequence?

If they're all integers, then you want to operate on them as integers - you can just return the original sequence.

If there's a mix of integers and doubles, then you want to operate on them as doubles. Create a new sequence by copying the doubles, and converting the integers to doubles.

If there's an item that's neither an integer nor a double, throw an error.

Then in code like builtin_add, call your utility function to get the normalised sequence. You know that all the values of that sequence will be the same type, so you can just check the first one to decide whether to take the integer path or the double path.

@emanuel4you
Copy link
Author

First of all, a huge thank you to everyone.

I have now successfully replaced almost all functions with real double types.
It was a big brain teaser, but ultimately the source is short and also saves resources.
Doubles are only used when they are absolutely necessary.
I check the type beforehand and it all worked thanks to the good example with a few C macros.

I plan to use it for a mini CAD project.
I will base it on AcadLisp.
But I also want to be able to use the additional one.
The dialect of this LISP is a bit different.

There is already the first problem :
(~ n)

It is already taken for macros, what should I do? :-O
What I also have no idea about is how I can port
(repeat n (....))
and
(while (bla)(...))

Attached is a small log for those interested.
The whole thing is already a bit AutoCADLisp-heavy, but I will publish a source that conforms to the model.

user> (1+ pi)
(1+ 3.141593)
4.141593
user> (+ pi 7.4 1 5)
(+ 3.141593 7.400000 1 5)
16.541593
user> (/= 0 0.0)
nil
user> (= 0 0.0)
T
user> (sin 1)
(sin 1)
0.841471
user> (tan 1)
(tan 1)
1.557408
user> (expt 3 2)
(expt 3 2)
9
user> (cos 0.5)
(cos 0.500000)
0.877583
user> (number? 42.0)
number?
T
user> (float 3)
(float 3)
3.000000
user> (atof 7)
(atof 7)
0.000000
user> (atof "7")
(atof "7")
7.000000
user> (atoi "Hallo")
(atoi "Hallo")
0
user> (atoi "42")
(atoi "42")
42
user> pi
3.141593
user> (abs -0.33333)
(abs -0.333330)
0.333330
user> (- 45.08 3)
(- 45.080002 3)
42.080002
user> (integer? 1.0)
(integer? 1.000000)
nil
user> (integer? 1)
(integer? 1)
T
user>

lisp.log.txt

Thank you!

@sdt
Copy link
Contributor

sdt commented Oct 2, 2024

Nice work there @emanuel4you 👍🏻

I'm not going to be able to help you with the rest I'm afraid.

Regarding your (~ n) problem (I take it this means something else in acadlisp) - one of the many beautiful things about the make-a-lisp project is that you've also made your lisp. You can change it however you want. If you need ~ for your own purposes, take it. You're free to choose other symbols for unquote and splice-unquote.

@emanuel4you
Copy link
Author

emanuel4you commented Oct 3, 2024

So, I've posted everything here.
If you're interested, you're welcome to try it out. :-)

Update to mal cpp:
add new: double type
add new calculating with opperators like: user> (+ 1 2 3.0 ...)
add new symbols (constant): pi
add new symbols (opperator): %, /=, 1+, 1-
add new symbols (function): abs, ascii, atan, atof, atoi, car, cadr, caddr, cdr, chr, cos, exit, expt, float, sin, sqrt, substr, tan, zero?

@sdt
I'm just a little hobby programmer,
In 1980 I learned Basic at school.
In 1990 I came to LISP via AutoCad.

I really enjoyed the project,
it showed me once again that C++ can't cast everything.
(see the function itoa64) that I need for chr (int to ascii) .

I see the inclusion of "while" and "repeat" as superfluous at this point,
considering the fact that the "EVAL" function causes this TCO problem.
I think that is very difficult to remove or to rebuild everything like that.
that a loop would run.

LG Emanuel

@kanaka
Copy link
Owner

kanaka commented Oct 3, 2024

@emanuel4you that test file indicates that recursive non-TCO code will segfault the C++ implementation. But the C++ implementation does support TCO, so you should be able to implement repeat and while forms. Something like this:

For example, here is a tail-recursive Clojure style dotimes (except this version counts down instead of up):

user> (defmacro! dotimes (fn* [bind expr] (let* [b (nth bind 0) n (- (nth bind 1) 1)] (if (< n 0) nil `(let* [~b ~n] (if (= ~n 0) ~expr (do ~expr (dotimes [~b ~n] ~expr))))))))
user> (dotimes [x 5] (do (prn :x :is x) (+ 10 x)))
:x :is 4
:x :is 3
:x :is 2
:x :is 1
:x :is 0
10

Anyways, sounds like you've got what you needed. I'm going to close the issue now.

@kanaka kanaka closed this as completed Oct 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants