aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/parser.rs
diff options
context:
space:
mode:
author简律纯 <i@jyunko.cn>2025-09-12 04:02:02 +0800
committer简律纯 <i@jyunko.cn>2025-09-12 04:02:02 +0800
commit0288d0956330d5ac8db48b752240f723e8703929 (patch)
tree0297dee9f8166af0a856dd3a1057ad5f25f14c6a /src/parser.rs
parent5135876b5e2a6c40232414ea0b7eb875fa225cf0 (diff)
downloadOneRoll-0288d0956330d5ac8db48b752240f723e8703929.tar.gz
OneRoll-0288d0956330d5ac8db48b752240f723e8703929.zip
feat: initial basic roll features
Diffstat (limited to 'src/parser.rs')
-rw-r--r--src/parser.rs161
1 files changed, 161 insertions, 0 deletions
diff --git a/src/parser.rs b/src/parser.rs
new file mode 100644
index 0000000..8f4633e
--- /dev/null
+++ b/src/parser.rs
@@ -0,0 +1,161 @@
+use crate::errors::DiceError;
+use crate::types::{DiceModifier, DiceRoll, Expression};
+
+mod oneroll {
+ include!(concat!(env!("OUT_DIR"), "/oneroll_grammar.rs"));
+}
+
+use oneroll::{Grammar, Rule};
+use pest::Parser;
+
+pub struct DiceParser;
+
+impl DiceParser {
+ pub fn parse_expression(input: &str) -> Result<Expression, DiceError> {
+ let pairs = Grammar::parse(Rule::main, input)
+ .map_err(|e| DiceError::ParseError(e.to_string()))?;
+
+ let pair = pairs.peek().unwrap();
+ Self::parse_dice_expr(pair)
+ }
+
+ fn parse_dice_expr(pair: pest::iterators::Pair<Rule>) -> Result<Expression, DiceError> {
+ match pair.as_rule() {
+ Rule::main => {
+ // main 规则包含 dice_expr
+ let inner = pair.into_inner().next().unwrap();
+ Self::parse_dice_expr(inner)
+ }
+ Rule::dice_expr => {
+ let mut pairs = pair.into_inner();
+ let mut expr = Self::parse_dice_term(pairs.next().unwrap())?;
+
+ while let Some(pair) = pairs.next() {
+ match pair.as_rule() {
+ Rule::op => {
+ let op = pair.as_str();
+ let right = Self::parse_dice_term(pairs.next().unwrap())?;
+
+ expr = match op {
+ "+" => Expression::Add(Box::new(expr), Box::new(right)),
+ "-" => Expression::Subtract(Box::new(expr), Box::new(right)),
+ "*" => Expression::Multiply(Box::new(expr), Box::new(right)),
+ "/" => Expression::Divide(Box::new(expr), Box::new(right)),
+ "^" => Expression::Power(Box::new(expr), Box::new(right)),
+ _ => return Err(DiceError::ParseError(format!("未知操作符: {}", op))),
+ };
+ }
+ Rule::comment => {
+ let comment = Self::parse_comment(pair)?;
+ if let Some(comment_text) = comment {
+ expr = Expression::WithComment(Box::new(expr), Some(comment_text));
+ }
+ }
+ _ => {}
+ }
+ }
+ Ok(expr)
+ }
+ _ => Err(DiceError::ParseError(format!("期望骰子表达式,得到: {:?}", pair.as_rule()))),
+ }
+ }
+
+ fn parse_dice_term(pair: pest::iterators::Pair<Rule>) -> Result<Expression, DiceError> {
+ match pair.as_rule() {
+ Rule::dice_term => {
+ let inner = pair.into_inner().next().unwrap();
+ match inner.as_rule() {
+ Rule::dice_roll => Self::parse_dice_roll(inner),
+ Rule::paren_expr => {
+ let expr = Self::parse_dice_expr(inner.into_inner().next().unwrap())?;
+ Ok(Expression::Paren(Box::new(expr)))
+ }
+ Rule::number => {
+ let num = inner.as_str().parse::<i32>()
+ .map_err(|_| DiceError::ParseError("无效数字".to_string()))?;
+ Ok(Expression::Number(num))
+ }
+ _ => Err(DiceError::ParseError("无效的骰子项".to_string())),
+ }
+ }
+ _ => Err(DiceError::ParseError("期望骰子项".to_string())),
+ }
+ }
+
+ fn parse_dice_roll(pair: pest::iterators::Pair<Rule>) -> Result<Expression, DiceError> {
+ let mut pairs = pair.into_inner();
+ let count = pairs.next().unwrap().as_str().parse::<i32>()
+ .map_err(|_| DiceError::ParseError("无效的骰子数量".to_string()))?;
+ let sides = pairs.next().unwrap().as_str().parse::<i32>()
+ .map_err(|_| DiceError::ParseError("无效的骰子面数".to_string()))?;
+
+ let mut modifiers = Vec::new();
+ if let Some(modifiers_pair) = pairs.next() {
+ for modifier_pair in modifiers_pair.into_inner() {
+ let modifier = Self::parse_modifier(modifier_pair)?;
+ modifiers.push(modifier);
+ }
+ }
+
+ Ok(Expression::DiceRoll(DiceRoll {
+ count,
+ sides,
+ modifiers,
+ }))
+ }
+
+ fn parse_modifier(pair: pest::iterators::Pair<Rule>) -> Result<DiceModifier, DiceError> {
+ match pair.as_rule() {
+ Rule::modifier => {
+ let inner = pair.into_inner().next().unwrap();
+ match inner.as_rule() {
+ Rule::explode => Ok(DiceModifier::Explode),
+ Rule::reroll => {
+ let num = inner.into_inner().next().unwrap().as_str().parse::<i32>()
+ .map_err(|_| DiceError::ParseError("无效的重投数值".to_string()))?;
+ Ok(DiceModifier::Reroll(num))
+ }
+ Rule::reroll_once => {
+ let num = inner.into_inner().next().unwrap().as_str().parse::<i32>()
+ .map_err(|_| DiceError::ParseError("无效的条件重投数值".to_string()))?;
+ Ok(DiceModifier::RerollOnce(num))
+ }
+ Rule::keep_high => {
+ let num = inner.into_inner().next().unwrap().as_str().parse::<i32>()
+ .map_err(|_| DiceError::ParseError("无效的取高数值".to_string()))?;
+ Ok(DiceModifier::KeepHigh(num))
+ }
+ Rule::keep_low => {
+ let num = inner.into_inner().next().unwrap().as_str().parse::<i32>()
+ .map_err(|_| DiceError::ParseError("无效的取低数值".to_string()))?;
+ Ok(DiceModifier::KeepLow(num))
+ }
+ Rule::drop_high => {
+ let num = inner.into_inner().next().unwrap().as_str().parse::<i32>()
+ .map_err(|_| DiceError::ParseError("无效的丢弃高数值".to_string()))?;
+ Ok(DiceModifier::DropHigh(num))
+ }
+ Rule::drop_low => {
+ let num = inner.into_inner().next().unwrap().as_str().parse::<i32>()
+ .map_err(|_| DiceError::ParseError("无效的丢弃低数值".to_string()))?;
+ Ok(DiceModifier::DropLow(num))
+ }
+ _ => Err(DiceError::ParseError("未知的修饰符".to_string())),
+ }
+ }
+ _ => Err(DiceError::ParseError("期望修饰符".to_string())),
+ }
+ }
+
+ fn parse_comment(pair: pest::iterators::Pair<Rule>) -> Result<Option<String>, DiceError> {
+ match pair.as_rule() {
+ Rule::comment => {
+ let comment = pair.as_str().trim_start_matches('#').trim();
+ Ok(if comment.is_empty() { None } else { Some(comment.to_string()) })
+ }
+ _ => Ok(None),
+ }
+ }
+
+
+}