From e18e03bff543883e613ecd10deea0c70ca5671a3 Mon Sep 17 00:00:00 2001 From: Landon Curt Noll Date: Fri, 24 Mar 2023 02:57:38 -0700 Subject: [PATCH] fix .gitignore, Makefile and README.md for 2014/endoh Unpack src.zip. --- 2014/endoh1/.gitignore | 2 + 2014/endoh1/Makefile | 289 ++++++++++++++++++++++++++++----------- 2014/endoh1/README.md | 43 +++--- 2014/endoh1/Rakefile | 42 ++++++ 2014/endoh1/alpha.rb | 141 +++++++++++++++++++ 2014/endoh1/assemble.rb | 81 +++++++++++ 2014/endoh1/ast-def.rb | 89 ++++++++++++ 2014/endoh1/gen-prog.rb | 58 ++++++++ 2014/endoh1/gen-table.rb | 54 ++++++++ 2014/endoh1/link.rb | 46 +++++++ 2014/endoh1/machine.rb | 57 ++++++++ 2014/endoh1/parse.rb | 87 ++++++++++++ 2014/endoh1/prog-tmpl.c | 11 ++ 2014/endoh1/read_me.md | 149 ++++++++++++++++++++ 2014/endoh1/register.rb | 83 +++++++++++ 2014/endoh1/serialize.rb | 122 +++++++++++++++++ 2014/endoh1/src.rb | 280 +++++++++++++++++++++++++++++++++++++ 2014/endoh1/src.zip | Bin 18901 -> 0 bytes 2014/endoh1/tool.rb | 49 +++++++ 2014/endoh1/vm.c | 29 ++++ 20 files changed, 1616 insertions(+), 96 deletions(-) create mode 100644 2014/endoh1/Rakefile create mode 100644 2014/endoh1/alpha.rb create mode 100644 2014/endoh1/assemble.rb create mode 100644 2014/endoh1/ast-def.rb create mode 100644 2014/endoh1/gen-prog.rb create mode 100644 2014/endoh1/gen-table.rb create mode 100644 2014/endoh1/link.rb create mode 100644 2014/endoh1/machine.rb create mode 100644 2014/endoh1/parse.rb create mode 100644 2014/endoh1/prog-tmpl.c create mode 100644 2014/endoh1/read_me.md create mode 100644 2014/endoh1/register.rb create mode 100644 2014/endoh1/serialize.rb create mode 100644 2014/endoh1/src.rb delete mode 100644 2014/endoh1/src.zip create mode 100644 2014/endoh1/tool.rb create mode 100644 2014/endoh1/vm.c diff --git a/2014/endoh1/.gitignore b/2014/endoh1/.gitignore index 216deeb88..7ba4a572a 100644 --- a/2014/endoh1/.gitignore +++ b/2014/endoh1/.gitignore @@ -1,3 +1,5 @@ main main.c prog +treacle +treacle.c diff --git a/2014/endoh1/Makefile b/2014/endoh1/Makefile index 33f3a0c45..40561a75a 100644 --- a/2014/endoh1/Makefile +++ b/2014/endoh1/Makefile @@ -1,161 +1,262 @@ #!/usr/bin/env make # -# 2014 makefile +# IOCCC 2014 winning entry - endoh1 + +################################################################################ # -# This work by Landon Curt Noll, Simon Cooper, and Leonid A. Broukhis -# is licensed under: +# Regarding individual entries: +# +# IOCCC winning entry code may not work on your system. Even with standards, +# conditions and build environments change, especially when it comes to more +# extreme code such you might find in an IOCCC winner. Moreover, what was +# allowed and liked in one IOCCC contest may no longer be liked/allowed in +# later IOCCC years. +# +# Corrections on winning author and fixing typos in remarks are VERY WELCOME. +# +# GitHub pull requests are welcome: +# +# If you have a patch that might make running an entry work in modern Unix-like +# environments, please create a GitHub pull request for the IOCCC judges to +# consider. +# +# Suggestions and corrections regarding Makefiles, winning author information, +# fixing typos in remarks, and code patches are welcome via opening a GitHub +# issue or better yet, by creating a GitHub pull request for the IOCCC judges +# to consider. To form a GitHub issue or GitHub pull, please visit: +# +# https://github.com/ioccc-src/winner +# +################################################################################ + +# This work by Landon Curt Noll and Leonid A. Broukhis is licensed under: # # Creative Commons Attribution-ShareAlike 3.0 Unported License. # # See: http://creativecommons.org/licenses/by-sa/3.0/ -################ -# tool locations -################ +############################# +# shell used by this Makefile +############################# # SHELL= bash -CP= cp -CPP= cpp -GUNZIP= gunzip -LD= ld -MAKE= make -RM= rm -SED= sed -TAR= tar -TRUE= true + +####################### +# common tool locations +####################### +# +include ../../var.mk # Set X11_LIBDIR to the directory where the X11 library resides # -X11_LIBDIR= /usr/X11R6/lib +#X11_LIBDIR= /usr/X11R6/lib +#X11_LIBDIR= /usr/X11/lib +X11_LIBDIR= /opt/X11/lib # Set X11_INCLUDEDIR to the directory where the X11 include files reside # -X11_INCDIR= /usr/X11R6/include +#X11_INCDIR= /usr/X11R6/include +#X11_INCDIR= /usr/X11/include +X11_INCDIR= /opt/X11/include -# Compiler warnings +# Common C compiler warnings to silence # -#CWARN= -CWARN= -Wall -#CWARN= -Wall -pedantic +#CSILENCE= +CSILENCE= -Wno-empty-body -# compiler standard +# Common C compiler warning flags +# +#CWARN= ${CSILENCE} +#CWARN= -Wall ${CSILENCE} +#CWARN= -Wall -Wextra ${CSILENCE} +CWARN= -Wall -Wextra -pedantic ${CSILENCE} + +# Compiler standard # #CSTD= #CSTD= -ansi -CSTD= -std=c11 +CSTD= -std=gnu11 -# compiler bit architecture +# Compiler bit architecture # # Some entries require 32-bitness: # ARCH= -m32 # -# Some entries require 64-bitess: +# Some entries require 64-bitness: # ARCH= -m64 # # By default we assume nothing: # ARCH= -# optimization +# Defines that are needed to compile +# +CDEFINE= +#CDEFINE= -DIOCCC + +# Include files that are needed to compile +# +CINCLUDE= +#CINCLUDE= -include stdlib.h +#CINCLUDE= -include stdio.h +#CINCLUDE= -include stdlib.h -include stdio.h +#CINCLUDE= -I ${X11_INCDIR} -I ${X11_INCDIR}/X11 +#CINCLUDE= `${SDL2_CONFIG} --cflags` + +# Optimization # # Most compiles will safely use -O2. Some can use only -O1 or -O. # A few compilers have broken optimizers or this entry make break # under those buggy optimizers and thus you may not want anything. +# Reasonable compilers will allow for -O3. # #OPT= #OPT= -O #OPT= -O1 -OPT= -O2 -#OPT= -O3 +#OPT= -O2 +OPT= -O3 + +# Default flags for ANSI C compilation +# +CFLAGS= ${CSTD} ${CWARN} ${ARCH} ${CDEFINE} ${CINCLUDE} ${OPT} # Libraries needed to build # LIBS= +#LIBS= -lm +#LIBS= -L ${X11_LIBDIR} -lX11 +#LIBS= `${SDL2_CONFIG} --libs` -# default flags for ANSI C compilation -# -CFLAGS= ${CWARN} ${CSTD} ${ARCH} ${OPT} -Wno-empty-body -funsigned-char - -# ANSI compiler -# -# Set CC to the name of your ANSI compiler. -# -# Some entries seem to need gcc. If you have gcc, set -# both CC and MAY_NEED_GCC to gcc. -# -# If you do not have gcc, set CC to the name of your ANSI compiler, and -# set MAY_NEED_GCC to either ${CC} (and hope for the best) or to just : -# to disable such programs. +# C compiler to use # CC= cc #CC=clang -MAY_NEED_GCC= gcc +#CC=gcc + +# Compiler add-ons or replacements for clang only +# +ifeq ($(CC),clang) +# +CSILENCE+= -Wno-comma -Wno-missing-variable-declarations \ + -Wno-poison-system-directories -Wno-shadow +# +CWARN+= -Weverything +# +endif + +# Specific add-ons or replacements for gcc only +# +ifeq ($(CC),gcc) +# +#CSILENCE+= +# +#CWARN+= +# +endif ############################## # Special flags for this entry ############################## # -ENTRY= prog main -DATA= +ENTRY= endoh1 +PROG= prog +# +OBJ= ${PROG}.o +DATA= Rakefile alpha.rb assemble.rb ast-def.rb gen-prog.rb gen-table.rb link.rb \ + machine.rb main.c parse.rb prog-tmpl.c read_me.md register.rb \ + serialize.rb src.rb tool.rb vm.c +TARGET= ${PROG} main treacle +# ALT_OBJ= -ALT_ENTRY= +ALT_TARGET= ################# # build the entry ################# # -all: ${ENTRY} ${DATA} +all: data ${TARGET} @${TRUE} +.PHONY: all alt data everything clean clobber install \ + love haste waste maker easter_egg sandwich \ + supernova deep_magic magic charon pluto + +${PROG}: ${PROG}.c + ${CC} ${CFLAGS} $< -o $@ ${LIBS} + main: main.c ${CC} ${CFLAGS} $< -o $@ ${LIBS} main.c: prog - ./prog > $@ - -prog: prog.c - ${CC} ${CFLAGS} $< -o $@ ${LIBS} + ${RM} -f $@ + ./${PROG} > $@ # alternative executable # -alt: ${ALT_ENTRY} +alt: data ${ALT_TARGET} @${TRUE} +treacle.c: ${PROG} + ${RM} -f $@ + ./${PROG} < ${PROG}.c > $@ + +treacle: treacle.c + ${CC} ${CFLAGS} $< -o $@ ${LIBS} + # data files # data: ${DATA} @${TRUE} +# both all and alt +# +everything: all alt + ############### # utility rules ############### # -everything: all alt - clean: - ${RM} -f ${ENTRY}.o ${ALT_OBJ} main.o + ${RM} -f ${OBJ} ${ALT_OBJ} + ${RM} -f main.o + @-if [ -f indent.c ]; then \ + echo ${RM} -f indent.c; \ + ${RM} -f indent.c; \ + fi clobber: clean - ${RM} -f ${ENTRY} ${ALT_ENTRY} main.c main - -nuke: clobber - @${TRUE} - -dist_clean: nuke - @${TRUE} + ${RM} -f ${TARGET} ${ALT_TARGET} + @-if [ -e sandwich ]; then \ + ${RM} -f sandwich; \ + echo 'ate sandwich'; \ + fi install: - @echo "Surely we are joking Dr. Science!" + @echo "Dr. Spock says that is not logical!" @${TRUE} -# backwards compatibility +# you may be able to misunderstand the source by reading indent.c # -build: all - @${TRUE} +indent.c: ${PROG}.c + @if which "${GINDENT}" >/dev/null 2>&1; then \ + echo ${RM} -f $@; \ + ${RM} -f $@; \ + echo "${GINDENT} < $< > $@"; \ + ${GINDENT} < $< > $@; \ + elif which "${INDENT}" >/dev/null 2>&1; then \ + echo ${RM} -f $@; \ + ${RM} -f $@; \ + echo "${INDENT} < $< > $@"; \ + ${INDENT} < $< > $@; \ + else \ + echo "no intent tool found, ident $< yourself, sorry"; \ + echo "exit 1"; \ + exit 1; \ + fi ################## @@ -174,35 +275,69 @@ waste: @echo 'haste' @${TRUE} +maker: + @echo 'Participate in a maker faire' + @${TRUE} + easter_egg: - @echo you expected to sometimes mis-understand this $${RANDOM} magic - @echo chongo '' "/\\oo/\\" - @echo Readers shall not be disallowed from failong to be unable to not partly misunderstand this partocular final echo. + @echo "you expected to often misunderstand this $${RANDOM} magic" + @echo "chongo '' /\\oo/\\" + @echo "Eggy" + +sandwich: easter_egg + @if [ `id -u` -eq 0 ]; then \ + echo 'Okay.'; \ + echo "$${RANDOM}`date +%s`$${RANDOM}" > $@; \ + else \ + echo 'What? Make it yourself.'; \ + exit 1; \ + fi # Understand the history of the Homebrew Computer Club # as well as the West Coast Computer Faire and # you might be confused different. :-) # -supernova: nuke +supernova: sandwich @-if [ -r .code_anal ]; then \ - ${RM} -f .code_anal_v4; \ + ${RM} -f .code_anal_v6; \ else \ - echo "We are not expected to understand that"; \ + echo "planet deniers, like some members of the IAU, are so cute when they try to defend their logic"; \ fi + @echo "A $@ helps ${MAKE} the elements that help form planets" @${TRUE} -deep_magic: +deep_magic: supernova @-if [ -r .code_anal ]; then \ - ccode_analysis --deep_magic 2ed3582ec5c094a093fea2a6bcb9f19439892873c9cc45ed1ceaafe08b7d52e1b2c7cd524be3b0b2 --FNV1a_hash 512-bit "${ENTRY}"; \ + ccode_analysis --deep_magic 7889066cdbdf09b0408021754586e895be641cc9be29e7eafd3ca65359541f17 \ + --FNV1a_hash_512_bit -v2 "${PROG}" "${PROG}.c"; \ else \ - echo "You are holding it wrong!"; \ + echo "Wrong! Do it again!"; \ + sleep 2; \ + echo "Wrong! Do it again!"; \ + sleep 2; \ + echo "Wrong! Do it again!"; \ fi @${TRUE} magic: deep_magic @-if [ -r .code_anal ]; then \ - ccode_analysis --mode 391581 --level 216193 --FNV1a_hash 512-bit "${ENTRY}"; \ + ccode_analysis --level 20003 --mode 5717175 --FNV1a_hash_512_bit -v2 "${PROG}" "${PROG}.c"; \ else \ - echo "These aren't the primes you're looking for Pauline!"; \ + echo "If you don't eat yer meat, you can't have any pudding!"; \ + echo "How can you have any pudding if you don't eat yer meat?!"; \ fi @${TRUE} + +# The IOCCC resident astronomer states +# +charon: magic + @echo "$@ is a dwarf planet" + @echo "dwarf is a type of planet" + @echo "therefore IAU, $@ is a planet" + +pluto: charon + ${MAKE} charon + @echo "$@ is a dwarf planet" + @echo "dwarf is a type of planet" + @echo "therefore, $@ is a planet" + @echo "having lots of planets is good feature because supernovae can help ${MAKE} a lot of them" diff --git a/2014/endoh1/README.md b/2014/endoh1/README.md index 4ec9c1c4e..917a1360a 100644 --- a/2014/endoh1/README.md +++ b/2014/endoh1/README.md @@ -1,41 +1,47 @@ # Most square (YODA award) -Yusuke Endoh - - + Yusuke Endoh + + +# To build: -## Judges' comments: -### To build: - - make +```sh +make +``` ### To run: - ./prog > foo.c +```sh +./prog > foo.c +``` ### Try: - ./prog > main.c +```sh +./prog > main.c - make main - ./main Hello +make main +./main Hello +``` -### Selected Judges Remarks: +## Judges' comments: Big terminals with tiny fonts have their uses in certain cases. One of them is: - ./prog < prog.c > treacle.c - make treacle - # or instead of make: cc -Wno-empty-body -funsigned-char treacle.c -o treacle - ./treacle +```sh +./prog < prog.c > treacle.c +make treacle +./treacle +``` When using a smart phone to read the output, you may need to step back to see the bigger picture because that picture is worth more than 1000 words. ## Author's comments: + ### Remarks You may want to use a large display with a very small-font terminal. @@ -44,11 +50,10 @@ You may want to use a large display with a very small-font terminal. Decompress `src.zip` if you need explanation. --------------------------------------------------------------------------------- - diff --git a/2014/endoh1/Rakefile b/2014/endoh1/Rakefile new file mode 100644 index 000000000..c6c696918 --- /dev/null +++ b/2014/endoh1/Rakefile @@ -0,0 +1,42 @@ +file "ast.txt" => ["src.rb", "parse.rb", "ast-def.rb"] do |t| + ruby "parse.rb", t.prerequisites[0], t.name +end + +file "seq.txt" => ["ast.txt", "serialize.rb"] do |t| + ruby "serialize.rb", t.prerequisites[0], t.name +end + +file "alpha.txt" => ["seq.txt", "alpha.rb"] do |t| + ruby "alpha.rb", t.prerequisites[0], t.name +end + +file "reg.txt" => ["alpha.txt", "register.rb"] do |t| + ruby "register.rb", t.prerequisites[0], t.name +end + +file "raw.txt" => ["reg.txt", "assemble.rb", "machine.rb"] do |t| + ruby "assemble.rb", t.prerequisites[0], t.name +end + +file "main.c" => ["raw.txt", "link.rb", "machine.rb", "vm.c"] do |t| + ruby "link.rb", t.prerequisites[0], t.name +end + +file "prog.c" => ["main.c", "gen-prog.rb", "prog-tmpl.c"] do |t| + ruby "gen-prog.rb", t.prerequisites[0], t.name +end + +file "prog" => "prog.c" do |t| + sh "gcc", "-Wall", "-W", "-Wextra", "-o", t.name, t.prerequisites[0], "-g" +end + +task :test => :default do + sh "./prog > m.c && gcc -Wall -W -Wextra -o m m.c" + sh "./m ABCDEabcde0123456789 | ruby qr-png.rb && zbarimg -q t.png" + sh "./m | ruby qr-png.rb && zbarimg -q t.png > t.c" + sh "echo -n QR-Code: > t2.c && cat m.c >> t2.c && diff t.c t2.c && echo && echo OK && echo" + sh "cat prog.c | ./iocccsize -i; wc -c prog.c" + sh "cat m.c | ./iocccsize -i; wc -c m.c" +end + +task :default => "prog" diff --git a/2014/endoh1/alpha.rb b/2014/endoh1/alpha.rb new file mode 100644 index 000000000..90596512f --- /dev/null +++ b/2014/endoh1/alpha.rb @@ -0,0 +1,141 @@ +# alpha conversion (variable renaming) + +require_relative "ast-def" + +class ID + @@num = 0 + + def initialize(name) + @unified = nil + @name = "#{ name }:#{ @@num }" + @@num += 1 + end + + attr_reader :name, :unified + + def inspect + if @unified + @unified.inspect + else + "#<#{ self.class }:#{ @name }>" + end + end + + def unify(other) + if @unified + @unified.unify(other) + elsif other.term != self + @unified = other + end + end + + def term + @unified ? @unified.term : self + end + + def ==(other) + term.name == other.term.name + end + + alias eql? == + + def hash + if @unified + @unified.hash + else + @name + end + end +end + +def merge(vars1, vars2) + vars1.each do |key, var| + if vars2[key] && vars2[key] != var + var.unify(vars2[key]) + end + end + vars1.merge(vars2) +end + +def check_opt_inc_dec(seq) + seq.each_cons(4) do |insn1, insn2, insn3, insn4| + next unless insn1.is_a?(IRead) + next unless insn2 == IPush[1] + next unless insn3 == IBinary[:+] || insn3 == IBinary[:-] + next unless insn4 == IWrite[insn1.var] + insn1.tmp.unify(insn4.tmp) + end + + seq.each do |insn| + case insn + when IBranch, ILoop + check_opt_inc_dec(insn.s1) + check_opt_inc_dec(insn.s2) + end + end +end + +def alpha(seq, vars) + nvars = vars.dup + seq.reverse_each do |insn| + case insn + when IRead + if insn.var + if insn.tmp + nvars[insn.var] ||= insn.tmp + nvars[insn.var].unify(insn.tmp) + else + insn.tmp = nvars[insn.var] ||= ID.new(insn.var) + end + end + when IWrite + if insn.var + if insn.tmp + nvars[insn.var] ||= insn.tmp + nvars[insn.var].unify(insn.tmp) + else + insn.tmp = nvars[insn.var] + end + nvars.delete(insn.var) + end + when IExit + nvars.clear + when IBranch + nvars1 = alpha(insn.s1, nvars) + nvars2 = alpha(insn.s2, nvars) + nvars = merge(nvars1, nvars2) + when ILoop + begin + nvars_back = nvars.dup + nvars1 = alpha(insn.s1, nvars) + nvars2 = alpha(insn.s1, alpha(insn.s2, nvars)) + nvars = merge(nvars1, nvars2) + merge(nvars, nvars_back) + end until nvars.keys == nvars_back.keys + end + end + nvars +end + +def rename(seq) + seq.each do |insn| + case insn + when IRead, IWrite + insn.tmp = ID.new(insn.var) if insn.var == "dummy" + insn.var.replace insn.tmp.term.name if insn.var + when IBranch, ILoop + rename(insn.s1) + rename(insn.s2) + end + end +end + +if ARGV.size != 2 + puts "usage: #$0 " +else + seq = eval(File.read(ARGV[0])) + alpha(seq, {}) + check_opt_inc_dec(seq) + rename(seq) + File.write(ARGV[1], seq.pretty_inspect) +end diff --git a/2014/endoh1/assemble.rb b/2014/endoh1/assemble.rb new file mode 100644 index 000000000..0dd2471cc --- /dev/null +++ b/2014/endoh1/assemble.rb @@ -0,0 +1,81 @@ +# dump machine code + +require_relative "ast-def" +require_relative "machine" + +def dump_seq(seq) + seq.each_cons(2) do |insn1, insn2| + if insn1.is_a?(IPush) && insn2.is_a?(IPush) + p insn1, insn2 + raise "consecutive push" + end + end + + seq.map do |insn| + case insn + when IRead then (REGISTER_READ + insn.var.ord - ?a.ord).chr if insn.var + when IWrite then (REGISTER_WRITE + insn.var.ord - ?a.ord).chr if insn.var + when IInc then MNEMONIC[:Inc] + (REGISTER_READ + insn.var.ord - ?a.ord).chr + when IDec then MNEMONIC[:Dec] + (REGISTER_READ + insn.var.ord - ?a.ord).chr + when IPush then encode_number(insn.num) + when IBranch + MNEMONIC[:BranchS] + dump_seq(insn.s1) + + MNEMONIC[:BranchM] + dump_seq(insn.s2) + + MNEMONIC[:BranchE] + when ILoop + MNEMONIC[:LoopS] + dump_seq(insn.s1) + + MNEMONIC[:LoopM] + dump_seq(insn.s2) + + MNEMONIC[:LoopE] + when IBinary then MNEMONIC[insn.op] + when ITest then MNEMONIC[:Test] + when IARef then MNEMONIC[:ARef] + when IOut then MNEMONIC[:Out] + when IExit then MNEMONIC[:Exit] + when IASet then MNEMONIC[:ASet] + end + end.join +end + +def check_opt_inc_dec(seq, i) + return unless seq[i].is_a?(IRead) + return unless seq[i + 1] == IPush[1] + return unless seq[i + 2] == IBinary[:+] || seq[i + 2] == IBinary[:-] + return unless seq[i + 3] == IWrite[seq[i].var] + insn = (seq[i + 2].op == :+ ? IInc : IDec)[seq[i].var] + flag = seq[i + 4].is_a?(IRead) && seq[i + 4].var == seq[i].var + [insn, seq[i].var, flag] +end + +def opt_inc_dec(seq) + while true + modified = false + (seq.size - 3).times do |i| + new_insn, var, flag = check_opt_inc_dec(seq, i) + if new_insn + if flag + seq[i, 5] = [new_insn] + else + seq[i, 4] = [new_insn, IWrite[var]] + end + modified = true + end + end + break if !modified + end + + seq.each do |insn| + case insn + when IBranch, ILoop + opt_inc_dec(insn.s1) + opt_inc_dec(insn.s2) + end + end +end + +if ARGV.size != 2 + puts "usage: #$0 " +else + seq = eval(File.read(ARGV[0])) + opt_inc_dec(seq) + File.write(ARGV[1], dump_seq(seq)) +end diff --git a/2014/endoh1/ast-def.rb b/2014/endoh1/ast-def.rb new file mode 100644 index 000000000..0f9d11413 --- /dev/null +++ b/2014/endoh1/ast-def.rb @@ -0,0 +1,89 @@ +require "pp" + +class Node + def ==(other) + self.class == other.class && + self.class::Keys.all? do |key| + instance_variable_get(:"@#{ key }") == other.send(key) + end + end + + attr_accessor :tmp + + def self.create(*keys) + klass = Class.new(Node) + klass.const_set(:Keys, keys) + klass.class_eval do + keys.each do |key| + attr_reader key + end + end + klass + end + + def self.[](*values) + new(*values) + end + + def initialize(*values) + self.class::Keys.zip(values) do |key, value| + instance_variable_set(:"@#{ key }", value) + end + end + + def inspect + "#{ self.class }[#{ + self.class::Keys.map do |key| + instance_variable_get(:"@#{ key }").inspect + end.join(", ") + }]" + end + + def pretty_print(q) + q.group(2, "#{ self.class }[", "]") do + q.seplist( + self.class::Keys.map do |key| + instance_variable_get(:"@#{ key }") + end + ) do |v| + q.pp v + end + end + end +end + +PRead = Node.create(:var) +PImm = Node.create(:num) +PNop = Node.create + +PBinary = Node.create(:op, :e1, :e2) +PIfThenElse = Node.create(:cond, :e1, :e2) + +PRShift = Node.create(:val, :shift) +PARef = Node.create(:e1, :e2) # e1[e2] +PSRef = Node.create(:e) +PIRef = Node.create(:e) + +PSequence = Node.create(:seq, :e) + +PASet = Node.create(:e1, :e2, :e3) # e1[e2] = e3 +PAssign = Node.create(:var, :exp) +PLoop = Node.create(:cond, :body) +PExit = Node.create(:e) +POut = Node.create(:e) +PTest = Node.create(:e) + +IRead = Node.create(:var) +IWrite = Node.create(:var) +IInc = Node.create(:var) +IDec = Node.create(:var) +INop = Node.create +IPush = Node.create(:num) +IBinary = Node.create(:op) +IARef = Node.create +IASet = Node.create +IExit = Node.create +IOut = Node.create +ITest = Node.create +IBranch = Node.create(:s1, :s2) +ILoop = Node.create(:s1, :s2) diff --git a/2014/endoh1/gen-prog.rb b/2014/endoh1/gen-prog.rb new file mode 100644 index 000000000..79b61025b --- /dev/null +++ b/2014/endoh1/gen-prog.rb @@ -0,0 +1,58 @@ +tmpl = < 92 - 34 + if $1 == "\0" + (len + 34).chr + s[offset, len] + else + (len + 92).chr + ($1.ord + 36).chr + end + end +end + +if ARGV.size != 2 + puts "usage: #$0 " +else + Code = File.read(ARGV[0]) + + vm_src = File.read("vm.c").lines.drop(1).join.gsub(/^\s*/, "").gsub("\n", "").chomp.gsub(/\s*\/\*.*?\*\//, "") + quine_data = compress("#include@" + vm_src.gsub('"CODE"', "Q").gsub('"', """'")) + + # back patch of absolute address + Code.gsub!(encode_number(39999)) { encode_number((Code ).size + 39901 - 1) } + Code.gsub!(encode_number(97531)) { encode_number((Code + Table ).size + 39901 - 1) } + Code.gsub!(encode_number(50000)) { encode_number((Code + Table + quine_data).size + 39901 + 1) } + + File.write(ARGV[1], "#include\n" + vm_src.sub("CODE") { Code + Table + quine_data } + "\n") +end diff --git a/2014/endoh1/machine.rb b/2014/endoh1/machine.rb new file mode 100644 index 000000000..f4d1b9fb6 --- /dev/null +++ b/2014/endoh1/machine.rb @@ -0,0 +1,57 @@ +# 33 : not used +# 34 : NG +# 35..55 : read +# 56..76 : write +# 77..85 : binary op +# 86..91 : not used +# 92 : NG +# 93..98 : control (0..5) +# 99..104 : other insns (6..12) +# 105..126 : num (13..32) + +MNEMONIC = { + :Nop => "", + + :+ => 77.chr, + :- => 78.chr, + :* => 79.chr, + :"&&" => 79.chr, + :^ => 80.chr, + :> => 81.chr, + :/ => 82.chr, + :% => 83.chr, + :">>" => 84.chr, # 6 + :"==" => 85.chr, + + :BranchS => 93.chr, # 0 -> BranchM + :LoopM => 94.chr, # 1 -> LoopE + :BranchM => 95.chr, # 2 -> BranchE + :LoopE => 96.chr, # 3 -> LoopS (back) + :BranchE => 97.chr, # 4 + :LoopS => 98.chr, # 5 + + :ARef => 99.chr, # 6 + :ASet => 100.chr, # 7 + :Out => 101.chr, # 8 + :Exit => 102.chr, # 9 + :Inc => 103.chr, # 10 + :Dec => 104.chr, # 11 + + :Test => 105.chr, +} + +REGISTER_READ = 35 +REGISTER_WRITE = 56 +IMM = 106 + +def encode_number(n) + if n >= 0 + tt = n.to_s(126 - IMM + 1).tr("0-9a-z", [*IMM.chr..126.chr].join) + raise if tt[0] == 126.chr + tt[0, 1] = tt[0].tr([*IMM.chr..125.chr].join, [*(IMM + 1).chr..126.chr].join) + else + raise if n != -1 + tt = IMM.chr + end + tt +end diff --git a/2014/endoh1/parse.rb b/2014/endoh1/parse.rb new file mode 100644 index 000000000..fe09f8716 --- /dev/null +++ b/2014/endoh1/parse.rb @@ -0,0 +1,87 @@ +# parse pseudo Ruby code and generate AST + +require_relative "ast-def" +require "ripper" + +Subroutines = {} + +def expr(e) + case e[0] + when :binary then PBinary[e[2], expr(e[1]), expr(e[3])] + when :var_ref, :vcall + name = e[1][1] + case name + when "nop" then PNop[] + when "minus1" then PImm[-1] + when "Zero" then PImm[0] + else PRead[name] + end + when :aref then PARef[expr(e[1]), expr(e[2][1][0])] + when :assign then PASet[expr(e[1][1]), expr(e[1][2][1][0]), expr(e[2])] + when :@int then PImm[e[1].to_i] + when :paren + PSequence[statements(e[1][0..-2]), expr(e[1][-1])] + when :ifop + PIfThenElse[expr(e[1]), expr(e[2]), expr(e[3])] + when :method_add_arg + case e[1][1][1] + when "inc", "dec" + r = e[2][1][1][0][1][1] + PSequence[ + [PAssign[r, PBinary[e[1][1][1] == "inc" ? :+ : :-, PRead[r], PImm[1]]]], + PRead[r] + ] + else + pp e + raise e[1][1][1] + end + when Array then expr(e[0]) + else + pp e + raise + end +end + +def statement(e) + case e[0] + when :assign then PAssign[e[1][1][1], expr(e[2])] + when :method_add_block + if e[1][1][1][1] == "loop" + PLoop[expr(e[1][2][1][1]), statements(e[2][2])] + else + pp e + raise + end + when :command + case e[1][1] + when "call" then Subroutines[e[2][1][0][1][1]] + when "exit" then PExit[expr(e[2][1][0])] + when "out" then POut[expr(e[2][1][0])] + when "test" then PTest[expr(e[2][1][0])] + else + pp e + exit + end + when :void_stmt then PNop[] + else + pp e + raise e[0].to_s + end +end + +def statements(s) + s.map {|e| statement(e) } +end + +def program(e) + e[1].each do |e| + Subroutines[e[1][1][1][1]] = statements(e[2][2]) + end +end + +if ARGV.size != 2 + puts "usage: #$0 " +else + program(Ripper.sexp(File.read(ARGV[0]))) + File.write(ARGV[1], Subroutines["main"].pretty_inspect) +end diff --git a/2014/endoh1/prog-tmpl.c b/2014/endoh1/prog-tmpl.c new file mode 100644 index 000000000..c856f0aec --- /dev/null +++ b/2014/endoh1/prog-tmpl.c @@ -0,0 +1,11 @@ +char*s="CODE",*p,b,d[9338],*q,x,*r=d; +int main(){ + ;for(p=q=5000+0+d;*s;s++)if(*s>32)*p++=*s-89?*s:32; + for(p=INDX+q;(b=*p++);){ + for(d[17]=10;x=*p++,b<92&&344*23;r++)*r=r[36-x]; + } + puts(d); + return(0); +}/*IOCCC2014IOCCC2014IOCCC2014IOCCC2014IOCCC*/ diff --git a/2014/endoh1/read_me.md b/2014/endoh1/read_me.md new file mode 100644 index 000000000..4a9858257 --- /dev/null +++ b/2014/endoh1/read_me.md @@ -0,0 +1,149 @@ +# Q(uine)R code generator + +## What is difficult? + +As you may know, QR code (specification: ISO/IEC 18004) is quite complex. +It requires a bit stream, Reed-Solomon codes, BCH codes, +data relocation, big (and irregular!) numerical tables, +human-oriented pattern arrangement (difficult to implement by a small program), +etc. + +On the other hand, a quine has a big restriction: +it needs to have two copies of the program! + +Do you think that it is possible to write QR code encoder in C within *1kB*? + + +## How did I achieve that? + +It is known that we can mitigate the quine restriction by `eval`. +For example, this is a quine written in Ruby: + + eval s="print 'eval s='; p s" + +Note that `print` appears just once in the program. + +To emulate this approach in C, I adopted the following structure. + + char s[]="
int main() { }"; + int main() { ; } + +* `` : An own VM implementation (written in C) that executes the main code +* `
` : An object code of quine and QR code encoder (written in the assembly language of the VM) + +`
` (about 1100 bytes) appears once +and `` (about 450 bytes) appears twice. +So the whole code is about 1100 + 450 * 2 = 2000 bytes. Hooray! + + +## What sort of VM is that? + +The VM is a kind of stack machine which makes the object code compact. +The instruction set is carefully selected +to minimize the sizes of both `
` and ``. + +* 21 integer registers access + * read, write, inc-and-read, dec-and-read +* Binary operations + * add, sub, mul, div, mod, xor, less than, equal, rshift +* Stack operation + * push (an immediate value) +* Heap access + * load, store +* If-Then-Else +* While-Loop +* Misc. + * putchar, exit + +The instructions have variable length, +but many of them are represented as one printable character. +(See `machine.rb` and `assemble.rb` in detail.) +The mapping between instructions and characters is also carefully designed +to minimize ``, +which makes the resulted program non-human-readable. + + +## How did I create the main code? + +It is too tedious to write the machine language directly. +So I wrote the source code in Ruby syntax and created a Ruby-to-VM compiler +that involves alpha renaming and automatic register allocation +only for this purpose. + +* `src.rb`: the source code of QR code encoder written in Ruby syntax. +* `vm.c`: the source code of the VM. + +The compiler tool chain is the following. + + src.rb interp.c + | | + | [parse.rb] | + V | + ast.txt | + | | + | [serialize.rb] | + V | + seq.txt | + | | + | [alpha.rb] | + V | + alpha.txt | + | | + | [register.rb] | + V | + reg.txt | + | | + | [assemble.rb] | + V | + raw.txt | + | | + +---------+---------+ + | + | [link.rb] + V + main.c + | + | [gen-prog.c] + V + prog.c + +* `parse.rb` converts Ruby to AST. +* `serialize.rb` converts AST to intermediate instruction sequence. +* `alpha.rb` applies "alpha-renaming" to the instruction sequence. +* `register.rb` applies "register allocation". +* `assemble.rb` converts the sequence to the final stack-machine instructions. +* `interp.c` is an implementation of the VM. +* `link.rb` generates `main.c` by linking the instructions and interpreter source. +* `gen-prog.c` generates `prog.c` based on `main.c` + +You can run the process by `rake` command. `ruby-minisat` gem is needed. + + $ gem install ruby-minisat + $ rake + +## Tools + +I checked that the following QR code readers can read the output of my program. + + * Linux: [ZBar bar code reader][1] + * Android: [Barcode Scanner][2] + +[1]: http://zbar.sourceforge.net/ +[2]: https://play.google.com/store/apps/details?id=com.google.zxing.client.android] + +You can use `tool.rb` to convert the output to a png file. + + $ ./main Hello | ruby tool.rb > hello.png + + $ zbarimg -q hello.png + QR-Code:Hello + + $ ruby tool.rb prog.c + $ ruby tool.rb prog.c | ruby tool.rb + +## Limitations + +* QR code error correction level is fixed to L (Low). +* It supports only byte mode (not numeric, alphanumeric, kanji mode...) +* The mask pattern is fixed to 4. +* The mask is not applied to the remained bits. diff --git a/2014/endoh1/register.rb b/2014/endoh1/register.rb new file mode 100644 index 000000000..c3949d952 --- /dev/null +++ b/2014/endoh1/register.rb @@ -0,0 +1,83 @@ +# register allocation (variable renaming again!) + +require_relative "ast-def" +require "minisat" + +Conflicts = {} + +def liveness(seq, vars) + nvars = vars.dup + + seq.reverse_each do |insn| + case insn + when IRead, IWrite + nvars[insn.var] = true if insn.var + when IExit + nvars.clear + when IBranch + nvars1 = liveness(insn.s1, nvars) + nvars2 = liveness(insn.s2, nvars) + nvars = nvars1.merge(nvars2) + when ILoop + begin + nvars_back = nvars + nvars1 = liveness(insn.s1, nvars) + nvars2 = liveness(insn.s1, liveness(insn.s2, nvars)) + nvars = nvars1.merge(nvars2) + end until nvars.keys == nvars_back.keys + end + + nvars.keys.sort.combination(2) do |v1, v2| + Conflicts[[v1, v2].sort] = true + end + + nvars.delete(insn.var) if insn.is_a?(IWrite) && insn.var + end + + nvars +end + +def graph_coloring(liveness, k) + solver = MiniSat::Solver.new + + h = {} + + liveness.keys.flatten.uniq.each do |var| + vs = (0...k).map { solver.new_var } + solver << vs + vs.combination(2) {|v1, v2| solver << [-v1, -v2] } + h[var] = vs + end + + liveness.each_key do |var1, var2| + h[var1].zip(h[var2]) {|v1, v2| solver << [-v1, -v2] } + end + + solver.solve + + h.each do |var, vs| + h[var] = (vs.find_index {|v| solver[v] } + ?a.ord).chr + end + h +end + +def rename(seq, h) + seq.each do |insn| + case insn + when IRead, IWrite + insn.var.replace h[insn.var] if insn.var + when IBranch, ILoop + rename(insn.s1, h) + rename(insn.s2, h) + end + end +end + +if ARGV.size != 2 + puts "usage: #$0 " +else + seq = eval(File.read(ARGV[0])) + liveness(seq, {}) + rename(seq, graph_coloring(Conflicts, 21)) + File.write(ARGV[1], seq.pretty_inspect) +end diff --git a/2014/endoh1/serialize.rb b/2014/endoh1/serialize.rb new file mode 100644 index 000000000..f9ef9e7ad --- /dev/null +++ b/2014/endoh1/serialize.rb @@ -0,0 +1,122 @@ +# translate AST into stack-based instruction sequence + +require_relative "ast-def" + +class PRead + def emit + [IRead[var]] + end +end + +class PImm + def emit + [IPush[num]] + end +end + +class PNop + def emit + [INop[]] + end +end + +class PBinary + def emit + if op == :< + e2.emit + e1.emit + [IBinary[:>]] + else + e1.emit + e2.emit + [IBinary[op]] + end + end +end + +class PIfThenElse + def emit + s1 = e1.emit + s2 = e2.emit + + # merge the common tails if any + tails = [] + s1.reverse.zip(s2.reverse) do |i1, i2| + break if i1 != i2 + s1.pop + s2.pop + tails.unshift i1 + end + + cond.emit + [IBranch[s1, s2]] + tails + end +end + +class PARef + def emit + if e1 == PImm[0] && e2.is_a?(PBinary) && e2.op == :+ + e2.emit[0..-2] + [IARef[]] + elsif e2 == PImm[0] && e1.is_a?(PBinary) && e1.op == :+ + e1.emit[0..-2] + [IARef[]] + else + e1.emit + e2.emit + [IARef[]] + end + end +end + +class PASet + def emit + if e1 == PImm[0] && e2.is_a?(PBinary) && e2.op == :+ + e3.emit + e2.emit[0..-2] + [IASet[]] + elsif e2 == PImm[0] && e1.is_a?(PBinary) && e1.op == :+ + e3.emit + e1.emit[0..-2] + [IASet[]] + else + e3.emit + e2.emit + e1.emit + [IASet[]] + end + end +end + +class PAssign + def emit + exp.emit + [IWrite[var != ?_ ? var : nil]] + end +end + +class PSequence + def emit + seq.flat_map {|e| e.emit } + e.emit + end +end + +class PLoop + def emit + [ILoop[cond.emit, body.flat_map {|e| e.emit }]] + end +end + +class PTest + def emit + e.emit + [ITest[]] + end +end + +class PExit + def emit + e.emit + [IExit[]] + end +end + +class POut + def emit + e.emit + [IOut[]] + end +end + +class Array + def emit + flat_map {|e| e.emit } + end +end + +if ARGV.size != 2 + puts "usage: #$0 " +else + ast = eval(File.read(ARGV[0])) + File.write(ARGV[1], ast.flat_map {|e| e.emit }.pretty_inspect) +end diff --git a/2014/endoh1/src.rb b/2014/endoh1/src.rb new file mode 100644 index 000000000..35accbace --- /dev/null +++ b/2014/endoh1/src.rb @@ -0,0 +1,280 @@ +# magical calculation methods! + +# calculate the total code words for the version given +total_codewords { + n = Version / 7 * 5 + 5 + TotalCodes = + Size_1 * Size_1 / 8 - 24 - + (Version > 6 ? n * n / 8 + n : (Version > 1) * 4) +} + +# calculate the error correction code words for the version given +error_correction_codewords { + ECCodes = TotalCodes * 54 / 265 - Zero[39999 + Version] + 43 +} + +# calculate the degree of the generator polynomial +generator_polynomial_degree { + Degree = + Version < 6 ? ECCodes : + Version == 10 ? 18: + (n = 16 + loop(ECCodes / 2 % dec(n)) { } + n * 2) +} + +other_constants { + N = ECCodes / Degree + M = N - TotalCodes % N + K = (TotalCodes - ECCodes) / N + 1 + S = Degree + K + D = K * N - M +} + + +# GF(2^8) +gen_table { + i = 511 + j = 1 + loop(i) { + j = ((Forward[Backward[j] = dec(i)] = j) ^ (285 * (j % 2))) / 2 + } +} + +# calculate the generator polynomial +setup_generator_polynomial { + i = Degree + 1 + loop(dec(i)) { + j = Degree + 1 + x = 0 + loop(j > i) { + y = Coefs[dec(j)] + dummy = Coefs[j] = Backward[x ^ Forward[Degree - i + y]] + x = Forward[y] + } + } +} + +# paint fixed cells +setup_fixed_patterns { + # three big markers + i = Size + loop(i) { + i = i - 1 + j = Size + loop(j) { + k = i - j * Size + dummy = + Zero[j * Size + i] = + Zero[minus1 - k] = + Zero[Size * Size + k] = + (i < 7 && dec(j) < 7 && (((i - 6) * i + 19 + (j - 6) * j) / 5) ^ 2 < 3) - + ((i > 7) + (j > 7) > 0) + } + } + + # alignment markers + x = Version / 7 + 2 + n = Version - 32 ? (Version + x / 2) * 2 / (x - 1) * 2 : 26 + i = x * x + loop(Version > 1 && dec(i) > 1) { + j = 25 + loop(i - x && j) { + y = Size_1 - 8 + n + dummy = Zero[ + ((y - i / x * n) % (y - 4) + j / 5) * Size + + ((y - i % x * n) % (y - 4) + j % 5) + ] = (33084991 >> dec(j)) % 2 + } + } + + # BCH for version info + n = Version * 4096 + k = n + j = 64 + loop(j) { + k = k ^ ((7973 * j) * (k + 1 > 4096 * j)) + j = j / 2 + } + # (n + k) is the code + + # version info and timing + i = 18 * (Version > 6) + loop(i + Size - 16) { + dummy = + Zero[x * Size + y] = + Zero[y * Size + x] = + dec(i) < 0 ? + (x = Size - 8 + i ; y = 6 ; (0 - i) % 2) : + (x = Size - 11 + i % 3; y = i / 3; ((n + k) >> i) % 2) + } + + # format info + i = 15 + loop(i) { + # BCH for format info (4 + 8) (mask = 4, error correction level = L) + # 26159 is the code + n = dec(i) > 7 ? Size_1 - 14 + i : i + (i > 5) + dummy = + Zero[Size * (Size - 8) + 8] = + Zero[n * Size + 8] = + Zero[9 * Size_1 - n + 8 + (i / 3 == 2)] = + (26159 >> i) % 2 + } +} + +# data encoding +setup_data { + n = minus1 + buff = 64 + z = 17 + j = 0 + loop(inc(n) < D) { + x = Input[j] + _ = x ? + (y = V9 < 0 ? (j = j + 1; x) : (V9 = V9 - 1; V9 ? InputLen % 256 : (InputLen >> 8)) + emit = buff + y / 16 + buff = (y % 16) * 16 + nop) : + (emit = buff + z = z ^ 253 + buff = z + nop) + dummy = EncodedData[n] = emit + } +} + +# error correction +encode { + x = D + i = N + loop(i) { + y = D + S * i + n = K - (dec(i) < M) + j = n + Degree + loop(j) { + dummy = EncodedData[dec(y)] = dec(j) < n ? EncodedData[dec(x)] : 0 + } + loop(j < n * Degree) { + t = j % Degree + 1 + idx = EncodedData + y + j / Degree + dummy = idx[t] = idx[t] ^ ((idx[0] > 0) * Forward[Coefs[t] + Backward[idx[0]]]) + j = j + 1 + } + } +} + +# make the pattern by placing the data +place { + x = Size_1 + y = Size_1 + j = minus1 + loop(inc(j) < S) { + i = M * (j == K - 1) + loop(i < N) { + t = 8 + loop(t) { + xx = x + (x > 5) + _ = Zero[y * Size + xx] < 0 ? + (dummy = Zero[y * Size + xx] = + ((EncodedData[j + (j < K ? i * K - (i < M ? i : M) : D + i * S)] >> dec(t)) ^ + (y / 2 + xx / 3)) % 2 == 0; + nop) : nop + x = x - + (x % 2 ? 1 : + (x / 2 % 2 ? + (y ? (y = y - 1; minus1) : 1) : + (y - Size_1 ? (y = y + 1; minus1) : 1))) + } + i = i + 1 + } + } +} + +# print the pattern to stdout +output { + k = Size + 4 + y = Size_1 - k + loop(inc(y) < k) { + x = Size_1 - k + loop(inc(x) < k) { + out 27; out 91; out 52 + out 55 - + (x > minus1 && x < Size && y > minus1 && y < Size) * + (x + y * Size < 0 ? 0 : (Zero[x + y * Size] > 0)) * 7 + out 109; out 32; out 32 + } + out 27; out 91; out 48; out 109; out 10 + } +} + +# generate the code itself by decompression +decompress { + i = 97531 # a placeholder value that will be replaced by assemble.rb + InputLen = minus1 + # decompress + loop((b = Zero[inc(i)]; b)) { + _ = b < 92 ? + (loop(dec(b) > 33) { + x = Zero[inc(i)] + _ = x - 81 ? nop : + (k = 39900 # quine + x = Input[inc(InputLen)] = 34 + loop((z = Zero[inc(k)]; z)) { + dummy = Input[inc(InputLen)] = z + } + nop + ) + dummy = Input[inc(InputLen)] = x + } + nop) : + (i = i + 1 + loop(dec(b) > 91) { + dummy = Input[InputLen] = Input[inc(InputLen) - i[0] + 36] + } + nop) + } + InputLen = InputLen + 1 + dummy = 17[Input] = 10 # newline after "#include" +} + +main { + # memory layout + # 0...31329: Pattern data + # 32000...32512: Forward translation of GF(2^8) + # 32512...32768: Backward translation of GF(2^8) + # 32800...33000: Coefficients of generator polynomial + # 33000...37000: EncodedData + # 40000... : Code + nul + Input + Forward = 32000 + Backward = 32512 + Coefs = 32800 + EncodedData = 33000 + Input = 50000 # a placeholder + + loop(Input[InputLen]) { InputLen = InputLen + 1 } + _ = InputLen ? nop : (call decompress; nop) + + Version = 0 + loop(Version < 40) { + Size_1 = 4 * inc(Version) + 16 + Size = Size_1 + 1 + call total_codewords + call error_correction_codewords + call generator_polynomial_degree + call other_constants + V9 = Version > 9 + _ = D - 1 - V9 > InputLen ? ( + call gen_table + call setup_generator_polynomial + call setup_fixed_patterns + call setup_data + call encode + call place + call output + exit 0 + nop + ) : nop + } + exit 1 +} diff --git a/2014/endoh1/src.zip b/2014/endoh1/src.zip deleted file mode 100644 index d197a9030b81390ab44ba7f24c33b5e07da7bfbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18901 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 diff --git a/2014/endoh1/tool.rb b/2014/endoh1/tool.rb new file mode 100644 index 000000000..26d46ccaa --- /dev/null +++ b/2014/endoh1/tool.rb @@ -0,0 +1,49 @@ +require "zlib" + +lines = [*$<] + +if lines.join.gsub(/\e\[\d*m/, "").chars.uniq.sort == ["\n", " "] + + matrix = lines.map do |line| + row = [] + cell = nil + line.chomp.scan(/\G(?:\e\[(\d+)m|..)/) do + if $1 + cell = $1 == "40" ? :black : :white + else + row << cell + end + end + row + end + + size = matrix.size + if size != matrix.first.size + raise "not square: #{ matrix.first.size } x #{ matrix.size }" + end + $stderr.puts "Version: #{ (size - 25) / 4 }" + + scale = 400 / size + data = (0 ... scale * size).flat_map do |y| + [0] + (0 ... scale * size).flat_map do |x| + matrix[y / scale][x / scale] == :black ? [0, 0, 0] : [255, 255, 255] + end + end + + print "\x89PNG\r\n\x1a\n" + [ + ["IHDR", [scale * size, scale * size, 8, 2, 0, 0, 0].pack("NNCCCCC")], + ["IDAT", Zlib::Deflate.deflate(data.pack("C*"))], + ["IEND", ""], + ].each do |type, data| + print [data.bytesize, type, data, Zlib.crc32(type + data)].pack("NA4A*N") + end + +else + + width = lines.map {|line| line.chomp.size + 4 * 4 }.max + lines.each do |line| + puts line.chomp.ljust(width).gsub(/../) { "\e[#{ $& == " " ? 47 : 40 }m " } + end + +end diff --git a/2014/endoh1/vm.c b/2014/endoh1/vm.c new file mode 100644 index 000000000..eae7e969a --- /dev/null +++ b/2014/endoh1/vm.c @@ -0,0 +1,29 @@ +#include +int H[99999],*s,*c,d,t; +int main(int n,char*v[]){ + for(s=c=H+39999;(*++s="CODE"[d++]);); + for(;n>1&&(*++s=v[1][t++]);); + /*for(;n=getchar(),n>0;)*++s=n;*/ + for(s=H;d=*c++%93,d-9;){ + int v=*s,g[]={n+v,v-n,n*v,v^n,n>n,v==n}; + for(t=0;d<4&&d>=2*!!n&&(c-=d/3*2,v=3+*c++,t||v!=98+d);) + t+=v++/6-16?0:v/2%3-1; + for(t=d-14;t>0&&d<34&&105<*c;)t=t*21+*c++-106; + n= + /*d==12?fprintf(stderr,"test:%d\n",n),*s--:*/ + d>76?s--,g[d-77]: /* operation */ + d>55?H[d-21]=n,*s--: /* reg write */ + d>9? + *++s=n, + 12>d?H[*c++]+=21-d*2: /* inc / dec */ + d<34?t:H[d] /* imm push / reg read */ + : + d>5? + s--, + 7d?*s--:n; /* control */ + } + return n; +}