10
\$\begingroup\$

This question already has an answer here:

Write a program that:

  • if a compiled language is used, compiles without errors
  • if an interpreted language is used, runs without syntax errors

for every major release of your language, and produces different output for every major release under which it is compiled/run. (It may produce the same or different output for versions within the same major release, as long as it varies among major releases.) You may not output or use any part of the language version in your program.

A "major release" of a language is defined to be a publicly available release that added to the major version number. For example, C# has had 6 major releases, C has had 3 (K&R is not a numbered version and does not contribute to the count), and Ruby has also had 3 (due to version 0.95). What number constitutes the major version number can change over time (Java has had 8 major releases).

Any language used in this challenge must have:

  • At least three major releases, and
  • A dedicated Wikipedia article.
\$\endgroup\$

marked as duplicate by Rainbolt, Community Apr 17 '15 at 16:12

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 7
    \$\begingroup\$ %put &sysver; That will be valid and produce different output in every major release of SAS. Might want to disallow version printing. \$\endgroup\$ – Alex A. Apr 15 '15 at 22:48
  • 1
    \$\begingroup\$ Two questions: 1) Does "major release" have to be the earliest version that added to the major version number? For example, for Python 2 does it have to be Python 2.0.0, Python 2.0.1 (earliest on the releases page) or is something like Python 2.2.0 or 2.7.8 fine? 2) What happens if we can't get hold of very old interpreters/compilers or they don't work on our computers? \$\endgroup\$ – Sp3000 Apr 16 '15 at 3:46
  • 3
    \$\begingroup\$ The J language does not support or distribute versions older than J5, and even if you tried you would be extremely hard-pressed to find anything more than documentation for J4, and nothing for anything older. Further, J8 was a major release, but nothing in the engine changed, only in the IDE and window libraries. Which major releases of J (1 <= n <= 8) must be supported? \$\endgroup\$ – algorithmshark Apr 16 '15 at 5:26
  • 4
    \$\begingroup\$ This question is basically a less well specified generalisation of this one. \$\endgroup\$ – Peter Taylor Apr 16 '15 at 7:42
  • 2
    \$\begingroup\$ The "for all major release" part makes the challenge harder for languages with many major versions. It also increases the work needed to set up the environment for testing. \$\endgroup\$ – n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ Apr 16 '15 at 9:49
40
\$\begingroup\$

Python

if 2 + 2 is 4:
    # Lies must be abolished

    try:
        fact = '2 + 2 = %r' % 5
        exec('True = False')
        exec('assert (2 + 2 == 5) is True')
    except ValueError:
        print('War is peace.')    
    except NameError:
        print('Freedom is slavery.')
    except SyntaxError:
        print('Ignorance is strength.')

else:
    # All is good
    print('Big Brother is watching you.')

(Edit: Had to make a change because string formatting was actually introduced in... 0.9.9)

Tested on the following versions:

  • 0.9.1: Prints Big Brother is watching you. because 2 + 2 is 5.*

  • 1.5: Prints War is peace. since %r string formatting is unavailable, raising a ValueError,

  • 2.1: Prints Freedom is slavery. since True is not defined, raising a NameError,

  • 3.4: Prints Ignorance is strength. since True cannot be assigned to, raising a SyntaxError.

As a bonus, Python 2.3 to 2.7 gives no output, since the assertion passes.

Note that there are four spaces on both "blank" lines, and the file also needs to have a trailing newline.

* is checks object equality, and only from 1.0.0 onwards were small integers shared. You can still see the effect today by trying larger numbers, e.g. 999 + 1 is 1000 gives False.

\$\endgroup\$
  • \$\begingroup\$ "Lies must be abolished"... Why can't all program comments be like that? \$\endgroup\$ – ASCIIThenANSI Apr 17 '15 at 14:22
  • 1
    \$\begingroup\$ @ASCIIThenANSI: Take a look at Vigil. Automatic lie abolisher. \$\endgroup\$ – Alex A. Apr 17 '15 at 18:00
10
\$\begingroup\$

Python

Typical sunday afternoon in a house of three brothers.

try:    # The night before the exam.
        assert(2/5 < 3/5) # Math is hard!
        print("Be quiet!")
except: # Options: 1. Play. 2. Do nothing.
        try: print(__import__("this").s)
        except: "Too afraid to snap back."

Output

$ # Older brother:
$ python3.4 123.py
Be quiet!
$
$ # Much younger brother:
$ ./python1 123.py
$
$ # Slightly younger brother:
$ python2.7 123.py 
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!

How it works

try:
        assert(2/5 < 3/5)

        # In Python 3, '2/5' and '3/5' are floats.
        # In lower versions, they're ints and the assertion fails.

        print("Be quiet!")

        # Python 3 prints this and exits.
        # Lower versions already caught an exception.
