himanushi / Titan

勉強のためRustとパーサーとか学ぶ用。Developing the strongest programming language with ChatGPT. Join us!
0 stars 0 forks source link

計算機 #9

Open himanushi opened 1 year ago

himanushi commented 1 year ago

ではこのパーサーを使って計算するためのrsファイルを作成してください。

以下は、計算を行うための calc.rs ファイルの例です。このファイルでは、パーサーを使って式を解析し、その解析結果を eval 関数で評価しています。

Copy code
use crate::parser::{expr, Expr};
use nom::Finish;

pub fn calculate(input: &str) -> Result<f64, String> {
    let result = expr(input).finish();

    match result {
        Ok((_, ast)) => Ok(eval(&ast)),
        Err(e) => Err(format!("Parse error: {}", e.to_string())),
    }
}

fn eval(expr: &Expr) -> f64 {
    match expr {
        Expr::Num(num) => *num,
        Expr::Add(left, right) => eval(left) + eval(right),
        Expr::Sub(left, right) => eval(left) - eval(right),
        Expr::Mul(left, right) => eval(left) * eval(right),
        Expr::Div(left, right) => eval(left) / eval(right),
    }
}

calculate 関数は、式を受け取り、その式を評価した結果を返します。パーサーが成功した場合、パースされた式を eval 関数に渡して評価を行い、結果を返します。パーサーが失敗した場合、パースエラーのメッセージを返します。

以下は、calculate 関数を使って計算を行うための例です。

Copy code
use crate::calc::calculate;

fn main() {
    match calculate("1 + 2 * 3 - 4 / 2") {
        Ok(result) => println!("Result: {}", result),
        Err(msg) => eprintln!("Error: {}", msg),
    }
}

この例では、1 + 2 * 3 - 4 / 2 の計算結果が表示されます。

himanushi commented 1 year ago

エラーが発生しました。

error[E0432]: unresolved import crate::calc --> src/main.rs:1:12 | 1 | use crate::calc::calculate; | ^^^^ could not find calc in the crate root

For more information about this error, try rustc --explain E0432. error: could not compile titan due to previous error エラーを解決するには、calc.rs ファイルを main.rs から参照できるようにする必要があります。具体的には、calc.rs ファイルを src/ ディレクトリに移動し、main.rs ファイルの先頭で mod calc を宣言します。また、calculate 関数を pub にして、main.rs からアクセスできるようにします。

以下は、修正後の main.rs ファイルの例です。

rust Copy code mod calc; mod parser;

use calc::calculate;

fn main() { match calculate("1 + 2 * 3 - 4 / 2") { Ok(result) => println!("Result: {}", result), Err(msg) => eprintln!("Error: {}", msg), } } calc.rs ファイルの先頭には、pub を追加して calculate 関数を公開します。

rust Copy code use crate::parser::{expr, Expr}; use nom::Finish;

pub fn calculate(input: &str) -> Result<f64, String> { let result = expr(input).finish();

match result {
    Ok((_, ast)) => Ok(eval(&ast)),
    Err(e) => Err(format!("Parse error: {}", e.to_string())),
}

}

fn eval(expr: &Expr) -> f64 { match expr { Expr::Num(num) => num, Expr::Add(left, right) => eval(left) + eval(right), Expr::Sub(left, right) => eval(left) - eval(right), Expr::Mul(left, right) => eval(left) eval(right), Expr::Div(left, right) => eval(left) / eval(right), } }

himanushi commented 1 year ago

エラーが発生しました。

error[E0432]: unresolved import crate::parser::expr --> src/calc.rs:1:21 1 use crate::parser::{expr, Expr}; ^^^^
no expr in parser
help: a similar name exists in the module: Expr

エラーの内容から、parser.rs ファイルに expr という要素が含まれていないため、calc.rs ファイルで expr を使用できないことがわかります。この問題を解決するには、parser.rs ファイルで expr を宣言する必要があります。

以下は、修正後の parser.rs ファイルの例です。expr を定義し、Expr に加えて、Num, Add, Sub, Mul, Div を公開します。

rust Copy code use nom::{ branch::alt, bytes::complete::tag, character::complete::{char, digit1, multispace0}, combinator::{map, value}, multi::{fold_many0, fold_many1}, sequence::{delimited, pair, preceded, recognize, terminated, tuple}, IResult, };

[derive(Debug)]

pub enum Expr { Num(f64), Add(Box, Box), Sub(Box, Box), Mul(Box, Box), Div(Box, Box), }

pub fn expr(input: &str) -> IResult<&str, Expr> { let (input, init) = term(input)?; foldmany0( pair(alt((tag("+"), tag("-"))), term), init, |acc, (op, val): (&str, Expr)| { let next = Box::new(val); match op { "+" => Expr::Add(Box::new(acc), next), "-" => Expr::Sub(Box::new(acc), next), => unreachable!(), } }, )(input) }

fn term(input: &str) -> IResult<&str, Expr> { let (input, init) = factor(input)?; foldmany0( pair(alt((tag(""), tag("/"))), factor), init, |acc, (op, val): (&str, Expr)| { let next = Box::new(val); match op { "" => Expr::Mul(Box::new(acc), next), "/" => Expr::Div(Box::new(acc), next), => unreachable!(), } }, )(input) }

fn factor(input: &str) -> IResult<&str, Expr> { alt(( map(recognize(digit1), |s: &str| { Expr::Num(s.parse().unwrap()) }), delimited( multispace0, preceded(char('('), expr), terminated(char(')'), multispace0), ), ))(input) } また、calc.rs ファイルで Expr のインポートを変更する必要があります。use crate::parser::Expr と書き換えます。

rust Copy code use crate::parser::Expr; use nom::Finish;

pub fn calculate(input: &str) -> Result<f64, String> { let result = expr(input).finish();

match result {
    Ok((_, ast)) => Ok(eval(&ast)),
    Err(e) => Err(format!("Parse error: {}", e.to_string())),
}

}

fn eval(expr: &Expr) -> f64 { match expr { Expr::Num(num) => *num, Expr::Add(left, right) => eval