bcdice 3.0.0.pre.alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.editorconfig +21 -0
- data/.github/.codecov.yaml +10 -0
- data/.github/workflows/coverage.yaml +21 -0
- data/.github/workflows/lint.yaml +13 -0
- data/.github/workflows/test.yml +20 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +96 -0
- data/.rubocop_todo.yml +113 -0
- data/CHANGELOG.md +868 -0
- data/Gemfile +11 -0
- data/LICENSE +29 -0
- data/README.md +38 -0
- data/ROADMAP.md +30 -0
- data/Rakefile +118 -0
- data/bcdice.gemspec +27 -0
- data/bin/repl.rb +21 -0
- data/docs/README.txt +2028 -0
- data/docs/dicebot_sort_key.md +72 -0
- data/docs/how_to_make_dicebot.md +453 -0
- data/lib/bcdice.rb +8 -0
- data/lib/bcdice/arithmetic_evaluator.rb +167 -0
- data/lib/bcdice/base.rb +423 -0
- data/lib/bcdice/command_parser.rb +242 -0
- data/lib/bcdice/common_command.rb +23 -0
- data/lib/bcdice/common_command/add_dice.rb +54 -0
- data/lib/bcdice/common_command/add_dice/node.rb +488 -0
- data/lib/bcdice/common_command/add_dice/parser.rb +282 -0
- data/lib/bcdice/common_command/add_dice/randomizer.rb +48 -0
- data/lib/bcdice/common_command/barabara_dice.rb +57 -0
- data/lib/bcdice/common_command/calc.rb +39 -0
- data/lib/bcdice/common_command/choice.rb +35 -0
- data/lib/bcdice/common_command/d66_dice.rb +63 -0
- data/lib/bcdice/common_command/reroll_dice.rb +190 -0
- data/lib/bcdice/common_command/upper_dice.rb +165 -0
- data/lib/bcdice/common_command/version.rb +26 -0
- data/lib/bcdice/dice_table.rb +8 -0
- data/lib/bcdice/dice_table/chain_table.rb +33 -0
- data/lib/bcdice/dice_table/d66_grid_table.rb +26 -0
- data/lib/bcdice/dice_table/d66_range_table.rb +26 -0
- data/lib/bcdice/dice_table/d66_table.rb +34 -0
- data/lib/bcdice/dice_table/range_table.rb +267 -0
- data/lib/bcdice/dice_table/roll_result.rb +43 -0
- data/lib/bcdice/dice_table/sai_fic_skill_table.rb +32 -0
- data/lib/bcdice/dice_table/table.rb +35 -0
- data/lib/bcdice/enum.rb +15 -0
- data/lib/bcdice/format.rb +36 -0
- data/lib/bcdice/game_system.rb +187 -0
- data/lib/bcdice/game_system/AFF2e.rb +141 -0
- data/lib/bcdice/game_system/AceKillerGene.rb +55 -0
- data/lib/bcdice/game_system/Airgetlamh.rb +117 -0
- data/lib/bcdice/game_system/Alsetto.rb +124 -0
- data/lib/bcdice/game_system/Alshard.rb +27 -0
- data/lib/bcdice/game_system/AlterRaise.rb +282 -0
- data/lib/bcdice/game_system/Amadeus.rb +622 -0
- data/lib/bcdice/game_system/Amadeus_Korean.rb +501 -0
- data/lib/bcdice/game_system/AnimaAnimus.rb +96 -0
- data/lib/bcdice/game_system/Arianrhod.rb +50 -0
- data/lib/bcdice/game_system/ArsMagica.rb +149 -0
- data/lib/bcdice/game_system/Avandner.rb +101 -0
- data/lib/bcdice/game_system/BBN.rb +143 -0
- data/lib/bcdice/game_system/BadLife.rb +435 -0
- data/lib/bcdice/game_system/BarnaKronika.rb +188 -0
- data/lib/bcdice/game_system/BattleTech.rb +530 -0
- data/lib/bcdice/game_system/BeastBindTrinity.rb +348 -0
- data/lib/bcdice/game_system/BeginningIdol.rb +2527 -0
- data/lib/bcdice/game_system/BeginningIdol_Korean.rb +2378 -0
- data/lib/bcdice/game_system/BladeOfArcana.rb +252 -0
- data/lib/bcdice/game_system/BlindMythos.rb +460 -0
- data/lib/bcdice/game_system/BloodCrusade.rb +423 -0
- data/lib/bcdice/game_system/BloodMoon.rb +213 -0
- data/lib/bcdice/game_system/CardRanker.rb +249 -0
- data/lib/bcdice/game_system/ChaosFlare.rb +181 -0
- data/lib/bcdice/game_system/Chill.rb +163 -0
- data/lib/bcdice/game_system/Chill3.rb +52 -0
- data/lib/bcdice/game_system/CodeLayerd.rb +132 -0
- data/lib/bcdice/game_system/ColossalHunter.rb +569 -0
- data/lib/bcdice/game_system/CrashWorld.rb +81 -0
- data/lib/bcdice/game_system/Cthulhu.rb +252 -0
- data/lib/bcdice/game_system/Cthulhu7th.rb +767 -0
- data/lib/bcdice/game_system/Cthulhu7th_ChineseTraditional.rb +386 -0
- data/lib/bcdice/game_system/Cthulhu7th_Korean.rb +383 -0
- data/lib/bcdice/game_system/CthulhuTech.rb +263 -0
- data/lib/bcdice/game_system/Cthulhu_ChineseTraditional.rb +243 -0
- data/lib/bcdice/game_system/Cthulhu_Korean.rb +240 -0
- data/lib/bcdice/game_system/DarkBlaze.rb +207 -0
- data/lib/bcdice/game_system/DarkDaysDrive.rb +425 -0
- data/lib/bcdice/game_system/DarkSouls.rb +91 -0
- data/lib/bcdice/game_system/DeadlineHeroes.rb +583 -0
- data/lib/bcdice/game_system/DemonParasite.rb +524 -0
- data/lib/bcdice/game_system/DetatokoSaga.rb +310 -0
- data/lib/bcdice/game_system/DetatokoSaga_Korean.rb +310 -0
- data/lib/bcdice/game_system/DiceBot.rb +17 -0
- data/lib/bcdice/game_system/DiceOfTheDead.rb +111 -0
- data/lib/bcdice/game_system/DoubleCross.rb +373 -0
- data/lib/bcdice/game_system/DoubleCross_Korean.rb +371 -0
- data/lib/bcdice/game_system/Dracurouge.rb +1856 -0
- data/lib/bcdice/game_system/Dracurouge_Korean.rb +310 -0
- data/lib/bcdice/game_system/DungeonsAndDragons.rb +19 -0
- data/lib/bcdice/game_system/EarthDawn.rb +225 -0
- data/lib/bcdice/game_system/EarthDawn3.rb +275 -0
- data/lib/bcdice/game_system/EarthDawn4.rb +272 -0
- data/lib/bcdice/game_system/EclipsePhase.rb +51 -0
- data/lib/bcdice/game_system/Elric.rb +36 -0
- data/lib/bcdice/game_system/Elysion.rb +1213 -0
- data/lib/bcdice/game_system/EmbryoMachine.rb +227 -0
- data/lib/bcdice/game_system/EndBreaker.rb +132 -0
- data/lib/bcdice/game_system/EtrianOdysseySRS.rb +27 -0
- data/lib/bcdice/game_system/Fiasco.rb +123 -0
- data/lib/bcdice/game_system/Fiasco_Korean.rb +122 -0
- data/lib/bcdice/game_system/FilledWith.rb +1543 -0
- data/lib/bcdice/game_system/FullMetalPanic.rb +27 -0
- data/lib/bcdice/game_system/FutariSousa.rb +994 -0
- data/lib/bcdice/game_system/FutariSousa_Korean.rb +994 -0
- data/lib/bcdice/game_system/Garako.rb +559 -0
- data/lib/bcdice/game_system/GardenOrder.rb +451 -0
- data/lib/bcdice/game_system/GehennaAn.rb +136 -0
- data/lib/bcdice/game_system/GeishaGirlwithKatana.rb +120 -0
- data/lib/bcdice/game_system/GoblinSlayer.rb +168 -0
- data/lib/bcdice/game_system/GoldenSkyStories.rb +77 -0
- data/lib/bcdice/game_system/Gorilla.rb +44 -0
- data/lib/bcdice/game_system/GranCrest.rb +518 -0
- data/lib/bcdice/game_system/Gundog.rb +52 -0
- data/lib/bcdice/game_system/GundogRevised.rb +294 -0
- data/lib/bcdice/game_system/GundogZero.rb +261 -0
- data/lib/bcdice/game_system/Gurps.rb +403 -0
- data/lib/bcdice/game_system/GurpsFW.rb +2330 -0
- data/lib/bcdice/game_system/HarnMaster.rb +208 -0
- data/lib/bcdice/game_system/HatsuneMiku.rb +706 -0
- data/lib/bcdice/game_system/Hieizan.rb +48 -0
- data/lib/bcdice/game_system/HouraiGakuen.rb +270 -0
- data/lib/bcdice/game_system/HuntersMoon.rb +503 -0
- data/lib/bcdice/game_system/Illusio.rb +89 -0
- data/lib/bcdice/game_system/InfiniteFantasia.rb +51 -0
- data/lib/bcdice/game_system/Insane.rb +822 -0
- data/lib/bcdice/game_system/Insane_Korean.rb +773 -0
- data/lib/bcdice/game_system/IthaWenUa.rb +38 -0
- data/lib/bcdice/game_system/JamesBond.rb +45 -0
- data/lib/bcdice/game_system/Kamigakari.rb +394 -0
- data/lib/bcdice/game_system/Kamigakari_Korean.rb +428 -0
- data/lib/bcdice/game_system/KanColle.rb +938 -0
- data/lib/bcdice/game_system/KemonoNoMori.rb +258 -0
- data/lib/bcdice/game_system/KillDeathBusiness.rb +1420 -0
- data/lib/bcdice/game_system/KillDeathBusiness_Korean.rb +1510 -0
- data/lib/bcdice/game_system/KurayamiCrying.rb +49 -0
- data/lib/bcdice/game_system/LiveraDoll.rb +375 -0
- data/lib/bcdice/game_system/LogHorizon.rb +3567 -0
- data/lib/bcdice/game_system/LogHorizon_Korean.rb +1407 -0
- data/lib/bcdice/game_system/LostRecord.rb +30 -0
- data/lib/bcdice/game_system/LostRoyal.rb +242 -0
- data/lib/bcdice/game_system/MagicaLogia.rb +1148 -0
- data/lib/bcdice/game_system/MeikyuDays.rb +415 -0
- data/lib/bcdice/game_system/MeikyuKingdom.rb +466 -0
- data/lib/bcdice/game_system/MeikyuKingdomBasic.rb +269 -0
- data/lib/bcdice/game_system/MetalHead.rb +249 -0
- data/lib/bcdice/game_system/MetalHeadExtream.rb +917 -0
- data/lib/bcdice/game_system/MetallicGuardian.rb +31 -0
- data/lib/bcdice/game_system/MonotoneMuseum.rb +354 -0
- data/lib/bcdice/game_system/MonotoneMuseum_Korean.rb +304 -0
- data/lib/bcdice/game_system/Nechronica.rb +211 -0
- data/lib/bcdice/game_system/Nechronica_Korean.rb +160 -0
- data/lib/bcdice/game_system/NeverCloud.rb +74 -0
- data/lib/bcdice/game_system/NightWizard.rb +231 -0
- data/lib/bcdice/game_system/NightWizard3rd.rb +24 -0
- data/lib/bcdice/game_system/NightmareHunterDeep.rb +117 -0
- data/lib/bcdice/game_system/NinjaSlayer.rb +274 -0
- data/lib/bcdice/game_system/NjslyrBattle.rb +54 -0
- data/lib/bcdice/game_system/Nuekagami.rb +293 -0
- data/lib/bcdice/game_system/OneWayHeroics.rb +134 -0
- data/lib/bcdice/game_system/OracleEngine.rb +265 -0
- data/lib/bcdice/game_system/OrgaRain.rb +77 -0
- data/lib/bcdice/game_system/Oukahoushin3rd.rb +162 -0
- data/lib/bcdice/game_system/Paradiso.rb +323 -0
- data/lib/bcdice/game_system/Paranoia.rb +64 -0
- data/lib/bcdice/game_system/ParanoiaRebooted.rb +109 -0
- data/lib/bcdice/game_system/ParasiteBlood.rb +231 -0
- data/lib/bcdice/game_system/Pathfinder.rb +21 -0
- data/lib/bcdice/game_system/Peekaboo.rb +324 -0
- data/lib/bcdice/game_system/Pendragon.rb +39 -0
- data/lib/bcdice/game_system/PhantasmAdventure.rb +73 -0
- data/lib/bcdice/game_system/Postman.rb +185 -0
- data/lib/bcdice/game_system/PulpCthulhu.rb +184 -0
- data/lib/bcdice/game_system/Raisondetre.rb +171 -0
- data/lib/bcdice/game_system/RecordOfLodossWar.rb +58 -0
- data/lib/bcdice/game_system/RecordOfSteam.rb +149 -0
- data/lib/bcdice/game_system/RokumonSekai2.rb +110 -0
- data/lib/bcdice/game_system/RoleMaster.rb +24 -0
- data/lib/bcdice/game_system/RuneQuest.rb +47 -0
- data/lib/bcdice/game_system/RyuTuber.rb +266 -0
- data/lib/bcdice/game_system/Ryutama.rb +199 -0
- data/lib/bcdice/game_system/SRS.rb +348 -0
- data/lib/bcdice/game_system/SamsaraBallad.rb +111 -0
- data/lib/bcdice/game_system/Satasupe.rb +1173 -0
- data/lib/bcdice/game_system/ScreamHighSchool.rb +128 -0
- data/lib/bcdice/game_system/SevenFortressMobius.rb +34 -0
- data/lib/bcdice/game_system/ShadowRun.rb +26 -0
- data/lib/bcdice/game_system/ShadowRun4.rb +49 -0
- data/lib/bcdice/game_system/ShadowRun5.rb +80 -0
- data/lib/bcdice/game_system/SharedFantasia.rb +69 -0
- data/lib/bcdice/game_system/ShinMegamiTenseiKakuseihen.rb +85 -0
- data/lib/bcdice/game_system/ShinkuuGakuen.rb +506 -0
- data/lib/bcdice/game_system/ShinobiGami.rb +680 -0
- data/lib/bcdice/game_system/ShoujoTenrankai.rb +677 -0
- data/lib/bcdice/game_system/Skynauts.rb +324 -0
- data/lib/bcdice/game_system/SteamPunkers.rb +485 -0
- data/lib/bcdice/game_system/StellarKnights.rb +856 -0
- data/lib/bcdice/game_system/SterileLife.rb +452 -0
- data/lib/bcdice/game_system/StrangerOfSwordCity.rb +103 -0
- data/lib/bcdice/game_system/StratoShout.rb +316 -0
- data/lib/bcdice/game_system/StratoShout_Korean.rb +314 -0
- data/lib/bcdice/game_system/Strave.rb +223 -0
- data/lib/bcdice/game_system/SwordWorld.rb +491 -0
- data/lib/bcdice/game_system/SwordWorld2_0.rb +295 -0
- data/lib/bcdice/game_system/SwordWorld2_5.rb +121 -0
- data/lib/bcdice/game_system/TherapieSein.rb +93 -0
- data/lib/bcdice/game_system/TokumeiTenkousei.rb +96 -0
- data/lib/bcdice/game_system/TokyoGhostResearch.rb +122 -0
- data/lib/bcdice/game_system/TokyoNova.rb +19 -0
- data/lib/bcdice/game_system/Torg.rb +364 -0
- data/lib/bcdice/game_system/Torg1_5.rb +139 -0
- data/lib/bcdice/game_system/TorgEternity.rb +414 -0
- data/lib/bcdice/game_system/TrinitySeven.rb +364 -0
- data/lib/bcdice/game_system/TunnelsAndTrolls.rb +287 -0
- data/lib/bcdice/game_system/TwilightGunsmoke.rb +527 -0
- data/lib/bcdice/game_system/Utakaze.rb +153 -0
- data/lib/bcdice/game_system/VampireTheMasquerade5th.rb +132 -0
- data/lib/bcdice/game_system/Villaciel.rb +555 -0
- data/lib/bcdice/game_system/WARPS.rb +33 -0
- data/lib/bcdice/game_system/WaresBlade.rb +34 -0
- data/lib/bcdice/game_system/Warhammer.rb +336 -0
- data/lib/bcdice/game_system/WitchQuest.rb +275 -0
- data/lib/bcdice/game_system/WorldOfDarkness.rb +135 -0
- data/lib/bcdice/game_system/YankeeYogSothoth.rb +440 -0
- data/lib/bcdice/game_system/YearZeroEngine.rb +134 -0
- data/lib/bcdice/game_system/ZettaiReido.rb +142 -0
- data/lib/bcdice/game_system/meikyu_kingdom/item_table.rb +383 -0
- data/lib/bcdice/game_system/meikyu_kingdom/kingdom_name_table.rb +89 -0
- data/lib/bcdice/game_system/meikyu_kingdom/landscape_table.rb +196 -0
- data/lib/bcdice/game_system/meikyu_kingdom/name_tables.rb +223 -0
- data/lib/bcdice/game_system/meikyu_kingdom/placename_table.rb +214 -0
- data/lib/bcdice/game_system/meikyu_kingdom/tables.rb +610 -0
- data/lib/bcdice/game_system/meikyu_kingdom/word_table.rb +117 -0
- data/lib/bcdice/game_system/meikyu_kingdom_basic/item_table.rb +380 -0
- data/lib/bcdice/game_system/meikyu_kingdom_basic/kingdom_table.rb +184 -0
- data/lib/bcdice/game_system/meikyu_kingdom_basic/name_table.rb +417 -0
- data/lib/bcdice/game_system/meikyu_kingdom_basic/table.rb +912 -0
- data/lib/bcdice/game_system/meikyu_kingdom_basic/word_table.rb +121 -0
- data/lib/bcdice/game_system/one_way_heroics/dungeon_table.rb +67 -0
- data/lib/bcdice/game_system/one_way_heroics/random_event_table.rb +166 -0
- data/lib/bcdice/game_system/one_way_heroics/tables.rb +668 -0
- data/lib/bcdice/loader.rb +37 -0
- data/lib/bcdice/normalize.rb +38 -0
- data/lib/bcdice/preprocessor.rb +87 -0
- data/lib/bcdice/randomizer.rb +137 -0
- data/lib/bcdice/repl.rb +155 -0
- data/lib/bcdice/user_defined_dice_table.rb +153 -0
- data/lib/bcdice/version.rb +3 -0
- metadata +304 -0
@@ -0,0 +1,242 @@
|
|
1
|
+
require "bcdice/arithmetic_evaluator"
|
2
|
+
require "bcdice/normalize"
|
3
|
+
require "bcdice/format"
|
4
|
+
|
5
|
+
module BCDice
|
6
|
+
# よくある形式のコマンドのパースを補助するクラス
|
7
|
+
#
|
8
|
+
# @example Literal by String
|
9
|
+
# parser = CommandParser.new("MC")
|
10
|
+
# parsed = parser.parse("MC+2*3@30<=10/2-3") #=> <CommandParser::Parsed>
|
11
|
+
#
|
12
|
+
# parsed.command #=> "MC"
|
13
|
+
# parsed.modify_number #=> 6
|
14
|
+
# parsed.critical #=> 30
|
15
|
+
# parsed.cmp_op #=> #>=
|
16
|
+
# parsed.target_number #=> 2
|
17
|
+
#
|
18
|
+
# @example Literal by Regexp
|
19
|
+
# parser = CommandParser.new(/^RE\d+$/)
|
20
|
+
# parsed = parser.parse("RE44+20") #=> <CommandParser::Parsed>
|
21
|
+
#
|
22
|
+
# parsed.command #=> "RE44"
|
23
|
+
# parsed.modify_number #=> 20
|
24
|
+
class CommandParser < ArithmeticEvaluator
|
25
|
+
# @param literals [Array<String, Regexp>]
|
26
|
+
def initialize(*literals)
|
27
|
+
@literals = literals
|
28
|
+
@round_type = RoundType::FLOOR
|
29
|
+
@allowed_cmp_op = nil
|
30
|
+
|
31
|
+
@enabled_question_target = false
|
32
|
+
end
|
33
|
+
|
34
|
+
# パース結果
|
35
|
+
class Parsed
|
36
|
+
# @return [String]
|
37
|
+
attr_accessor :command
|
38
|
+
|
39
|
+
# @return [Integer, nil]
|
40
|
+
attr_accessor :critical
|
41
|
+
|
42
|
+
# @return [Integer, nil]
|
43
|
+
attr_accessor :fumble
|
44
|
+
|
45
|
+
# @return [Integer, nil]
|
46
|
+
attr_accessor :dollar
|
47
|
+
|
48
|
+
# @return [Integer]
|
49
|
+
attr_accessor :modify_number
|
50
|
+
|
51
|
+
# @return [Symbol, nil]
|
52
|
+
attr_accessor :cmp_op
|
53
|
+
|
54
|
+
# @return [Integer, nil]
|
55
|
+
attr_accessor :target_number
|
56
|
+
|
57
|
+
# @param value [Boolean]
|
58
|
+
# @return [Boolean]
|
59
|
+
attr_writer :question_target
|
60
|
+
|
61
|
+
def initialize
|
62
|
+
@critical = nil
|
63
|
+
@fumble = nil
|
64
|
+
@dollar = nil
|
65
|
+
@cmp_op = nil
|
66
|
+
@target_number = nil
|
67
|
+
@question_target = false
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Boolean]
|
71
|
+
def question_target?
|
72
|
+
@question_target
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param suffix_position [Symbol] クリティカルなどの表示位置
|
76
|
+
# @return [String]
|
77
|
+
def to_s(suffix_position = :after_command)
|
78
|
+
c = @critical ? "@#{@critical}" : nil
|
79
|
+
f = @fumble ? "##{@fumble}" : nil
|
80
|
+
d = @dollar ? "$#{@dollar}" : nil
|
81
|
+
m = Format.modifier(@modify_number)
|
82
|
+
target = @question_target ? "?" : @target_number
|
83
|
+
|
84
|
+
case suffix_position
|
85
|
+
when :after_command
|
86
|
+
[@command, c, f, d, m, @cmp_op, target].join()
|
87
|
+
when :after_modify_number
|
88
|
+
[@command, m, c, f, d, @cmp_op, target].join()
|
89
|
+
when :after_target_number
|
90
|
+
[@command, m, @cmp_op, target, c, f, d].join()
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# 特定の比較演算子のみ許可するようにする。
|
96
|
+
# 比較演算子なしを許可する場合には nil を指定してください。
|
97
|
+
#
|
98
|
+
# @param cmp_op [Array<Symbol, nil>] 許可する比較演算子の一覧
|
99
|
+
# @return [self]
|
100
|
+
def allow_cmp_op(*cmp_op)
|
101
|
+
@allowed_cmp_op = cmp_op
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
# 目標値 "?" を許容する
|
106
|
+
#
|
107
|
+
# @return [self]
|
108
|
+
def enable_question_target
|
109
|
+
@enabled_question_target = true
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
# 式をパースする
|
114
|
+
#
|
115
|
+
# @param expr [String]
|
116
|
+
# @param round_type [Symbol]
|
117
|
+
# @return [CommandParser::Parsed, nil]
|
118
|
+
def parse(expr, round_type = RoundType::FLOOR)
|
119
|
+
@tokens = tokenize(expr)
|
120
|
+
@idx = 0
|
121
|
+
@error = false
|
122
|
+
@round_type = round_type
|
123
|
+
|
124
|
+
@parsed = Parsed.new()
|
125
|
+
|
126
|
+
lhs()
|
127
|
+
if @error
|
128
|
+
return nil
|
129
|
+
end
|
130
|
+
|
131
|
+
@parsed.cmp_op = take_cmp_op()
|
132
|
+
rhs() if @parsed.cmp_op
|
133
|
+
|
134
|
+
if @idx < @tokens.size || @error
|
135
|
+
return nil
|
136
|
+
end
|
137
|
+
|
138
|
+
return @parsed
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
# @return [Array<String>]
|
144
|
+
def tokenize(expr)
|
145
|
+
expr.gsub(%r{[\(\)\+\-*/@#\$]|[<>!=]+}) { |e| " #{e} " }.split(" ")
|
146
|
+
end
|
147
|
+
|
148
|
+
def lhs
|
149
|
+
command = take()
|
150
|
+
unless literal?(command)
|
151
|
+
@error = true
|
152
|
+
return
|
153
|
+
end
|
154
|
+
|
155
|
+
command_suffix()
|
156
|
+
|
157
|
+
ret = 0
|
158
|
+
loop do
|
159
|
+
if consume("+")
|
160
|
+
ret += mul()
|
161
|
+
elsif consume("-")
|
162
|
+
ret -= mul()
|
163
|
+
else
|
164
|
+
break
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
command_suffix()
|
169
|
+
|
170
|
+
@parsed.command = command
|
171
|
+
@parsed.modify_number = ret
|
172
|
+
end
|
173
|
+
|
174
|
+
def command_suffix
|
175
|
+
loop do
|
176
|
+
if consume("@")
|
177
|
+
if @parsed.critical
|
178
|
+
@error = true
|
179
|
+
end
|
180
|
+
@parsed.critical = unary()
|
181
|
+
elsif consume("#")
|
182
|
+
if @parsed.fumble
|
183
|
+
@error = true
|
184
|
+
end
|
185
|
+
@parsed.fumble = unary()
|
186
|
+
elsif consume("$")
|
187
|
+
if @parsed.dollar
|
188
|
+
@error = true
|
189
|
+
end
|
190
|
+
@parsed.dollar = unary()
|
191
|
+
else
|
192
|
+
break
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def rhs
|
198
|
+
if @enabled_question_target && consume("?")
|
199
|
+
@parsed.question_target = true
|
200
|
+
@parsed.target_number = 0
|
201
|
+
else
|
202
|
+
@parsed.question_target = false
|
203
|
+
@parsed.target_number = expr()
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def literal?(command)
|
208
|
+
@literals.each do |lit|
|
209
|
+
case lit
|
210
|
+
when String
|
211
|
+
return true if command == lit
|
212
|
+
when Regexp
|
213
|
+
return true if command =~ lit
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
return false
|
218
|
+
end
|
219
|
+
|
220
|
+
def take
|
221
|
+
ret = @tokens[@idx]
|
222
|
+
@idx += 1
|
223
|
+
|
224
|
+
return ret
|
225
|
+
end
|
226
|
+
|
227
|
+
def take_cmp_op
|
228
|
+
cmp_op = Normalize.comparison_operator(take())
|
229
|
+
@error ||= denied_cmp_op?(cmp_op)
|
230
|
+
|
231
|
+
return cmp_op
|
232
|
+
end
|
233
|
+
|
234
|
+
def denied_cmp_op?(cmp_op)
|
235
|
+
if @allowed_cmp_op.nil?
|
236
|
+
false
|
237
|
+
else
|
238
|
+
!@allowed_cmp_op.include?(cmp_op)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "bcdice/common_command/add_dice"
|
2
|
+
require "bcdice/common_command/barabara_dice"
|
3
|
+
require "bcdice/common_command/calc"
|
4
|
+
require "bcdice/common_command/choice"
|
5
|
+
require "bcdice/common_command/d66_dice"
|
6
|
+
require "bcdice/common_command/reroll_dice"
|
7
|
+
require "bcdice/common_command/upper_dice"
|
8
|
+
require "bcdice/common_command/version"
|
9
|
+
|
10
|
+
module BCDice
|
11
|
+
module CommonCommand
|
12
|
+
COMMANDS = [
|
13
|
+
AddDice,
|
14
|
+
BarabaraDice,
|
15
|
+
Calc,
|
16
|
+
Choice,
|
17
|
+
D66Dice,
|
18
|
+
RerollDice,
|
19
|
+
UpperDice,
|
20
|
+
Version,
|
21
|
+
].freeze
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "bcdice/normalize"
|
2
|
+
require "bcdice/common_command/add_dice/parser"
|
3
|
+
require "bcdice/common_command/add_dice/randomizer"
|
4
|
+
|
5
|
+
module BCDice
|
6
|
+
module CommonCommand
|
7
|
+
class AddDice
|
8
|
+
PREFIX_PATTERN = /[\+\-\dD\(\[]+/.freeze
|
9
|
+
|
10
|
+
def initialize(command, randomizer, game_system)
|
11
|
+
@command = command
|
12
|
+
@bcdice = randomizer
|
13
|
+
@diceBot = game_system
|
14
|
+
|
15
|
+
@dice_list = []
|
16
|
+
@is_secret = false
|
17
|
+
end
|
18
|
+
|
19
|
+
def secret?
|
20
|
+
@is_secret
|
21
|
+
end
|
22
|
+
|
23
|
+
def eval()
|
24
|
+
parser = Parser.new(@command)
|
25
|
+
|
26
|
+
command = parser.parse()
|
27
|
+
if parser.error?
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
|
31
|
+
randomizer = Randomizer.new(@bcdice, @diceBot, command.cmp_op)
|
32
|
+
total = command.lhs.eval(randomizer)
|
33
|
+
|
34
|
+
output =
|
35
|
+
if randomizer.dice_list.size <= 1 && command.lhs.is_a?(Node::DiceRoll)
|
36
|
+
"(#{command}) > #{total}"
|
37
|
+
else
|
38
|
+
"(#{command}) > #{command.lhs.output} > #{total}"
|
39
|
+
end
|
40
|
+
|
41
|
+
dice_list = randomizer.dice_list
|
42
|
+
|
43
|
+
if command.cmp_op
|
44
|
+
dice_total = dice_list.inject(&:+)
|
45
|
+
output += @diceBot.check_result(total, dice_total, dice_list, randomizer.sides, command.cmp_op, command.rhs)
|
46
|
+
end
|
47
|
+
|
48
|
+
@is_secret = parser.secret?
|
49
|
+
|
50
|
+
return output
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,488 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BCDice
|
4
|
+
module CommonCommand
|
5
|
+
class AddDice
|
6
|
+
# 加算ロールの構文解析木のノードを格納するモジュール
|
7
|
+
module Node
|
8
|
+
# 加算ロールコマンドのノード。
|
9
|
+
#
|
10
|
+
# 目標値が設定されていない場合は +lhs+ のみを使用する。
|
11
|
+
# 目標値が設定されている場合は、+lhs+、+cmp_op+、+rhs+ を使用する。
|
12
|
+
class Command
|
13
|
+
# 左辺のノード
|
14
|
+
# @return [Object]
|
15
|
+
attr_reader :lhs
|
16
|
+
# 比較演算子
|
17
|
+
# @return [Symbol]
|
18
|
+
attr_reader :cmp_op
|
19
|
+
# 右辺のノード
|
20
|
+
# @return [Integer, String]
|
21
|
+
attr_reader :rhs
|
22
|
+
|
23
|
+
# ノードを初期化する
|
24
|
+
# @param [Object] lhs 左辺のノード
|
25
|
+
# @param [Symbol] cmp_op 比較演算子
|
26
|
+
# @param [Integer, String] rhs 右辺のノード
|
27
|
+
def initialize(lhs, cmp_op, rhs)
|
28
|
+
@lhs = lhs
|
29
|
+
@cmp_op = cmp_op
|
30
|
+
@rhs = rhs
|
31
|
+
end
|
32
|
+
|
33
|
+
# 文字列に変換する
|
34
|
+
# @return [String]
|
35
|
+
def to_s
|
36
|
+
@lhs.to_s + cmp_op_text + @rhs.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
# ノードのS式を返す
|
40
|
+
# @return [String]
|
41
|
+
def s_exp
|
42
|
+
if @cmp_op
|
43
|
+
"(Command (#{@cmp_op} #{@lhs.s_exp} #{@rhs}))"
|
44
|
+
else
|
45
|
+
"(Command #{@lhs.s_exp})"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# メッセージ中で比較演算子をどのように表示するかを返す
|
52
|
+
# @return [String]
|
53
|
+
def cmp_op_text
|
54
|
+
case @cmp_op
|
55
|
+
when :'!='
|
56
|
+
"<>"
|
57
|
+
when :==
|
58
|
+
"="
|
59
|
+
else
|
60
|
+
@cmp_op.to_s
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# 二項演算子のノード
|
66
|
+
class BinaryOp
|
67
|
+
# ノードを初期化する
|
68
|
+
# @param [Object] lhs 左のオペランドのノード
|
69
|
+
# @param [Symbol] op 演算子
|
70
|
+
# @param [Object] rhs 右のオペランドのノード
|
71
|
+
def initialize(lhs, op, rhs)
|
72
|
+
@lhs = lhs
|
73
|
+
@op = op
|
74
|
+
@rhs = rhs
|
75
|
+
end
|
76
|
+
|
77
|
+
# ノードを評価する
|
78
|
+
#
|
79
|
+
# 左右のオペランドをそれぞれ再帰的に評価した後で、演算を行う。
|
80
|
+
#
|
81
|
+
# @param [Randomizer] randomizer ランダマイザ
|
82
|
+
# @return [Integer] 評価結果
|
83
|
+
def eval(randomizer)
|
84
|
+
lhs = @lhs.eval(randomizer)
|
85
|
+
rhs = @rhs.eval(randomizer)
|
86
|
+
|
87
|
+
return calc(lhs, rhs)
|
88
|
+
end
|
89
|
+
|
90
|
+
# 文字列に変換する
|
91
|
+
# @return [String]
|
92
|
+
def to_s
|
93
|
+
"#{@lhs}#{@op}#{@rhs}"
|
94
|
+
end
|
95
|
+
|
96
|
+
# メッセージへの出力を返す
|
97
|
+
# @return [String]
|
98
|
+
def output
|
99
|
+
"#{@lhs.output}#{@op}#{@rhs.output}"
|
100
|
+
end
|
101
|
+
|
102
|
+
# ノードのS式を返す
|
103
|
+
# @return [String]
|
104
|
+
def s_exp
|
105
|
+
"(#{op_for_s_exp} #{@lhs.s_exp} #{@rhs.s_exp})"
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# 演算を行う
|
111
|
+
# @param [Integer] lhs 左のオペランド
|
112
|
+
# @param [Integer] rhs 右のオペランド
|
113
|
+
# @return [Integer] 演算の結果
|
114
|
+
def calc(lhs, rhs)
|
115
|
+
lhs.send(@op, rhs)
|
116
|
+
end
|
117
|
+
|
118
|
+
# S式で使う演算子の表現を返す
|
119
|
+
# @return [String]
|
120
|
+
def op_for_s_exp
|
121
|
+
@op
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# 除算ノードの基底クラス
|
126
|
+
#
|
127
|
+
# 定数 +ROUNDING_METHOD+ で端数処理方法を示す記号
|
128
|
+
# ( +'U'+, +'R'+, +''+ ) を定義すること。
|
129
|
+
# また、除算および端数処理を行う +divide_and_round+ メソッドを実装すること。
|
130
|
+
class DivideBase < BinaryOp
|
131
|
+
# ノードを初期化する
|
132
|
+
# @param [Object] lhs 左のオペランドのノード
|
133
|
+
# @param [Object] rhs 右のオペランドのノード
|
134
|
+
def initialize(lhs, rhs)
|
135
|
+
super(lhs, :/, rhs)
|
136
|
+
end
|
137
|
+
|
138
|
+
# 文字列に変換する
|
139
|
+
#
|
140
|
+
# 通常の結果の末尾に、端数処理方法を示す記号を付加する。
|
141
|
+
#
|
142
|
+
# @return [String]
|
143
|
+
def to_s
|
144
|
+
"#{super}#{rounding_method}"
|
145
|
+
end
|
146
|
+
|
147
|
+
# メッセージへの出力を返す
|
148
|
+
#
|
149
|
+
# 通常の結果の末尾に、端数処理方法を示す記号を付加する。
|
150
|
+
#
|
151
|
+
# @return [String]
|
152
|
+
def output
|
153
|
+
"#{super}#{rounding_method}"
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
# 端数処理方法を示す記号を返す
|
159
|
+
# @return [String]
|
160
|
+
def rounding_method
|
161
|
+
self.class::ROUNDING_METHOD
|
162
|
+
end
|
163
|
+
|
164
|
+
# S式で使う演算子の表現を返す
|
165
|
+
# @return [String]
|
166
|
+
def op_for_s_exp
|
167
|
+
"#{@op}#{rounding_method}"
|
168
|
+
end
|
169
|
+
|
170
|
+
# 演算を行う
|
171
|
+
# @param [Integer] lhs 左のオペランド
|
172
|
+
# @param [Integer] rhs 右のオペランド
|
173
|
+
# @return [Integer] 演算の結果
|
174
|
+
def calc(lhs, rhs)
|
175
|
+
if rhs.zero?
|
176
|
+
return 1
|
177
|
+
end
|
178
|
+
|
179
|
+
return divide_and_round(lhs, rhs)
|
180
|
+
end
|
181
|
+
|
182
|
+
# 除算および端数処理を行う
|
183
|
+
# @param [Integer] _dividend 被除数
|
184
|
+
# @param [Integer] _divisor 除数(0以外)
|
185
|
+
# @return [Integer]
|
186
|
+
def divide_and_round(_dividend, _divisor)
|
187
|
+
raise NotImplementedError
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# 除算(切り上げ)のノード
|
192
|
+
class DivideWithRoundingUp < DivideBase
|
193
|
+
# 端数処理方法を示す記号
|
194
|
+
ROUNDING_METHOD = "U"
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
# 除算および端数処理を行う
|
199
|
+
# @param [Integer] dividend 被除数
|
200
|
+
# @param [Integer] divisor 除数(0以外)
|
201
|
+
# @return [Integer]
|
202
|
+
def divide_and_round(dividend, divisor)
|
203
|
+
(dividend.to_f / divisor).ceil
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# 除算(四捨五入)のノード
|
208
|
+
class DivideWithRoundingOff < DivideBase
|
209
|
+
# 端数処理方法を示す記号
|
210
|
+
ROUNDING_METHOD = "R"
|
211
|
+
|
212
|
+
private
|
213
|
+
|
214
|
+
# 除算および端数処理を行う
|
215
|
+
# @param [Integer] dividend 被除数
|
216
|
+
# @param [Integer] divisor 除数(0以外)
|
217
|
+
# @return [Integer]
|
218
|
+
def divide_and_round(dividend, divisor)
|
219
|
+
(dividend.to_f / divisor).round
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# 除算(切り捨て)のノード
|
224
|
+
class DivideWithRoundingDown < DivideBase
|
225
|
+
# 端数処理方法を示す記号
|
226
|
+
ROUNDING_METHOD = ""
|
227
|
+
|
228
|
+
private
|
229
|
+
|
230
|
+
# 除算および端数処理を行う
|
231
|
+
# @param [Integer] dividend 被除数
|
232
|
+
# @param [Integer] divisor 除数(0以外)
|
233
|
+
# @return [Integer]
|
234
|
+
def divide_and_round(dividend, divisor)
|
235
|
+
dividend / divisor
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# 符号反転のノード
|
240
|
+
class Negate
|
241
|
+
# 符号反転の対象
|
242
|
+
# @return [Object]
|
243
|
+
attr_reader :body
|
244
|
+
|
245
|
+
# ノードを初期化する
|
246
|
+
# @param [Object] body 符号反転の対象
|
247
|
+
def initialize(body)
|
248
|
+
@body = body
|
249
|
+
end
|
250
|
+
|
251
|
+
# ノードを評価する
|
252
|
+
#
|
253
|
+
# 対象オペランドを再帰的に評価した後、評価結果の符号を反転する。
|
254
|
+
#
|
255
|
+
# @param [Randomizer] randomizer ランダマイザ
|
256
|
+
# @return [Integer] 評価結果
|
257
|
+
def eval(randomizer)
|
258
|
+
-@body.eval(randomizer)
|
259
|
+
end
|
260
|
+
|
261
|
+
# 文字列に変換する
|
262
|
+
# @return [String]
|
263
|
+
def to_s
|
264
|
+
"-#{@body}"
|
265
|
+
end
|
266
|
+
|
267
|
+
# メッセージへの出力を返す
|
268
|
+
# @return [String]
|
269
|
+
def output
|
270
|
+
"-#{@body.output}"
|
271
|
+
end
|
272
|
+
|
273
|
+
# ノードのS式を返す
|
274
|
+
# @return [String]
|
275
|
+
def s_exp
|
276
|
+
"(- #{@body.s_exp})"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# ダイスロールのノード
|
281
|
+
class DiceRoll
|
282
|
+
# ノードを初期化する
|
283
|
+
# @param [Number] times ダイスを振る回数のノード
|
284
|
+
# @param [Number] sides ダイスの面数のノード
|
285
|
+
def initialize(times, sides)
|
286
|
+
@times = times.literal
|
287
|
+
@sides = sides.literal
|
288
|
+
|
289
|
+
# ダイスを振った結果の出力
|
290
|
+
@text = nil
|
291
|
+
end
|
292
|
+
|
293
|
+
# ノードを評価する(ダイスを振る)
|
294
|
+
#
|
295
|
+
# 評価結果は出目の合計値になる。
|
296
|
+
# 出目はランダマイザに記録される。
|
297
|
+
#
|
298
|
+
# @param [Randomizer] randomizer ランダマイザ
|
299
|
+
# @return [Integer] 評価結果(出目の合計値)
|
300
|
+
def eval(randomizer)
|
301
|
+
dice_groups = randomizer.roll(@times, @sides)
|
302
|
+
|
303
|
+
# TODO: Ruby 2.4以降では Array#sum が使える
|
304
|
+
total = dice_groups.flatten.reduce(0, &:+)
|
305
|
+
|
306
|
+
dice_str = dice_groups
|
307
|
+
.map { |dice_list| "[#{dice_list.join(',')}]" }
|
308
|
+
.join
|
309
|
+
@text = "#{total}#{dice_str}"
|
310
|
+
|
311
|
+
return total
|
312
|
+
end
|
313
|
+
|
314
|
+
# 文字列に変換する
|
315
|
+
# @return [String]
|
316
|
+
def to_s
|
317
|
+
"#{@times}D#{@sides}"
|
318
|
+
end
|
319
|
+
|
320
|
+
# メッセージへの出力を返す
|
321
|
+
# @return [String]
|
322
|
+
def output
|
323
|
+
@text
|
324
|
+
end
|
325
|
+
|
326
|
+
# ノードのS式を返す
|
327
|
+
# @return [String]
|
328
|
+
def s_exp
|
329
|
+
"(DiceRoll #{@times} #{@sides})"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
# フィルタ処理付きダイスロールのノード。
|
334
|
+
#
|
335
|
+
# ダイスロール後、条件に従って出目を選択し、和を求める。
|
336
|
+
class DiceRollWithFilter
|
337
|
+
# フィルタの構造体
|
338
|
+
#
|
339
|
+
# 各フィルタには、あらかじめソートされた出目の配列が渡される。
|
340
|
+
#
|
341
|
+
# @!attribute abbr
|
342
|
+
# @return [Symbol] フィルタの略称
|
343
|
+
# @!attribute apply
|
344
|
+
# @return [Proc] フィルタ処理の内容
|
345
|
+
Filter = Struct.new(:abbr, :apply)
|
346
|
+
|
347
|
+
# 大きな出目から複数個取る
|
348
|
+
KEEP_HIGHEST = Filter.new(
|
349
|
+
:KH,
|
350
|
+
lambda { |sorted_values, n| sorted_values.reverse.take(n) }
|
351
|
+
).freeze
|
352
|
+
|
353
|
+
# 小さな出目から複数個取る
|
354
|
+
KEEP_LOWEST = Filter.new(
|
355
|
+
:KL,
|
356
|
+
lambda { |sorted_values, n| sorted_values.take(n) }
|
357
|
+
).freeze
|
358
|
+
|
359
|
+
# 大きな出目から複数個除く
|
360
|
+
DROP_HIGHEST = Filter.new(
|
361
|
+
:DH,
|
362
|
+
lambda { |sorted_values, n| sorted_values.reverse.drop(n) }
|
363
|
+
).freeze
|
364
|
+
|
365
|
+
# 小さな出目から複数個除く
|
366
|
+
DROP_LOWEST = Filter.new(
|
367
|
+
:DL,
|
368
|
+
lambda { |sorted_values, n| sorted_values.drop(n) }
|
369
|
+
).freeze
|
370
|
+
|
371
|
+
# ノードを初期化する
|
372
|
+
# @param [Number] times ダイスを振る回数のノード
|
373
|
+
# @param [Number] sides ダイスの面数のノード
|
374
|
+
# @param [Number] n_filtering ダイスを残す/減らす個数のノード
|
375
|
+
# @param [Filter] filter フィルタ
|
376
|
+
def initialize(times, sides, n_filtering, filter)
|
377
|
+
@times = times.literal
|
378
|
+
@sides = sides.literal
|
379
|
+
@n_filtering = n_filtering.literal
|
380
|
+
@filter = filter
|
381
|
+
|
382
|
+
# ダイスを振った結果の出力
|
383
|
+
@text = nil
|
384
|
+
end
|
385
|
+
|
386
|
+
# ノードを評価する(ダイスを振り、出目を選択して和を求める)
|
387
|
+
#
|
388
|
+
# 評価結果は出目の合計値になる。
|
389
|
+
# 出目はランダマイザに記録される。
|
390
|
+
#
|
391
|
+
# @param [Randomizer] randomizer ランダマイザ
|
392
|
+
# @return [Integer] 評価結果(出目の合計値)
|
393
|
+
def eval(randomizer)
|
394
|
+
sorted_values = randomizer.roll_once(@times, @sides).sort
|
395
|
+
total = @filter
|
396
|
+
.apply[sorted_values, @n_filtering]
|
397
|
+
.reduce(0, &:+)
|
398
|
+
|
399
|
+
@text = "#{total}[#{sorted_values.join(',')}]"
|
400
|
+
|
401
|
+
return total
|
402
|
+
end
|
403
|
+
|
404
|
+
# 文字列に変換する
|
405
|
+
# @return [String]
|
406
|
+
def to_s
|
407
|
+
"#{@times}D#{@sides}#{@filter.abbr}#{@n_filtering}"
|
408
|
+
end
|
409
|
+
|
410
|
+
# メッセージへの出力を返す
|
411
|
+
# @return [String]
|
412
|
+
def output
|
413
|
+
@text
|
414
|
+
end
|
415
|
+
|
416
|
+
# ノードのS式を返す
|
417
|
+
# @return [String]
|
418
|
+
def s_exp
|
419
|
+
"(DiceRollWithFilter #{@times} #{@sides} #{@filter.abbr.inspect} #{@n_filtering})"
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
# カッコで式をまとめるノード
|
424
|
+
class Parenthesis
|
425
|
+
# @param expr [Object] カッコ内のノード
|
426
|
+
def initialize(expr)
|
427
|
+
@expr = expr
|
428
|
+
end
|
429
|
+
|
430
|
+
# @param randomizer [Randomizer]
|
431
|
+
# @return [integer]
|
432
|
+
def eval(randomizer)
|
433
|
+
@expr.eval(randomizer)
|
434
|
+
end
|
435
|
+
|
436
|
+
# @return [String]
|
437
|
+
def to_s
|
438
|
+
"(#{@expr})"
|
439
|
+
end
|
440
|
+
|
441
|
+
# @return [String]
|
442
|
+
def output
|
443
|
+
"(#{@expr.output})"
|
444
|
+
end
|
445
|
+
|
446
|
+
# @return [String] S式
|
447
|
+
def s_exp
|
448
|
+
"(Parenthesis #{@expr.s_exp})"
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
# 数値のノード
|
453
|
+
class Number
|
454
|
+
# 値
|
455
|
+
# @return [Integer]
|
456
|
+
attr_reader :literal
|
457
|
+
|
458
|
+
# ノードを初期化する
|
459
|
+
# @param [Integer] literal 値
|
460
|
+
def initialize(literal)
|
461
|
+
@literal = literal
|
462
|
+
end
|
463
|
+
|
464
|
+
# 符号を反転した結果の数値ノードを返す
|
465
|
+
# @return [Number]
|
466
|
+
def negate
|
467
|
+
Number.new(-@literal)
|
468
|
+
end
|
469
|
+
|
470
|
+
# ノードを評価する
|
471
|
+
# @return [Integer] 格納している値
|
472
|
+
def eval(_randomizer)
|
473
|
+
@literal
|
474
|
+
end
|
475
|
+
|
476
|
+
# 文字列に変換する
|
477
|
+
# @return [String]
|
478
|
+
def to_s
|
479
|
+
@literal.to_s
|
480
|
+
end
|
481
|
+
|
482
|
+
alias output to_s
|
483
|
+
alias s_exp to_s
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
end
|