except:
        try: print(__import__("this").s)

        # Try importing 'this' (an easter egg which prints the Zen of
        # Python) and printing 'this.s' (same old Zen with ROT13 obfuscation).
        # The easter egg doesn't exist in Python 1 and an exception is raised.

        except: "Too afraid to snap back."

        # Bare string. Would be printed in REPL; does nothing in a program.
\$\endgroup\$
3
\$\begingroup\$

This C++03 C++11 and C++14 program prints old, medium or new: (C++03 is a set of technical corrections to C++98: compilers consider the two to be synonymous)

#define M(x, ...) __VA_ARGS__

int a[] = { M(1'2, 3'4, 5, 0) };
const char *b[]={ 0, "is", "the", "best", "always", "new" };
struct foo { ~foo() { throw std::string("old"); } };

void done() {
    std::cout << "medium" << std::endl;
    std::exit(0);
}
int main() {
  if (b[a[1]]) {
    std::cout << b[a[1]] << std::endl;
  } else {
    std::set_terminate(done); 
    try { foo test; }
    catch( std::string c) {
      std::cout << c.c_str() << std::endl;
    }
  }
}

live example

There are a few ways to do this; I figured out ways that current versions of clang and gcc support when you tell it to compile in a particular version mode.

It relies on a few things.

To distinguish C++14 from C++11/C++03, it uses ' as an internal delimiter in integer constants. Then I discard a leading token, which either contains a , or not depending on the C++ version. The 2nd element of the array is now either 0 or 5. I use this to look up in another array -- entry 0 is null, and entry 5 is "new". If it is non-null, I print it and am finished (for C++14).

To distinguish C++03 from C++11, it throws from a destructor. In C++11, all destructors are implicitly noexcept, which calls the std::set_terminate handler.

I install a terminate handler that prints out "medium" (for C++11), and in the catch block I print "old" (for C++03).

Breaking changes stolen from here for C++11 and here for C++14.

\$\endgroup\$
  • 1
    \$\begingroup\$ Invalid not because it relies on version numbers, but because it doesn't include C++98. \$\endgroup\$ – EMBLEM Apr 16 '15 at 20:54
  • \$\begingroup\$ @emb C++03 was a bunch of bug fixes on C++98, not a major revision (according to the original language author, and me). The standard library was extended if I recall correctly, so I guess I could do overload resolution based checking, if ya really want it. \$\endgroup\$ – Yakk Apr 16 '15 at 21:11
  • 1
    \$\begingroup\$ @emb more evidence: gcc.gnu.org/onlinedocs/gcc/Standards.html gcc uses the same settings for -std=c++98 as c++03, and ditto for clang.llvm.org/cxx_status.html clang. Both consider C++03 to be a technical correction to C++98. While wikipedia claims there where library changes, none seem in evidence at this point. \$\endgroup\$ – Yakk Apr 16 '15 at 22:47
2
\$\begingroup\$

C

Golfed, for the hell of it.

O;main(o){o=((O='\o'),O);}

It does nothing.

Fortunately.
I didn't find anything relevant, so this is just about playing with undefined behaviors again.


C89

§3.1.3.4 7 — Character constants

In addition, certain nongraphic characters are representable by escape sequences consisting of the backslash \ followed by a lower-case letter: \a , \b , \f , \n , \r , \t , and \v . If any other escape sequence is encountered, the behavior is undefined.

Yup. '\o' implies an undefined behavior in C89, while it is only implemention-defined in C99 and C11 (it gives me 'o').


C99

This is where it gets tricky.

§6.5.16 4 — Assignment operators

(...) If an attempt is made to modify the result of an assignment operator or to access it after the next sequence point, the behavior is undefined.

This is a weird wording specific to C99.

A sequence point is needed after (O='\o'): , do the trick.
Since it is necessary to access the result (i.e. "to read or modify the value of an object" as defined by the standard, whose identifier is O) to store it in o, the behavior is undefined.


C11

All of that does not apply in C11.
It should work fine while compiling in C11.
Nobody uses C11.

\$\endgroup\$
  • \$\begingroup\$ @ColeJohnson I believe the downvote was for a previous revision. Having said that though, I think the code needs to actually output something, as per the question... (interesting finds though) \$\endgroup\$ – Sp3000 Apr 17 '15 at 6:31
  • \$\begingroup\$ @Sp3000 Find a compiler filling those undefined behaviors with pretty outputs. Having said that, #include <stdio.h> main(o){gets(o);} produces different warnings with gcc or clang. \$\endgroup\$ – lesmon Apr 17 '15 at 10:04

Not the answer you're looking for? Browse other questions tagged or ask your own question.