Skip to content

353: Add support argv parameter of SPy main function#449

Open
kanin-kearpimy wants to merge 4 commits intospylang:mainfrom
kanin-kearpimy:353-add-support-for-argv-and-exit-code
Open

353: Add support argv parameter of SPy main function#449
kanin-kearpimy wants to merge 4 commits intospylang:mainfrom
kanin-kearpimy:353-add-support-for-argv-and-exit-code

Conversation

@kanin-kearpimy
Copy link
Copy Markdown
Contributor

@kanin-kearpimy kanin-kearpimy commented Apr 2, 2026

Issue: #353

Hi everyone,

this is implementation of backend SPy argv parameter for main function.

I separated argument wrapper to list.c and list.h then calling it in cmodwriter.

However, there is a tricky part happening

image

if any spy files need argument in main like below

def main(argv: list[str]) ...

It will include list.h to such compiled C file to use spy_wrap_argv. However, such implementation is in list.c which requires to compile before such current working file got compile.

Rough correct pipeline.

1) **pre-compile stage**
_list.c (and other built-in standard lib)

2) **current working files**
-> list.c -> [any current working file with `main(argv: list[str])`]

Our current pipeline does compile all standard libraries (e.g. _list.spy) at once. It's challenging to modify current pipeline to receive customized on-the-fly .c to compile as above pipeline. Also, to compile list.c with all libspy is challenging because list.c need _list.c to compiled first.

So, now I juggled by copy content of list.c and compile it with [any current working file with main(argv: list[str])] instead.

Well, I'm not sure I explained it clearly. Please let me know if you need elaboration.

@kanin-kearpimy kanin-kearpimy requested a review from antocuni April 2, 2026 13:13
@kanin-kearpimy kanin-kearpimy force-pushed the 353-add-support-for-argv-and-exit-code branch from cd8d6be to c83e239 Compare April 2, 2026 14:50
Copy link
Copy Markdown
Member

@antocuni antocuni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @kanin-kearpimy .

Congrats to find a way to solve the list[str] bootstrapping problem, I didn't think about it and indeed it's annoying.

I think we can solve it in a much easier way by just emitting the code directly in the generated C instead of trying to have it in a separate file.

See me comment below

returns_i32 = w_main.w_functype.w_restype == B.w_i32

if needs_argv:
self.tbh_includes.wl('#include "spy/list.h"')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here, instead of including spy/list.h, I'd just copy&paste the code for wrapping argv.
Something like this:

self.tbc.wb("""
#define spy_list_str spy__list$list__builtins$str$_ListImpl
#define spy_list_str_new spy__list$list__builtins$str$_ListImpl$__new__
#define spy_list_str_push spy__list$list__builtins$str$_ListImpl$_push

spy_list_str
spy_wrap_argv(int argc, const char *argv[]) {
    ...
}
""")

it's not ideal, but it's just a very short amount of code and it saves lots of complications

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah,

At first I thought of this as well. Let me resolve it this way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, refactored it.

@kanin-kearpimy kanin-kearpimy force-pushed the 353-add-support-for-argv-and-exit-code branch from 542f549 to 09f3c14 Compare April 2, 2026 17:23
Copy link
Copy Markdown
Member

@antocuni antocuni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there seem to be several leftovers from the previous attempt, but overall looks good 👍

}}

needs_argv = len(w_main.w_functype.params) == 1
returns_i32 = w_main.w_functype.w_restype == B.w_i32
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you need this instead of calling vm.typecheck_main as it was before?

returns_i32 = w_main.w_functype.w_restype == B.w_i32

if needs_argv:
self.tbh.wb("""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should go to the tbc, not tbh

Comment on lines +173 to +174
size_t size_str = strlen(argv[i]);
spy_Str *allo = spy_str_alloc(size_str);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
size_t size_str = strlen(argv[i]);
spy_Str *allo = spy_str_alloc(size_str);
size_t length = strlen(argv[i]);
spy_Str *s = spy_str_alloc(length);

I think it's better to call it length for consistency with the field in spy_Str.
Also, allo was a very werid name, better to use s (you need to fix it also below)

""")

if needs_argv and returns_i32:
execution_code = f"""
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
execution_code = f"""
main_src = f"""

a bit of nitpick, but I think this is clearer. After all, it's the "source of the main function".

@@ -0,0 +1,17 @@
#ifndef SPY_LIST_H
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this file is no longer needed, is it?

@@ -0,0 +1,20 @@
#include "spy/list.h"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above, delete it

main_c = self.tmpdir.join("src", "return_exit_code_src.c")
assert main_c.exists()
csrc = main_c.read()
assert "return 99;" in csrc
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I have already deleted/refactored these tests during the review of #419, see e.g. this commit :)
3ebdaa8

There are few code style problems:

  • there is no need for a "cwrite" test for every case. The existing test_cwrite checks that the option work, but we shouldn't test cwrite for every single combination of options.

Also, the rest of the file follows different naming convention, e.g. it always use src = """...""" for the source to compile. Please follow them for consistency.

csrc = main_c.read()
assert "return 99;" in csrc

def test_nested_functions_exit_code_cwrite(self):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was also deleted by 3ebdaa8 . It seems to test that you can call a function, but that's already tested elsewhere.

Please look at that commit and refactor/redelete all the tests which I have already fixed :)


import py.path

import spy.libspy
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not needed?

@antocuni
Copy link
Copy Markdown
Member

antocuni commented Apr 2, 2026

@kanin-kearpimy also, I just merged #445 which (rightly) adds the executable name as the first argv.
Make sure to test the same behavior also for the compiled case, plase 🙏

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

Successfully merging this pull request may close these issues.

2 participants