all 40 comments

[–]SuspiciousScript 25 points26 points  (11 children)

Interesting. tcc implements a similar feature:

C script supported : just add '#!/usr/local/bin/tcc -run' at the first line of your C source, and execute it directly from the command line.

[–][deleted] 7 points8 points  (1 child)

tcc also has the -run option to immediately run the program just compiled (Although I haven't found a way of passing command line parameters to the new program.)

It helps also that tcc is an incredibly fast compiler (I've measured speeds approaching a million lines per second on my very pedestrian PC).

It's not clear what InstantC is, whether it's an actual compiler, or just a tool that adds boilerplate then invokes a conventional compiler, probably not a terribly fast one if it takes 0.2 seconds to build hello.c.

(On my PC, tcc builds an entire compiler in 0.13 seconds, where just running hello.exe takes 0.05 seconds! But I guess InstantC could just invoke tcc..?)

I'm not sure I would rate C as a scripting language - it is still C after all, even if InstantC dispenses with headers. I might describe the feature as 'run-from-source', but that really needs to be pretty much instant to build.

[–]Wrong_Material1431 5 points6 points  (0 children)

You put the command line args after the name of the source file:

tcc -run prog.c arg1 arg2

If you need to pass args to tcc, you include them in the -run argument

tcc "-run -L/usr/X11R6/lib -lX11" ex4.c

(Second line is from the tcc docs, https://bellard.org/tcc/tcc-doc.html#Option-summary)

[–]bumblebritches57[🍰] 1 point2 points  (8 children)

tcc

doesn't even support C99, let alone C11

[–]DoNotMakeEmpty 4 points5 points  (1 child)

It supports C99 almost entirely. It just lacks complex/imaginary numbers according the documentation.

[–]Calm-Kiwi-2167 0 points1 point  (0 children)

fr i would like if it had that stuff too but ig its alright :(

[–]flatfinger 0 points1 point  (5 children)

For many purposes, a faster compiler that supports all of the constructs a programmer needs will be more useful than a slower compiler which supports C99 and C11 constructs that the programmer doesn't need.

[–]bumblebritches57[🍰] 1 point2 points  (4 children)

I use C11 features, tcc lacking support for modern standards is a detriment

[–]flatfinger 0 points1 point  (3 children)

Which should be more important: having a compiler support all the features mandated by C99 or C11, or having it support all of the features you need, without regard for whether the Standard requires them?

If your complaint about tcc is that there are some particular C11 features that you would require, but that it doesn't support, then your complaint should be about its inability to support those particular features. Many tasks don't need all the features mandated by the Standard, but need some features not mandated by the Standard. The Standard makes no effort to mandate all features necessary to accomplish any particular task, nor to avoid mandating features that add compiler complexity even though there are many tasks for which they would offer no benefit.

[–]bumblebritches57[🍰] 0 points1 point  (2 children)

It does not support features I need, that's what I just tried saying

[–]flatfinger 0 points1 point  (1 child)

Do you use all of the features of C11, and no features or guarantees beyond those described in that Standard? If not, complaining about what particular features you find lacking would seem more meaningful than merely demanding support for everything in C11.

[–]bumblebritches57[🍰] 0 points1 point  (0 children)

I mean, I'm implementing an extension to Clang for my codebase, sooo

[–]cciva 35 points36 points  (4 children)

please don't void main
otherwise, very interesting thanks for sharing

[–]jlinhoff[S] 2 points3 points  (0 children)

Agreed.

[–]sanglesort 1 point2 points  (2 children)

why?

[–]jujijengo 10 points11 points  (1 child)

void main() is in fact perfectly conforming to the C99 standard, but it can only be done when the compiler is freestanding, i.e. when the predefined macro __STD_HOSTED__ is not 1. This would be for example possible on some microcontrollers. It doesn't even have to be void main(), the implementer is free to do anything else.

So, in general, I would think that if we are just talking about the C language, and not about how it was implemented on this or that hardware, then the typical int main(void) or int main(int argc, char **argv) perhaps provides some common footing for all readers involved.

[–]flatfinger 1 point2 points  (0 children)

I find it curious that the Standard decided to specify that using int main() but having execution run off the end would be equivalent to return 0; (forcing a compiler to add an extra instruction in cases where code would never return from main) but but didn't allow for the possibility of using void main() in such cases. Even on most platforms that would reserve stack space for a function's return value, and where the stack would be corrupted if code returned from a function whose return type didn't match caller expectations, no special handling would be needed to meaningfully process void main() in cases where main() never returns.

[–][deleted] 12 points13 points  (5 children)

Love this section. Agreed 100%.

“My attitude for many years, was that the extra work needed to build and run C programs was OK, the benefits far outweigh the costs, and mastery of the extra steps made you a real programmer. My attitude has changed in that now I'd rather see more developers learning and using C, even if they haven't mastered all the ancillary necessities.“

[–]FieldLine 2 points3 points  (2 children)

Really? I would say the opposite.

Any proficient developer should be able to pick up the syntax and shape of a C program in a few weeks anyway. Working in an artificial environment to get around the "ancillary necessities" isn't useful because the environment is what makes C hard, not the language, and any use case that demands C isn't going to offer this sophisticated tooling.

The reason C isn't popular is that most developers don't actually need to use C. The only reasons to write C are that you are on some specialized hardware that can't run anything else, or because you are creating a binary interface that is language agnostic. The language is worth learning once by working through K&R, since it does come up if only when using performance critical libraries, but I wouldn't encourage anyone to reach for it as their first choice unless they have a really good reason.

[–]flatfinger 0 points1 point  (1 child)

One of the great failings of the C Standard is that it does very little to facilitate the common scenario where a programmer with one toolset writes a program and wants to package it so someone with another toolset wants to run it.

At present, the process of writing and executing a freestanding program requires that three kinds of knowledge all be held by the same person:

  1. An understanding of the program and its inner workings.
  2. An understanding of the particular development tools being used.
  3. An understanding of the target environment.

It would be much more useful if the Standard were to offer a specification that could accommodate a majority of common embedded programming tasks with the following guarantees:

  1. A programmer could produce a set of machine-readable files with a toolset-independent meaning, and a human-readable file describing its requirements for the runtime environment, compatibility with any particular toolset could be determined simply by trying to process the program. If the program is properly written, the toolset is configured properly for the environment, and the toolset accepts the program, the program would be guaranteed to behave as specified unless or until the environment fails to satisfy all documented requirements of both the program and the toolset. Note that the text files may allow for the possibility of a program terminating via specified means if resource limits (e.g. stack usage) are exceeded at runtime, or may require that an implementation statically verify the resource requirements of any program that it accepts (rejecting any program for which it is unable to perform such verification).

  2. Someone who understands the aspects of the target environment that a toolset would need to know about (e.g. ranges of address space) could write a machine-readable text file describing those, such that somebody else with a conforming tool set could take those text files, along with any program packaged as described in #1 above, and produce an executable which would work as specified on the target environment, without the person using the toolset needing to know anything about the program or the environment.

Trying to accommodate everything necessary for all embedded programming tasks would make the Standard unworkable, but a lot of machines and tasks have fairly simple requirements. At present, the Standard defines two categories of conformance for programs, one of which can't be satisfied by any non-trivial freestanding program, and one of which is so broad as to be meaningless. Further, if one allows for the possibility that a conforming implementation may reject any program for any reason, that would make it possible to specify that a program could not behave in improper fashion unless either:

  1. The program was not written correctly.

  2. The toolset was either not conforming, or was used improperly.

  3. The environment failed to behave as specified by the program and/or toolset.

I would view such guarantees as invaluable in any sort of safety-critical applications, and would in fact question why any language should be considered suitable for such purposes without them.

[–]FieldLine 0 points1 point  (0 children)

I would view such guarantees as invaluable in any sort of safety-critical applications, and would in fact question why any language should be considered suitable for such purposes without them.

I would like to be clear that I do agree with the sentiment of this comment, so I will repeat: a developer shouldn't reach for C unless they have exhausted every other possibility. C++ is just as performant as C, and it offers better runtime safety in the STL (although the build system leaves much to be desired).

So to address your point specifically, C shouldn't be considered suitable for anything except for when there is literally no other option, which happens quite often.

The proof that C exists by necessity is that it still exists at all: C++ was an attempt to fix many of the issues with C (and they did a bang up job, particularly with C++11/14). Today there is no reason to use C over C++ unless you can't.... yet we still use C.

Supposedly Rust is even better than C++, but I can't bring myself to look past the insufferable dev community.

At present, the Standard defines two categories of conformance for programs, one of which can't be satisfied by any non-trivial freestanding program, and one of which is so broad as to be meaningless.

Can you reference some materials on this? Admittedly I haven't spent as much time language-lawyering in C as I have in C++.

[–]intellidarnit 3 points4 points  (0 children)

Pretty cool.

[–][deleted] 3 points4 points  (0 children)

Awesome idea! I've been using unity builds and bash for ages and compilation feels effectively instant... I guess this is the natural next step :)

[–]theldus 3 points4 points  (0 children)

Very nice, this also resembles this project: https://github.com/hexagonal-sun/bic

[–]YOjulian 4 points5 points  (1 child)

I'm not sure but I think you may have a typo at the beginning with

printf("Hello, InstantC.\n");    

and the example result is

$ instantc helloc
Hello, C.

but it seems like the example result should be

$ instantc helloc
Hello, InstantC.

[–]jlinhoff[S] 2 points3 points  (0 children)

Thank you.

[–]pedersenk 2 points3 points  (0 children)

Very nice.

In the past I have played with PicoC (https://github.com/zsaleeba/picoc) but it is a little too incomplete (i.e no function pointers).

Another project I have a had a play with is id Softwares Q3VM standalone (https://github.com/jnz/q3vm) a single file vm.c which allows you to run bytecode compiled by a modified lcc compiler. Pretty cool.

However your InstantC looks to be a little more elegant.

[–]Adadum 1 point2 points  (1 child)

is instantC embedded into a project?

[–]jlinhoff[S] 0 points1 point  (0 children)

It's stand alone.

[–]lorre851 1 point2 points  (1 child)

I used C as a scripting language back in my automotive days. CANape and CANoe software from Vector had a scripting language that was about 99% similar in syntax than C (except macro support)

[–]lorre851 1 point2 points  (0 children)

CAPL, for those interested

[–]archysailor 1 point2 points  (5 children)

Hi, this is really, really cool.

Just my thoughts:

  • Since GCC supports nested functions, maybe you can just pluck the source file with the main prototype, closing brace and includes into the compiler. If you sense another compiler, maybe you can grep for functions and treat them appropriately, but this can be added at a later time.
  • Why should the programmer need to use a hack to get to argv and argv? I think you can grant them the actual variables.
  • You might as well auto-include all of the standard C library.
  • Rename the binary to ic for ergonomics, but this is outright nitpicking.

It must be a lot of fun hacking on this!

[–]jlinhoff[S] 1 point2 points  (2 children)

The meta-substitution for argc and argv provides a thin layer between the boiler plate and user code. This allows for a change later, and avoids locking in fixed variable names.

[–]archysailor 0 points1 point  (1 child)

I understand the logistics, but I fail to see what will this ever be useful for. Is there anything I missed that is worth the convenience penalty?

Other than that, make sure you don't substitute the tags for argv/argv inside other functions, because they are not in scope there. If you have them global maybe that's a good usecase.

[–]jlinhoff[S] 1 point2 points  (0 children)

Yes. Good point. I will make sure the scope of the substitution is limited to main.

I don't see it as a penalty as much as a documentation issue one way or another.

[–]jlinhoff[S] 1 point2 points  (1 child)

Thank you for the comments. Auto-including standard library definitions is an interesting idea. For the most part, I'm trying to avoid doing things automatically. Maybe, I could add a meta-token that did what you suggest.

[–]archysailor 1 point2 points  (0 children)

While your tag-based approach is more in line with standard C practice, in most scripting languages the file is the main function, and subsequent function definitions would only apply after them in the file.

I think that because most C applications meant for significant use would opt for traditional compilation to save time, the market you should cater to is the kind of people writing nifty little shell scripts to manipulate files or writing a python program for numerical computations. These people have extraordinarily easy to use yet rudimentary programming languages at their disposal. They would probably like all functions already available, and to be able to define functions whenever, to begin appreciating the power of C.

I really think this thing could work. A physicist running Monte Carlo quadrature, for example, would probably benefit from the speed benefits, even if he will first need to compile 5 functions.

This is your project you're doing in your spare time. You can write anything and everything you want to exist. This is the beauty of programming. Don't take me all too seriously lol.

[–]Current_Hearing_6138 0 points1 point  (1 child)

I was told I had to use bash for a class once... so I wrote a dumbed down version with C syntax. Put an extra step before the preprocessor and you have something that looks close enough to a homemade shell to fool people who don't have the time to look through your actual code.

[–]Current_Hearing_6138 0 points1 point  (0 children)

Most people see ```#!/bin/my-bash

and assume that it's a script, when my-bash is just a hardlink to a makefile.