Calendar - for "REAL" programmers: Difference between revisions
Content added Content deleted
m (fix typo in text) |
(Add Ruby.) |
||
Line 471: | Line 471: | ||
31 Wed</pre> |
31 Wed</pre> |
||
=={{header|Ruby}}== |
|||
{{omit from|Tcl|Real Tcl programmers use lower case! To be exact, a preamble could be written which would transform a Tcl interpreter into using upper case, but that would have to be part of this task's solution and would itself be in lower case, at least initially.}} |
|||
UPPERCASE RUBY is normally impossible, because Ruby is sensitive to case, and most methods and keywords have lowercase letters. To solve this task, we wrote a new program loader for UPPERCASE RUBY programs. |
|||
{{omit from|Java|Java's keywords are lower case}} |
|||
Our program loader [[respond to an unknown method call#Ruby|responds to unknown method calls]] by calling their lowercase equivalents, so Kernel#PUTS acts like Kernel#puts. Our program loader also defines Object#RESCUE to replace the 'rescue' keyword. We can now write UPPERCASE RUBY programs that call uppercase methods and use no lowercase keywords. |
|||
* <code>loadup.rb</code> is the program loader. |
|||
<lang ruby># loadup.rb - run UPPERCASE RUBY program |
|||
class Object |
|||
alias lowercase_method_missing method_missing |
|||
# Allow UPPERCASE method calls. |
|||
def method_missing(sym, *args, &block) |
|||
str = sym.to_s |
|||
if str == (down = str.downcase) |
|||
lowercase_method_missing sym, *args, &block |
|||
else |
|||
send down, *args, &block |
|||
end |
|||
end |
|||
# RESCUE an exception without the 'rescue' keyword. |
|||
def RESCUE(_BEGIN, _CLASS, _RESCUE) |
|||
begin _BEGIN.CALL |
|||
rescue _CLASS |
|||
_RESCUE.CALL; end |
|||
end |
|||
end |
|||
_PROGRAM = ARGV.SHIFT |
|||
_PROGRAM || ABORT("USAGE: #{$0} PROGRAM.RB ARGS...") |
|||
LOAD ($0 = _PROGRAM)</lang> |
|||
* <code>CAL.RB</code> is an UPPERCASE RUBY translation of [[Calendar#Ruby]]. |
|||
{{works with|Ruby|1.8.7}} |
|||
<lang ruby># CAL.RB - CALENDAR |
|||
REQUIRE 'DATE'.DOWNCASE |
|||
# FIND CLASSES. |
|||
OBJECT = [].CLASS.SUPERCLASS |
|||
DATE = OBJECT.CONST_GET('DATE'.DOWNCASE.CAPITALIZE) |
|||
# CREATES A CALENDAR OF _YEAR_. RETURNS THIS CALENDAR AS A MULTI-LINE |
|||
# STRING FIT TO _COLUMNS_. |
|||
OBJECT.SEND(:DEFINE_METHOD, :CAL) {|_YEAR, _COLUMNS| |
|||
# START AT JANUARY 1. |
|||
# |
|||
# DATE::ENGLAND MARKS THE SWITCH FROM JULIAN CALENDAR TO GREGORIAN |
|||
# CALENDAR AT 1752 SEPTEMBER 14. THIS REMOVES SEPTEMBER 3 TO 13 FROM |
|||
# YEAR 1752. (BY FORTUNE, IT KEEPS JANUARY 1.) |
|||
# |
|||
_DATE = DATE.NEW(_YEAR, 1, 1, DATE::ENGLAND) |
|||
# COLLECT CALENDARS OF ALL 12 MONTHS. |
|||
_MONTHS = (1..12).COLLECT {|_MONTH| |
|||
_ROWS = [DATE::MONTHNAMES[_MONTH].UPCASE.CENTER(20), |
|||
"SU MO TU WE TH FR SA"] |
|||
# MAKE ARRAY OF 42 DAYS, STARTING WITH SUNDAY. |
|||
_DAYS = [] |
|||
_DATE.WDAY.TIMES { _DAYS.PUSH " " } |
|||
CATCH(:BREAK) { |
|||
LOOP { |
|||
(_DATE.MONTH == _MONTH) || THROW(:BREAK) |
|||
_DAYS.PUSH("%2D".DOWNCASE % _DATE.MDAY) |
|||
_DATE += 1 }} |
|||
(42 - _DAYS.LENGTH).TIMES { _DAYS.PUSH " " } |
|||
_DAYS.EACH_SLICE(7) {|_WEEK| _ROWS.PUSH(_WEEK.JOIN " ") } |
|||
_ROWS } |
|||
# CALCULATE MONTHS PER ROW (MPR). |
|||
# 1. DIVIDE COLUMNS BY 22 COLUMNS PER MONTH, ROUNDED DOWN. (PRETEND |
|||
# TO HAVE 2 EXTRA COLUMNS; LAST MONTH USES ONLY 20 COLUMNS.) |
|||
# 2. DECREASE MPR IF 12 MONTHS WOULD FIT IN THE SAME MONTHS PER |
|||
# COLUMN (MPC). FOR EXAMPLE, IF WE CAN FIT 5 MPR AND 3 MPC, THEN |
|||
# WE USE 4 MPR AND 3 MPC. |
|||
_MPR = (_COLUMNS + 2).DIV 22 |
|||
_MPR = 12.DIV((12 + _MPR - 1).DIV _MPR) |
|||
# USE 20 COLUMNS PER MONTH + 2 SPACES BETWEEN MONTHS. |
|||
_WIDTH = _MPR * 22 - 2 |
|||
# JOIN MONTHS INTO CALENDAR. |
|||
_ROWS = ["[SNOOPY]".CENTER(_WIDTH), "#{_YEAR}".CENTER(_WIDTH)] |
|||
_MONTHS.EACH_SLICE(_MPR) {|_SLICE| |
|||
_SLICE[0].EACH_INDEX {|_I| |
|||
_ROWS.PUSH(_SLICE.MAP {|_A| _A[_I]}.JOIN " ") }} |
|||
_ROWS.JOIN("\012") } |
|||
(ARGV.LENGTH == 1) || ABORT("USAGE: #{$0} YEAR") |
|||
# GUESS WIDTH OF TERMINAL. |
|||
# 1. OBEY ENVIRONMENT VARIABLE COLUMNS. |
|||
# 2. TRY TO REQUIRE 'IO/CONSOLE' FROM RUBY 1.9.3. |
|||
# 3. TRY TO RUN `TPUT CO`. |
|||
# 4. ASSUME 80 COLUMNS. |
|||
LOADERROR = OBJECT.CONST_GET('LOAD'.DOWNCASE.CAPITALIZE + |
|||
'ERROR'.DOWNCASE.CAPITALIZE) |
|||
STANDARDERROR = OBJECT.CONST_GET('STANDARD'.DOWNCASE.CAPITALIZE + |
|||
'ERROR'.DOWNCASE.CAPITALIZE) |
|||
_INTEGER = 'INTEGER'.DOWNCASE.CAPITALIZE |
|||
_TPUT_CO = 'TPUT CO'.DOWNCASE |
|||
_COLUMNS = RESCUE(PROC {SEND(_INTEGER, ENV["COLUMNS"] || "")}, |
|||
STANDARDERROR, |
|||
PROC { |
|||
RESCUE(PROC { |
|||
REQUIRE 'IO/CONSOLE'.DOWNCASE |
|||
IO.CONSOLE.WINSIZE[1] |
|||
}, LOADERROR, |
|||
PROC { |
|||
RESCUE(PROC { |
|||
SEND(_INTEGER, `#{_TPUT_CO}`) |
|||
}, STANDARDERROR, |
|||
PROC {80}) }) }) |
|||
PUTS CAL(ARGV[0].TO_I, _COLUMNS)</lang> |
|||
* A local variable must start with a lowercase letter or an underscore, so we have many leading underscores (_YEAR, _COLUMN, _DATE and so on). |
|||
* We form blocks with curly braces <code>{ ... }</code>, never with lowercase keywords <code>do ... end</code>. |
|||
* <code>OBJECT = [].CLASS.SUPERCLASS</code> finds the superclass of the class of the empty array; this is the <code>Object</code> class! Class#CONST_GET finds some other classes. |
|||
* Class#DEFINE_METHOD defines a new method, Object#CAL, without a <code>def</code> keyword. |
|||
* Kernel#LOOP, Kernel#THROW, Kernel#CATCH and operator <code>||</code> act like a while loop without a <code>while</code> keyword. |
|||
<pre>$ ruby loadup.rb CAL.RB 1969 |
|||
[SNOOPY] |
|||
1969 |
|||
JANUARY FEBRUARY MARCH APRIL MAY JUNE |
|||
SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA |
|||
1 2 3 4 1 1 1 2 3 4 5 1 2 3 1 2 3 4 5 6 7 |
|||
5 6 7 8 9 10 11 2 3 4 5 6 7 8 2 3 4 5 6 7 8 6 7 8 9 10 11 12 4 5 6 7 8 9 10 8 9 10 11 12 13 14 |
|||
12 13 14 15 16 17 18 9 10 11 12 13 14 15 9 10 11 12 13 14 15 13 14 15 16 17 18 19 11 12 13 14 15 16 17 15 16 17 18 19 20 21 |
|||
19 20 21 22 23 24 25 16 17 18 19 20 21 22 16 17 18 19 20 21 22 20 21 22 23 24 25 26 18 19 20 21 22 23 24 22 23 24 25 26 27 28 |
|||
26 27 28 29 30 31 23 24 25 26 27 28 23 24 25 26 27 28 29 27 28 29 30 25 26 27 28 29 30 31 29 30 |
|||
30 31 |
|||
JULY AUGUST SEPTEMBER OCTOBER NOVEMBER DECEMBER |
|||
SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA SU MO TU WE TH FR SA |
|||
1 2 3 4 5 1 2 1 2 3 4 5 6 1 2 3 4 1 1 2 3 4 5 6 |
|||
6 7 8 9 10 11 12 3 4 5 6 7 8 9 7 8 9 10 11 12 13 5 6 7 8 9 10 11 2 3 4 5 6 7 8 7 8 9 10 11 12 13 |
|||
13 14 15 16 17 18 19 10 11 12 13 14 15 16 14 15 16 17 18 19 20 12 13 14 15 16 17 18 9 10 11 12 13 14 15 14 15 16 17 18 19 20 |
|||
20 21 22 23 24 25 26 17 18 19 20 21 22 23 21 22 23 24 25 26 27 19 20 21 22 23 24 25 16 17 18 19 20 21 22 21 22 23 24 25 26 27 |
|||
27 28 29 30 31 24 25 26 27 28 29 30 28 29 30 26 27 28 29 30 31 23 24 25 26 27 28 29 28 29 30 31 |
|||
31 30 </pre> |
|||
{{omit from|D|D's keywords are lower case}} |
{{omit from|D|D's keywords are lower case}} |
||
{{omit from|Java|Java's keywords are lower case}} |
|||
{{omit from|Tcl|Real Tcl programmers use lower case! To be exact, a preamble could be written which would transform a Tcl interpreter into using upper case, but that would have to be part of this task's solution and would itself be in lower case, at least initially.}} |