|
Quine
Sept 12, 2016 20:37:41 GMT 1
Post by Pjot on Sept 12, 2016 20:37:41 GMT 1
All, Over the years, I have tried to create Quines for fun. A Quine program is a program which can reproduce its own source code. The challenge is to create the smallest Quine possible. There are two rules to comply to: - A valid Quine program can not be empty
- A valid Quine program is not allowed to open its source file and print the contents
This challenge is harder than one would think! My first home-brewn Quine looked like this: FOR i = 1 TO 7 READ src$ p$ = CONCAT$(p$, src$, NL$) q$ = CONCAT$(q$, CHR$(68), CHR$(65), CHR$(84), CHR$(65), CHR$(32), CHR$(34), src$, CHR$(34), NL$) NEXT PRINT p$, CHOP$(q$) DATA "' A simple Quine program - http://en.wikipedia.org/wiki/Quine_(computing) - PvE 2010 - GPL" DATA "FOR i = 1 TO 7" DATA "READ src$" DATA "p$ = CONCAT$(p$, src$, NL$)" DATA "q$ = CONCAT$(q$, CHR$(68), CHR$(65), CHR$(84), CHR$(65), CHR$(32), CHR$(34), src$, CHR$(34), NL$)" DATA "NEXT" DATA "PRINT p$, CHOP$(q$)"
It contains 495 bytes. Pretty big isn't it! After 4 more attempts, using the feature of '?' as 'PRINT', my smallest Quine now is: s$="s$=%c%s%c:?34,s$,34 FORMAT s$":?34,s$,34 FORMAT s$
This Quine program contains 55 bytes. Anyone to create a smaller Quine? Regards Peter
|
|
|
Quine
Sept 14, 2016 21:22:46 GMT 1
Post by Pjot on Sept 14, 2016 21:22:46 GMT 1
Nobody? Actually, I did make an attempt, but could not beat the 55 bytes until now. While hacking around, I thought it could come handy to have a reserved variable containing the source code of the current program anyway. For example 'SOURCE$'. During runtime, it returns the source code of the program being executed. This would allow runtime changes to source code and re-conversion/compilation on the fly. Very convenient when prototyping, for example. No need to worry for commercial applications; the source code only would be stored in the resulting binary when the variable 'SOURCE$' actually is being used in the program. Any ideas or remarks? Regards Peter
|
|
|
Quine
Sept 15, 2016 9:03:17 GMT 1
Post by vovchik on Sept 15, 2016 9:03:17 GMT 1
Dear Peter, I like the idea of SOURCE$, and I am certain that we will find clever uses for it. With kind regards, vovchik
|
|
|
Quine
Sept 15, 2016 14:57:05 GMT 1
Post by Pjot on Sept 15, 2016 14:57:05 GMT 1
Hi vovchik, It is available in the latest beta. The variable name is 'SOURCE$' as mentioned. Only when this variable name occurs somewhere in the code, the converter will add the source code as an array of unsigned integers to the resulting binary. If the variable 'SOURCE$' does not occur, no source code is available in the resulting binary. The following program would be a very small Quine, I don't think we can get any smaller than this ?SOURCE$
But this is not the reason to implement it. As with interpreters, it would be pretty cool to do some ' meta-programming'. This allows a program to program itself. Technically, for BaCon being a code converter, there are quite a few possibilities to implement statements and functions allowing us to do so. One reflexive function is already there (this is TYPEOF$). And now we also have access to SOURCE$. Other suggestions are much appreciated! Best regards Peter
|
|
|
Quine
Sept 16, 2016 8:52:15 GMT 1
Post by vovchik on Sept 16, 2016 8:52:15 GMT 1
Dear Peter, That is pretty cool! I like it and it will open up some nice possibilities. This is the output of a run... Chicken Lobster ' ------------------ FUNCTION INBETWEEN$(STRING string$, STRING leftmarker$, STRING rightmarker$) ' ------------------ LOCAL myresult$ TYPE STRING LOCAL rm, lm TYPE int ' , x IF LEN(string$) > 0 THEN lm = INSTR(string$, leftmarker$) + LEN(leftmarker$) IF lm THEN rm = INSTR(string$, rightmarker$, lm + 1) IF rm > 0 THEN 'x = LEN(string$) myresult$ = MID$(string$, lm, rm - lm) RETURN myresult$ ELSE RETURN "" END IF ELSE RETURN "" END IF ELSE RETURN "" END IF END FUNCTION
data$ = "<product price=3>Chicken</product> <product price=11.50>Lobster</product>" PRINT INBETWEEN$(data$, "price=3>", "</") PRINT INBETWEEN$(data$, "price=11.50>", "</") PRINT SOURCE$
With kind regards, vovchik
|
|
|
Quine
Sept 16, 2016 15:50:02 GMT 1
Post by Pjot on Sept 16, 2016 15:50:02 GMT 1
Thanks vovchik, I also think it has lot of potential, a lot can be done with reflexive statements. In the meantime I also have added the LINENO variable which contains the current line number in the source code. The actual value of LINENO changes at each next line, of course Latest beta here. Regards Peter
|
|
|
Quine
Sept 16, 2016 21:05:45 GMT 1
Post by basica on Sept 16, 2016 21:05:45 GMT 1
Peter, that's an interesting idea for Bacon. I haven't tried your update yet and perhaps a simple command line "filename --source" which would then output the "filename.bac" source code might be useful.. Eliminates the need to include the "filename.bac" when making a tar package. I also think you have some more elaborate plans in mind here. Best regards, basica
|
|
|
Quine
Sept 16, 2016 21:23:45 GMT 1
Post by Pjot on Sept 16, 2016 21:23:45 GMT 1
Hi basica, I sure have, the metaprogramming stuff is really fascinating! One strategy to achieve metaprogramming, e.g. the ability to modify itself while running, is by using reflection. In BaCon we can achieve reflection as follows. PRAGMA LDFLAGS -rdynamic : ' Functions should be visible
SUB Hello_12(int x) : ' Raise to the power of 2 PRINT POW(x, 2) END SUB
SUB Hello_normal(int x) : ' Raise to the power of 3 PRINT POW(x, 3) END SUB
IF LINENO = 11 THEN : ' Are we at line 11 in our source? func$ = "Hello_" & STR$(LINENO) : ' If so we want to invoke Hello_12 ELSE func$ = "Hello_normal" : ' Else we would like to invoke Hello_normal ENDIF
IMPORT func$ FROM NULL TYPE void ALIAS "func" : ' Get the handle to this function
func(5) : ' Use a generic symbol to call either one
As demonstrated, the name of the function we would like to call can be constructed as a string. Once done, we can actually invoke the function with that name using a generic symbol. This way we can even create 'objects' or implement overloading. An IMPORT from NULL (self) already was possible, however, using a string for function name revealed a bug which is fixed in the latest beta. Regards Peter
|
|
|
Quine
Sept 16, 2016 21:39:01 GMT 1
Post by basica on Sept 16, 2016 21:39:01 GMT 1
Peter, Thanks for explaining. I'm going to have to do some extensive studying to catch up Thanks again, and best regards, basica
|
|
|
Quine
Sept 20, 2016 11:14:40 GMT 1
Post by Pjot on Sept 20, 2016 11:14:40 GMT 1
Usually, languages with reflexive features have the ability to create and evaluate code during runtime. For a compiled language this is a hard thing to do. I have looked into the possibility of embedding PicoC. This interpreter may work for regular numeric functions, however, the heavily optimized BaCon string functions require inclusion of all additional C stuff in the BaCon static library as well, next to the initialization of internal arrays and pointers for housekeeping. So below a different approach, where the EVAL function simply creates a shared object during runtime from where the main function is imported and invoked. It demonstrates the use of expressions, but as an extension (to be added), this approach will work for complete BaCon code parts as well. The code below uses the newly added 'CLOSE LIBRARY', therefore, grab the latest beta first before testing Regards Peter ' ' Simple EVAL wrapper to evaluate numeric and string functions during runtime. ' ' Requires the presence of the 'bacon' binary on your system. ' ' September 2016, PvE - MIT License. '-----------------------------------------------------------------------------
CONST EVAL_lib$ = "/tmp/eval_lib.bac" CONST EVAL_dso$ = "/tmp/eval_lib.so"
'-----------------------------------------------------------------------------
SUB EVAL_NUMERIC(code$)
LOCAL tmp_eval TYPE FILE* LOCAL err$
CLOSE LIBRARY EVAL_dso$
OPEN EVAL_lib$ FOR WRITING AS tmp_eval WRITELN "FUNCTION eval#():RETURN " & code$ & ":ENDFUNCTION" TO tmp_eval CLOSE FILE tmp_eval
err$ = EXEC$("bacon -f " & EVAL_lib$, NULL, 2)
IF LEN(err$) THEN PRINT "ERROR: Could not evaluate the string '" & code$ & "': " & err$ END 1 ELSE IMPORT "eval__b2c__float_var" FROM EVAL_dso$ TYPE double ALIAS eval# ENDIF
END SUB
'-----------------------------------------------------------------------------
SUB EVAL_STRING$(code$)
LOCAL tmp_eval TYPE FILE* LOCAL err$
CLOSE LIBRARY EVAL_dso$
OPEN EVAL_lib$ FOR WRITING AS tmp_eval WRITELN "FUNCTION eval$():RETURN " & code$ & ":ENDFUNCTION" TO tmp_eval CLOSE FILE tmp_eval
err$ = EXEC$("bacon -f " & EVAL_lib$, NULL, 2)
IF LEN(err$) THEN PRINT "ERROR: Could not evaluate the string '" & code$ & "': " & err$ END 1 ELSE IMPORT "eval__b2c__string_var" FROM EVAL_dso$ TYPE STRING ALIAS eval$ ENDIF
END SUB
'-----------------------------------------------------------------------------
SUB EVAL_CLEANUP
CLOSE LIBRARY EVAL_dso$
IF FILEEXISTS(EVAL_lib$) THEN DELETE FILE EVAL_lib$ IF FILEEXISTS(EVAL_dso$) THEN DELETE FILE EVAL_dso$
END SUB
'-----------------------------------------------------------------------------
FUNCTION EVAL(code$)
CALL EVAL_NUMERIC(code$)
RETURN eval#()
END FUNCTION
'-----------------------------------------------------------------------------
FUNCTION EVAL$(code$)
CALL EVAL_STRING$(code$)
RETURN eval$()
END FUNCTION
'-----------------------------------------------------------------------------
PRINT EVAL("5 * 3") PRINT EVAL("4 * 2 + SIN(RAD(90))")
PRINT EVAL$("MID$(\"Hello\" & \"world\", 5, 3)") PRINT EVAL$("\"This is a \" & CHR$(65)")
|
|
|
Quine
Oct 13, 2016 3:15:04 GMT 1
Post by alexfish on Oct 13, 2016 3:15:04 GMT 1
Hi Peter
Interesting ,
a while ago whilst looking at a way around coding SVG syntax without the Quotes "" IE one could copy & paste SVG examples into a code block. Hence tested this macro for making a string
# demo MAKESTRING #include <stdio.h> #include <string.h>
#define MAKESTRING( x ) #x
int main() {
char * str = MAKESTRING(make `a string like` "hello world"); puts(str); str = MAKESTRING(FOR t = 1 TO 100); puts(str); return(0); }
Not sure if it could be used in Quine Tried this in BaCon but failed.
either way could be a Nice Feature
BR Alex
|
|
|
Quine
Oct 13, 2016 20:05:32 GMT 1
Post by Pjot on Oct 13, 2016 20:05:32 GMT 1
Hi Alex, If you would omit the 'main()' function and embed your code in the USEC/ENDUSEC statements, it actually works: USEC
#include <stdio.h> #include <string.h>
#define MAKESTRING( x ) #x
char * str = MAKESTRING(make `a string like` "hello world"); puts(str); str = MAKESTRING(FOR t = 1 TO 100); puts(str);
ENDUSEC
PRINT SOURCE$
Best regards Peter
|
|
|
Quine
Oct 13, 2016 22:10:32 GMT 1
Post by alexfish on Oct 13, 2016 22:10:32 GMT 1
Thanks Peter
result on the Raspi
BR Alex
a bit off topic :: a svg example
USEC
#include <stdio.h> #include <string.h>
#define MAKESTRING( x ) #x
char * str = MAKESTRING(make `a string like` "hello world"); puts(str); str = MAKESTRING( <svg width="1000" height="1000" > <rect x="0" y="0" rx="60" ry="60" width="1000" height="1000" stroke = "none" style="fill:blue;stroke:none;stroke-width:1;opacity:1.0" /> // text <text text-anchor="middle" x="500" y="500" dy= "35" font-size="70" fill="yellow" >'BaCon'@(Pi)</text > <text text-anchor="middle" x="500" y="70" dy= "35" font-size="70" stroke="white" fill="none" >You are a Winner</text > <text text-anchor="middle" x="500" y="930" dy= "35" font-size="70" fill="yellow" > GL Meets SVG</text > // lines <line x1="0" y1="500" x2="1000" y2="500" style="stroke:rgb(255,0,0);stroke-width:1" /> <line x1="500" y1="0" x2="500" y2="1000" style="stroke:rgb(255,255,0);stroke-width:1" /> // Circles <circle cx="500" cy="300" r="300" stroke="white" stroke-width="1" fill="none" /> <circle cx="500" cy="500" r="500" stroke="white" stroke-width="1" fill="none" />
<svg width="100%" height="100%" x = "50" y = "50" > <circle cx="90" cy="90" r="90" stroke="red" stroke-width="2" fill="gold" /> </svg> </svg>); puts(str);
ENDUSEC
PRINT SOURCE$
|
|
|
Quine
Apr 27, 2017 20:09:54 GMT 1
Post by Pjot on Apr 27, 2017 20:09:54 GMT 1
By the way, for mathematical runtime evaluation BaCon can use libmatheval also. This library can compute expressions which contain variables as well. Below an example program. BR Peter IMPORT "evaluator_create(char*)" FROM "libmatheval.so.1" TYPE void* IMPORT "evaluator_evaluate(void*, int, char**, double*)" FROM "libmatheval.so.1" TYPE double IMPORT "evaluator_destroy(void*)" FROM "libmatheval.so.1" TYPE void
'------------------------------------------------------------------------
DECLARE f TYPE void*
'------------------------------------------------------------------------
expression$ = "5*(3+1)" ' An expression without variables
f = evaluator_create(expression$)
PRINT evaluator_evaluate(f, 0, NULL, NULL)
evaluator_destroy(f)
'------------------------------------------------------------------------
DECLARE var$[] = { "x" } ' The expression uses a variable called x
DECLARE nr[] = { 0.1 } TYPE double ' The variable has value 0.1
expression$ = "6+x*50" ' The expression to be calculated
f = evaluator_create(expression$)
PRINT evaluator_evaluate(f, 1, var$, nr)
evaluator_destroy(f)
|
|
|
Quine
May 2, 2017 21:16:21 GMT 1
Post by btiffin on May 2, 2017 21:16:21 GMT 1
Needs a semicolon, otherwise you get a stray newline and the quine isn't as quine-ey. $SOURCE; About to post to Rosetta Code. With the semi-colon it works with a trailing newline in the source or without. rosettacode.org/wiki/Quine#BaConSweet. Always a nice tool to wield, Peter. One possible pester. When logging the tectonics, it would be cleaner if -q caused no output (at all) unless there are problems. Many compilers only display when asked or if there are issues; it's a fairly powerful visual clue. Maybe a -qq to still allow control over the line counter mode toggled with -q and no output at all with -qq. Or for single letters, -q for quiet and -s for silent? Cheers, Brian
|
|