Skip to content

Instantly share code, notes, and snippets.

@ochaochaocha3
Created December 3, 2020 14:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ochaochaocha3/8f34c1ffd0c8ab6ca4d1442f54f82aa7 to your computer and use it in GitHub Desktop.
Save ochaochaocha3/8f34c1ffd0c8ab6ca4d1442f54f82aa7 to your computer and use it in GitHub Desktop.
BCDiceのLALR(1)構文解析器をまとめようとした跡(reduce/reduce conflict多発のため却下)
class BCDice::CommonCommand::Parser
token
ASTERISK
AT
BRACKETL
BRACKETR
C
CMP_OP
D
EQUAL
F
GREATER
H
K
L
LESS
MINUS
NOT
NUMBER
PARENL
PARENR
PLUS
QUESTION
R
S
SLASH
U
rule
command: add_dice
| reroll_dice
add_dice: secret add_dice_add
{
secret, lhs = val
raise ParseError unless lhs.include_dice?
result = AddDice::Node::Command.new(secret, lhs)
}
| secret add_dice_add CMP_OP add_dice_target
{
secret, lhs, cmp_op, rhs = val
raise ParseError if !lhs.include_dice? || rhs.include_dice? || cmp_op.nil?
result = AddDice::Node::Command.new(secret, lhs, cmp_op, rhs)
}
add_dice_target: add_dice_add
| QUESTION
{ result = AddDice::Node::UndecidedTarget.instance }
add_dice_add: add_dice_add PLUS add_dice_mul
{
lhs = val[0]
op, rhs = expand_negate(:+, val[2])
result = AddDice::Node::BinaryOp.new(lhs, op, rhs)
}
| add_dice_add MINUS add_dice_mul
{
lhs = val[0]
op, rhs = expand_negate(:-, val[2])
result = AddDice::Node::BinaryOp.new(lhs, op, rhs)
}
| add_dice_mul
add_dice_mul: add_dice_mul ASTERISK add_dice_unary
{
lhs = val[0]
rhs = val[2]
result = AddDice::Node::BinaryOp.new(lhs, :*, rhs)
}
| add_dice_mul SLASH add_dice_unary add_dice_round_type
{
lhs = val[0]
rhs = val[2]
divied_class = val[3]
result = divied_class.new(lhs, rhs)
}
| add_dice_unary
add_dice_round_type: /* none */
{ result = AddDice::Node::DivideWithRoundingDown }
| U
{ result = AddDice::Node::DivideWithRoundingUp }
| R
{ result = AddDice::Node::DivideWithRoundingOff }
add_dice_unary: PLUS add_dice_unary
{ result = val[1] }
| MINUS add_dice_unary
{
body = val[1]
result = body.is_a?(AddDice::Node::Negate) ? body.body : AddDice::Node::Negate.new(body)
}
| d_dice
d_dice: add_dice_term D add_dice_term
{
times = val[0]
sides = val[2]
raise ParseError if times.include_dice? || sides.include_dice?
result = AddDice::Node::DiceRoll.new(times, sides)
}
| add_dice_term D
{
times = val[0]
sides = AddDice::Node::Number.new(6)
raise ParseError if times.include_dice?
result = AddDice::Node::DiceRoll.new(times, sides)
}
| add_dice_term D add_dice_term add_dice_filter_type add_dice_term
{
times = val[0]
sides = val[2]
filter = val[3]
n_filtering = val[4]
raise ParseError if times.include_dice? || sides.include_dice? || n_filtering.include_dice?
result = AddDice::Node::DiceRollWithFilter.new(times, sides, n_filtering, filter)
}
| add_dice_term
add_dice_filter_type: K H
{ result = AddDice::Node::DiceRollWithFilter::KEEP_HIGHEST }
| K L
{ result = AddDice::Node::DiceRollWithFilter::KEEP_LOWEST }
| D H
{ result = AddDice::Node::DiceRollWithFilter::DROP_HIGHEST }
| D L
{ result = AddDice::Node::DiceRollWithFilter::DROP_LOWEST }
add_dice_term: PARENL add_dice_add PARENR
{ result = AddDice::Node::Parenthesis.new(val[1]) }
| NUMBER
{ result = AddDice::Node::Number.new(val[0]) }
reroll_dice: secret reroll_dice_notations reroll_dice_target
{
result = RerollDice::Node::Command.new(
secret: val[0],
notations: val[1],
cmp_op: val[2][:cmp_op],
target_number: val[2][:target],
source: @lexer.source
)
}
| secret reroll_dice_notations bracket reroll_dice_target
{
target = val[3]
threshold = val[2]
result = RerollDice::Node::Command.new(
secret: val[0],
notations: val[1],
cmp_op: target[:cmp_op],
target_number: target[:target],
reroll_cmp_op: threshold[:cmp_op],
reroll_threshold: threshold[:threshold],
source: @lexer.source
)
}
| secret reroll_dice_notations reroll_dice_target at
{
target = val[2]
threshold = val[3]
result = RerollDice::Node::Command.new(
secret: val[0],
notations: val[1],
cmp_op: target[:cmp_op],
target_number: target[:target],
reroll_cmp_op: threshold[:cmp_op],
reroll_threshold: threshold[:threshold],
source: @lexer.source
)
}
reroll_dice_target: /* none */
{ result = {} }
| CMP_OP arithmetic_term
{
cmp_op, target = val
raise ParseError unless cmp_op
result = {cmp_op: cmp_op, target: target}
}
bracket: BRACKETL arithmetic_add BRACKETR
{ result = {threshold: val[1]} }
| BRACKETL CMP_OP arithmetic_add BRACKETR
{
cmp_op = val[1]
threshold = val[2]
raise ParseError unless cmp_op
result = {cmp_op: cmp_op, threshold: threshold}
}
at: AT arithmetic_add
{ result = {threshold: val[1]} }
| AT CMP_OP arithmetic_add
{
cmp_op = val[1]
threshold = val[2]
raise ParseError unless cmp_op
result = {cmp_op: cmp_op, threshold: threshold}
}
reroll_dice_notations: reroll_dice_notations PLUS r_dice
{
notations = val[0]
notations.push(val[2])
result = notations
}
| r_dice
{ result = [val[0]] }
r_dice: arithmetic_term R arithmetic_term
{
times = val[0]
sides = val[2]
result = RerollDice::Node::Notation.new(times, sides)
}
secret: /* none */
{ result = false }
| S
{ result = true }
arithmetic_add: arithmetic_add PLUS arithmetic_mul
{ result = Arithmetic::Node::BinaryOp(val[0], :+, val[2]) }
| arithmetic_add MINUS arithmetic_mul
{ result = Arithmetic::Node::BinaryOp(val[0], :-, val[2]) }
| arithmetic_mul
arithmetic_mul: arithmetic_mul ASTERISK arithmetic_unary
{ result = Arithmetic::Node::BinaryOp(val[0], :*, val[2]) }
| arithmetic_mul SLASH arithmetic_unary arithmetic_round_type
{
divied_class = val[3]
result = divied_class.new(val[0], val[2])
}
| arithmetic_unary
arithmetic_round_type: /* none */
{ result = Arithmetic::Node::DivideWithGameSystemDefault }
| U
{ result = Arithmetic::Node::DivideWithCeil }
| C
{ result = Arithmetic::Node::DivideWithCeil }
| R
{ result = Arithmetic::Node::DivideWithRound }
| F
{ result = Arithmetic::Node::DivideWithFloor }
arithmetic_unary: PLUS arithmetic_unary
{ result = val[1] }
| MINUS arithmetic_unary
{ result = Arithmetic::Node::Negative.new(val[1]) }
| arithmetic_term
arithmetic_term: PARENL arithmetic_add PARENR
{ result = val[1] }
| NUMBER
{ result = Arithmetic::Node::Number.new(val[0]) }
end
---- header
require "bcdice/common_command/lexer"
require "bcdice/common_command/add_dice/node"
require "bcdice/common_command/reroll_dice/node"
require "bcdice/arithmetic/node"
---- inner
def self.parse(source)
new.parse(source)
end
def parse(source)
@lexer = Lexer.new(source)
do_parse()
rescue ParseError
nil
end
private
def next_token
@lexer.next_token
end
# 加減算の右辺が負数である場合に加減算を逆転させる
def expand_negate(op, rhs)
if rhs.is_a?(AddDice::Node::Negate)
if op == :+
return [:-, rhs.body]
elsif op == :-
return [:+, rhs.body]
end
end
[op, rhs]
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment