Memo

メモ > 技術 > プログラミング言語: Rust > プログラミング言語(インタプリタ)

プログラミング言語(インタプリタ)
Rustで作るプログラミング言語 ―コンパイラ/インタプリタの基礎からプログラミング言語の新潮流まで:書籍案内|技術評論社 https://gihyo.jp/book/2024/978-4-297-14192-9 GitHub - msakuta/ruscal: Programming language implementation learning project https://github.com/msakuta/ruscal ■文の導入 src/main.rs の内容を以下のようにする。
use std::io::Read; use nom::{ branch::alt, bytes::complete::tag, character::complete::{ alpha1, alphanumeric1, char, multispace0, }, combinator::{opt, recognize}, error::ParseError, multi::{fold_many0, many0, separated_list0}, number::complete::recognize_float, sequence::{delimited, pair}, Finish, IResult, Parser, }; fn main() { let mut buf = String::new(); if std::io::stdin().read_to_string(&mut buf).is_ok() { let parsed_statements = match statements(&buf) { Ok(parsed_statements) => parsed_statements, Err(e) => { eprintln!("Parse error: {e:?}"); return; } }; for statement in parsed_statements { println!("eval: {:?}", eval(statement)); } } } #[derive(Debug, PartialEq, Clone)] enum Expression<'src> { Ident(&'src str), NumLiteral(f64), FnInvoke(&'src str, Vec<Expression<'src>>), Add(Box<Expression<'src>>, Box<Expression<'src>>), Sub(Box<Expression<'src>>, Box<Expression<'src>>), Mul(Box<Expression<'src>>, Box<Expression<'src>>), Div(Box<Expression<'src>>, Box<Expression<'src>>), } type Statements<'a> = Vec<Expression<'a>>; fn unary_fn( f: fn(f64) -> f64, ) -> impl Fn(Vec<Expression>) -> f64 { move |args| { f(eval( args.into_iter().next().expect("function missing argument"), )) } } fn binary_fn( f: fn(f64, f64) -> f64, ) -> impl Fn(Vec<Expression>) -> f64 { move |args| { let mut args = args.into_iter(); let lhs = eval( args.next().expect("function missing the first argument"), ); let rhs = eval( args.next().expect("function missing the second argument"), ); f(lhs, rhs) } } fn eval(expr: Expression) -> f64 { use Expression::*; match expr { Ident("pi") => std::f64::consts::PI, Ident(id) => panic!("Unknown name {:?}", id), NumLiteral(n) => n, FnInvoke("sqrt", args) => unary_fn(f64::sqrt)(args), FnInvoke("sin", args) => unary_fn(f64::sin)(args), FnInvoke("cos", args) => unary_fn(f64::cos)(args), FnInvoke("tan", args) => unary_fn(f64::tan)(args), FnInvoke("asin", args) => unary_fn(f64::asin)(args), FnInvoke("acos", args) => unary_fn(f64::acos)(args), FnInvoke("atan", args) => unary_fn(f64::atan)(args), FnInvoke("atan2", args) => binary_fn(f64::atan2)(args), FnInvoke("pow", args) => binary_fn(f64::powf)(args), FnInvoke("exp", args) => unary_fn(f64::exp)(args), FnInvoke("log", args) => binary_fn(f64::log)(args), FnInvoke("log10", args) => unary_fn(f64::log10)(args), FnInvoke(name, _) => { panic!("Unknown function {name:?}") }, Add(lhs, rhs) => eval(*lhs) + eval(*rhs), Sub(lhs, rhs) => eval(*lhs) - eval(*rhs), Mul(lhs, rhs) => eval(*lhs) * eval(*rhs), Div(lhs, rhs) => eval(*lhs) / eval(*rhs), } } fn space_delimited<'src, O, E>( f: impl Parser<&'src str, O, E>, ) -> impl FnMut(&'src str) -> IResult<&'src str, O, E> where E: ParseError<&'src str>, { delimited(multispace0, f, multispace0) } fn factor(i: &str) -> IResult<&str, Expression> { alt((number, func_call, ident, parens))(i) } fn func_call(i: &str) -> IResult<&str, Expression> { let (r, ident) = space_delimited(identifier)(i)?; let (r, args) = space_delimited(delimited( tag("("), many0(delimited( multispace0, expr, space_delimited(opt(tag(","))), )), tag(")"), ))(r)?; Ok((r, Expression::FnInvoke(ident, args))) } fn ident(input: &str) -> IResult<&str, Expression> { let (r, res) = space_delimited(identifier)(input)?; Ok((r, Expression::Ident(res))) } fn identifier(input: &str) -> IResult<&str, &str> { recognize(pair( alt((alpha1, tag("_"))), many0(alt((alphanumeric1, tag("_")))), ))(input) } fn number(input: &str) -> IResult<&str, Expression> { let (r, v) = space_delimited(recognize_float)(input)?; Ok(( r, Expression::NumLiteral(v.parse().map_err( |_| { nom::Err::Error(nom::error::Error { input, code: nom::error::ErrorKind::Digit, }) }, )?), )) } fn parens(i: &str) -> IResult<&str, Expression> { space_delimited( delimited(tag("("), expr, tag(")")) )(i) } fn term(i: &str) -> IResult<&str, Expression> { let (i, init) = factor(i)?; fold_many0( pair(space_delimited(alt((char('*'), char('/')))), factor), move || init.clone(), |acc, (op, val): (char, Expression)| { match op { '*' => Expression::Mul(Box::new(acc), Box::new(val)), '/' => Expression::Div(Box::new(acc), Box::new(val)), _ => panic!( "Multiplicative expression should have '*' or '/' operator" ), } }, )(i) } fn expr(i: &str) -> IResult<&str, Expression> { let (i, init) = term(i)?; fold_many0( pair(space_delimited(alt((char('+'), char('-')))), term), move || init.clone(), |acc, (op, val): (char, Expression)| match op { '+' => Expression::Add(Box::new(acc), Box::new(val)), '-' => Expression::Sub(Box::new(acc), Box::new(val)), _ => panic!( "Additive expression should have '+' or '-' operator" ), }, )(i) } fn statements(i: &str) -> Result<Statements, nom::error::Error<&str>> { let (_, res) = separated_list0(tag(";"), expr)(i).finish()?; Ok(res) }
input.rscl を作成し、内容を以下のようにする。
1; 2+3; 4*5; sin(pi/2);
以下のように実行できる。 >cargo run -- < input.rscl eval: 1.0 eval: 5.0 eval: 20.0 eval: 1.0 プログラム内にあるfinish関数は、パーサーが成功した場合に残りの入力が無いかを確認するもの。 未処理の入力が残っていたら、パースが失敗したとみなされる。 処理結果として「残りの入力」と「パース下結果」を返しているが、残りの入力は(成功なら空なので)無視している。 ■変数の宣言 src/main.rs の内容を以下のようにする。
use std::{ collections::HashMap, io::Read }; use nom::{ branch::alt, bytes::complete::tag, character::complete::{ alpha1, alphanumeric1, char, multispace0, multispace1, }, combinator::{opt, recognize}, error::ParseError, multi::{fold_many0, many0, separated_list0}, number::complete::recognize_float, sequence::{delimited, pair}, Finish, IResult, Parser, }; fn main() { let mut buf = String::new(); if !std::io::stdin().read_to_string(&mut buf).is_ok() { panic!("Failed to read from stdin"); } let parsed_statements = match statements(&buf) { Ok(parsed_statements) => parsed_statements, Err(e) => { eprintln!("Parse error: {e:?}"); return; } }; let mut variables = HashMap::new(); for statement in parsed_statements { match statement { Statement::Expression(expr) => { println!("eval: {:?}", eval(expr, &variables)) } Statement::VarDef(name, expr) => { let value = eval(expr, &variables); variables.insert(name, value); } } } } #[derive(Debug, PartialEq, Clone)] enum Expression<'src> { Ident(&'src str), NumLiteral(f64), FnInvoke(&'src str, Vec<Expression<'src>>), Add(Box<Expression<'src>>, Box<Expression<'src>>), Sub(Box<Expression<'src>>, Box<Expression<'src>>), Mul(Box<Expression<'src>>, Box<Expression<'src>>), Div(Box<Expression<'src>>, Box<Expression<'src>>), } #[derive(Debug, PartialEq, Clone)] enum Statement<'src> { Expression(Expression<'src>), VarDef(&'src str, Expression<'src>), } type Statements<'a> = Vec<Statement<'a>>; fn unary_fn( f: fn(f64) -> f64, ) -> impl Fn(Vec<Expression>, &HashMap<&str, f64>) -> f64 { move |args, variables| { f(eval( args.into_iter().next().expect("function missing argument"), variables, )) } } fn binary_fn( f: fn(f64, f64) -> f64, ) -> impl Fn(Vec<Expression>, &HashMap<&str, f64>) -> f64 { move |args, variables| { let mut args = args.into_iter(); let lhs = eval( args.next().expect("function missing the first argument"), variables, ); let rhs = eval( args.next().expect("function missing the second argument"), variables, ); f(lhs, rhs) } } fn eval(expr: Expression, vars: &HashMap<&str, f64>) -> f64 { use Expression::*; match expr { Ident("pi") => std::f64::consts::PI, Ident(id) => *vars.get(id).expect("Variable not found"), NumLiteral(n) => n, FnInvoke("sqrt", args) => unary_fn(f64::sqrt)(args, vars), FnInvoke("sin", args) => unary_fn(f64::sin)(args, vars), FnInvoke("cos", args) => unary_fn(f64::cos)(args, vars), FnInvoke("tan", args) => unary_fn(f64::tan)(args, vars), FnInvoke("asin", args) => unary_fn(f64::asin)(args, vars), FnInvoke("acos", args) => unary_fn(f64::acos)(args, vars), FnInvoke("atan", args) => unary_fn(f64::atan)(args, vars), FnInvoke("atan2", args) => binary_fn(f64::atan2)(args, vars), FnInvoke("pow", args) => binary_fn(f64::powf)(args, vars), FnInvoke("exp", args) => unary_fn(f64::exp)(args, vars), FnInvoke("log", args) => binary_fn(f64::log)(args, vars), FnInvoke("log10", args) => unary_fn(f64::log10)(args, vars), FnInvoke(name, _) => { panic!("Unknown function {name:?}") }, Add(lhs, rhs) => eval(*lhs, vars) + eval(*rhs, vars), Sub(lhs, rhs) => eval(*lhs, vars) - eval(*rhs, vars), Mul(lhs, rhs) => eval(*lhs, vars) * eval(*rhs, vars), Div(lhs, rhs) => eval(*lhs, vars) / eval(*rhs, vars), } } fn space_delimited<'src, O, E>( f: impl Parser<&'src str, O, E>, ) -> impl FnMut(&'src str) -> IResult<&'src str, O, E> where E: ParseError<&'src str>, { delimited(multispace0, f, multispace0) } fn factor(i: &str) -> IResult<&str, Expression> { alt((number, func_call, ident, parens))(i) } fn func_call(i: &str) -> IResult<&str, Expression> { let (r, ident) = space_delimited(identifier)(i)?; let (r, args) = space_delimited(delimited( tag("("), many0(delimited( multispace0, expr, space_delimited(opt(tag(","))), )), tag(")"), ))(r)?; Ok((r, Expression::FnInvoke(ident, args))) } fn ident(input: &str) -> IResult<&str, Expression> { let (r, res) = space_delimited(identifier)(input)?; Ok((r, Expression::Ident(res))) } fn identifier(input: &str) -> IResult<&str, &str> { recognize(pair( alt((alpha1, tag("_"))), many0(alt((alphanumeric1, tag("_")))), ))(input) } fn number(input: &str) -> IResult<&str, Expression> { let (r, v) = space_delimited(recognize_float)(input)?; Ok(( r, Expression::NumLiteral(v.parse().map_err( |_| { nom::Err::Error(nom::error::Error { input, code: nom::error::ErrorKind::Digit, }) }, )?), )) } fn parens(i: &str) -> IResult<&str, Expression> { space_delimited( delimited(tag("("), expr, tag(")")) )(i) } fn term(i: &str) -> IResult<&str, Expression> { let (i, init) = factor(i)?; fold_many0( pair(space_delimited(alt((char('*'), char('/')))), factor), move || init.clone(), |acc, (op, val): (char, Expression)| { match op { '*' => Expression::Mul(Box::new(acc), Box::new(val)), '/' => Expression::Div(Box::new(acc), Box::new(val)), _ => panic!( "Multiplicative expression should have '*' or '/' operator" ), } }, )(i) } fn expr(i: &str) -> IResult<&str, Expression> { let (i, init) = term(i)?; fold_many0( pair(space_delimited(alt((char('+'), char('-')))), term), move || init.clone(), |acc, (op, val): (char, Expression)| match op { '+' => Expression::Add(Box::new(acc), Box::new(val)), '-' => Expression::Sub(Box::new(acc), Box::new(val)), _ => panic!( "Additive expression should have '+' or '-' operator" ), }, )(i) } fn var_def(i: &str) -> IResult<&str, Statement> { let (i, _) = delimited(multispace0, tag("var"), multispace1)(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarDef(name, expr))) } fn expr_statement(i: &str) -> IResult<&str, Statement> { let (i, res) = expr(i)?; Ok((i, Statement::Expression(res))) } fn statement(i: &str) -> IResult<&str, Statement> { alt((var_def, expr_statement))(i) } fn statements(i: &str) -> Result<Statements, nom::error::Error<&str>> { let (_, res) = separated_list0(tag(";"), statement)(i).finish()?; Ok(res) }
input.rscl を作成し、内容を以下のようにする。
var a = 1; a + 2;
以下のように実行できる。 >cargo run -- < input.rscl eval: 3.0 ■変数への代入 src/main.rs の内容を以下のようにする。
use std::{ collections::HashMap, io::Read }; use nom::{ branch::alt, bytes::complete::tag, character::complete::{ alpha1, alphanumeric1, char, multispace0, multispace1, }, combinator::{opt, recognize}, error::ParseError, multi::{fold_many0, many0, separated_list0}, number::complete::recognize_float, sequence::{delimited, pair}, Finish, IResult, Parser, }; fn main() { let mut buf = String::new(); if !std::io::stdin().read_to_string(&mut buf).is_ok() { panic!("Failed to read from stdin"); } let parsed_statements = match statements(&buf) { Ok(parsed_statements) => parsed_statements, Err(e) => { eprintln!("Parse error: {e:?}"); return; } }; let mut variables = HashMap::new(); for statement in parsed_statements { match statement { Statement::Expression(expr) => { println!("eval: {:?}", eval(expr, &variables)) } Statement::VarDef(name, expr) => { let value = eval(expr, &variables); variables.insert(name, value); } Statement::VarAssign(name, expr) => { if !variables.contains_key(name) { panic!("Variable is not defined"); } let value = eval(expr, &variables); variables.insert(name, value); } } } } #[derive(Debug, PartialEq, Clone)] enum Expression<'src> { Ident(&'src str), NumLiteral(f64), FnInvoke(&'src str, Vec<Expression<'src>>), Add(Box<Expression<'src>>, Box<Expression<'src>>), Sub(Box<Expression<'src>>, Box<Expression<'src>>), Mul(Box<Expression<'src>>, Box<Expression<'src>>), Div(Box<Expression<'src>>, Box<Expression<'src>>), } #[derive(Debug, PartialEq, Clone)] enum Statement<'src> { Expression(Expression<'src>), VarDef(&'src str, Expression<'src>), VarAssign(&'src str, Expression<'src>), } type Statements<'a> = Vec<Statement<'a>>; fn unary_fn( f: fn(f64) -> f64, ) -> impl Fn(Vec<Expression>, &HashMap<&str, f64>) -> f64 { move |args, variables| { f(eval( args.into_iter().next().expect("function missing argument"), variables, )) } } fn binary_fn( f: fn(f64, f64) -> f64, ) -> impl Fn(Vec<Expression>, &HashMap<&str, f64>) -> f64 { move |args, variables| { let mut args = args.into_iter(); let lhs = eval( args.next().expect("function missing the first argument"), variables, ); let rhs = eval( args.next().expect("function missing the second argument"), variables, ); f(lhs, rhs) } } fn eval(expr: Expression, vars: &HashMap<&str, f64>) -> f64 { use Expression::*; match expr { Ident("pi") => std::f64::consts::PI, Ident(id) => *vars.get(id).expect("Variable not found"), NumLiteral(n) => n, FnInvoke("sqrt", args) => unary_fn(f64::sqrt)(args, vars), FnInvoke("sin", args) => unary_fn(f64::sin)(args, vars), FnInvoke("cos", args) => unary_fn(f64::cos)(args, vars), FnInvoke("tan", args) => unary_fn(f64::tan)(args, vars), FnInvoke("asin", args) => unary_fn(f64::asin)(args, vars), FnInvoke("acos", args) => unary_fn(f64::acos)(args, vars), FnInvoke("atan", args) => unary_fn(f64::atan)(args, vars), FnInvoke("atan2", args) => binary_fn(f64::atan2)(args, vars), FnInvoke("pow", args) => binary_fn(f64::powf)(args, vars), FnInvoke("exp", args) => unary_fn(f64::exp)(args, vars), FnInvoke("log", args) => binary_fn(f64::log)(args, vars), FnInvoke("log10", args) => unary_fn(f64::log10)(args, vars), FnInvoke(name, _) => { panic!("Unknown function {name:?}") }, Add(lhs, rhs) => eval(*lhs, vars) + eval(*rhs, vars), Sub(lhs, rhs) => eval(*lhs, vars) - eval(*rhs, vars), Mul(lhs, rhs) => eval(*lhs, vars) * eval(*rhs, vars), Div(lhs, rhs) => eval(*lhs, vars) / eval(*rhs, vars), } } fn space_delimited<'src, O, E>( f: impl Parser<&'src str, O, E>, ) -> impl FnMut(&'src str) -> IResult<&'src str, O, E> where E: ParseError<&'src str>, { delimited(multispace0, f, multispace0) } fn factor(i: &str) -> IResult<&str, Expression> { alt((number, func_call, ident, parens))(i) } fn func_call(i: &str) -> IResult<&str, Expression> { let (r, ident) = space_delimited(identifier)(i)?; let (r, args) = space_delimited(delimited( tag("("), many0(delimited( multispace0, expr, space_delimited(opt(tag(","))), )), tag(")"), ))(r)?; Ok((r, Expression::FnInvoke(ident, args))) } fn ident(input: &str) -> IResult<&str, Expression> { let (r, res) = space_delimited(identifier)(input)?; Ok((r, Expression::Ident(res))) } fn identifier(input: &str) -> IResult<&str, &str> { recognize(pair( alt((alpha1, tag("_"))), many0(alt((alphanumeric1, tag("_")))), ))(input) } fn number(input: &str) -> IResult<&str, Expression> { let (r, v) = space_delimited(recognize_float)(input)?; Ok(( r, Expression::NumLiteral(v.parse().map_err( |_| { nom::Err::Error(nom::error::Error { input, code: nom::error::ErrorKind::Digit, }) }, )?), )) } fn parens(i: &str) -> IResult<&str, Expression> { space_delimited( delimited(tag("("), expr, tag(")")) )(i) } fn term(i: &str) -> IResult<&str, Expression> { let (i, init) = factor(i)?; fold_many0( pair(space_delimited(alt((char('*'), char('/')))), factor), move || init.clone(), |acc, (op, val): (char, Expression)| { match op { '*' => Expression::Mul(Box::new(acc), Box::new(val)), '/' => Expression::Div(Box::new(acc), Box::new(val)), _ => panic!( "Multiplicative expression should have '*' or '/' operator" ), } }, )(i) } fn expr(i: &str) -> IResult<&str, Expression> { let (i, init) = term(i)?; fold_many0( pair(space_delimited(alt((char('+'), char('-')))), term), move || init.clone(), |acc, (op, val): (char, Expression)| match op { '+' => Expression::Add(Box::new(acc), Box::new(val)), '-' => Expression::Sub(Box::new(acc), Box::new(val)), _ => panic!( "Additive expression should have '+' or '-' operator" ), }, )(i) } fn var_def(i: &str) -> IResult<&str, Statement> { let (i, _) = delimited(multispace0, tag("var"), multispace1)(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarDef(name, expr))) } fn var_assign(i: &str) -> IResult<&str, Statement> { let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarAssign(name, expr))) } fn expr_statement(i: &str) -> IResult<&str, Statement> { let (i, res) = expr(i)?; Ok((i, Statement::Expression(res))) } fn statement(i: &str) -> IResult<&str, Statement> { alt((var_def, var_assign, expr_statement))(i) } fn statements(i: &str) -> Result<Statements, nom::error::Error<&str>> { let (_, res) = separated_list0(tag(";"), statement)(i).finish()?; Ok(res) }
input.rscl を作成し、内容を以下のようにする。
var a = 1; a = a + 2; a * 2;
以下のように実行できる。 >cargo run -- < input.rscl eval: 6.0 ■条件分岐 src/main.rs の内容を以下のようにする。
use std::{ collections::HashMap, io::Read }; use nom::{ branch::alt, bytes::complete::tag, character::complete::{ alpha1, alphanumeric1, char, multispace0, multispace1, }, combinator::{opt, recognize}, error::ParseError, multi::{fold_many0, many0, separated_list0}, number::complete::recognize_float, sequence::{delimited, pair, preceded}, Finish, IResult, Parser, }; fn main() { let mut buf = String::new(); if !std::io::stdin().read_to_string(&mut buf).is_ok() { panic!("Failed to read from stdin"); } let parsed_statements = match statements(&buf) { Ok(parsed_statements) => parsed_statements, Err(e) => { eprintln!("Parse error: {e:?}"); return; } }; let mut variables = HashMap::new(); for statement in parsed_statements { match statement { Statement::Expression(expr) => { println!("eval: {:?}", eval(expr, &variables)) } Statement::VarDef(name, expr) => { let value = eval(expr, &variables); variables.insert(name, value); } Statement::VarAssign(name, expr) => { if !variables.contains_key(name) { panic!("Variable is not defined"); } let value = eval(expr, &variables); variables.insert(name, value); } } } } #[derive(Debug, PartialEq, Clone)] enum Expression<'src> { Ident(&'src str), NumLiteral(f64), FnInvoke(&'src str, Vec<Expression<'src>>), Add(Box<Expression<'src>>, Box<Expression<'src>>), Sub(Box<Expression<'src>>, Box<Expression<'src>>), Mul(Box<Expression<'src>>, Box<Expression<'src>>), Div(Box<Expression<'src>>, Box<Expression<'src>>), If( Box<Expression<'src>>, Box<Expression<'src>>, Option<Box<Expression<'src>>>, ), } #[derive(Debug, PartialEq, Clone)] enum Statement<'src> { Expression(Expression<'src>), VarDef(&'src str, Expression<'src>), VarAssign(&'src str, Expression<'src>), } type Statements<'a> = Vec<Statement<'a>>; fn unary_fn( f: fn(f64) -> f64, ) -> impl Fn(Vec<Expression>, &HashMap<&str, f64>) -> f64 { move |args, variables| { f(eval( args.into_iter().next().expect("function missing argument"), variables, )) } } fn binary_fn( f: fn(f64, f64) -> f64, ) -> impl Fn(Vec<Expression>, &HashMap<&str, f64>) -> f64 { move |args, variables| { let mut args = args.into_iter(); let lhs = eval( args.next().expect("function missing the first argument"), variables, ); let rhs = eval( args.next().expect("function missing the second argument"), variables, ); f(lhs, rhs) } } fn eval(expr: Expression, vars: &HashMap<&str, f64>) -> f64 { use Expression::*; match expr { Ident("pi") => std::f64::consts::PI, Ident(id) => *vars.get(id).expect("Variable not found"), NumLiteral(n) => n, FnInvoke("sqrt", args) => unary_fn(f64::sqrt)(args, vars), FnInvoke("sin", args) => unary_fn(f64::sin)(args, vars), FnInvoke("cos", args) => unary_fn(f64::cos)(args, vars), FnInvoke("tan", args) => unary_fn(f64::tan)(args, vars), FnInvoke("asin", args) => unary_fn(f64::asin)(args, vars), FnInvoke("acos", args) => unary_fn(f64::acos)(args, vars), FnInvoke("atan", args) => unary_fn(f64::atan)(args, vars), FnInvoke("atan2", args) => binary_fn(f64::atan2)(args, vars), FnInvoke("pow", args) => binary_fn(f64::powf)(args, vars), FnInvoke("exp", args) => unary_fn(f64::exp)(args, vars), FnInvoke("log", args) => binary_fn(f64::log)(args, vars), FnInvoke("log10", args) => unary_fn(f64::log10)(args, vars), FnInvoke(name, _) => { panic!("Unknown function {name:?}") }, Add(lhs, rhs) => eval(*lhs, vars) + eval(*rhs, vars), Sub(lhs, rhs) => eval(*lhs, vars) - eval(*rhs, vars), Mul(lhs, rhs) => eval(*lhs, vars) * eval(*rhs, vars), Div(lhs, rhs) => eval(*lhs, vars) / eval(*rhs, vars), If(cond, t_case, f_case) => { if eval(*cond, vars) != 0. { eval(*t_case, vars) } else if let Some(f_case) = f_case { eval(*f_case, vars) } else { 0. } } } } fn space_delimited<'src, O, E>( f: impl Parser<&'src str, O, E>, ) -> impl FnMut(&'src str) -> IResult<&'src str, O, E> where E: ParseError<&'src str>, { delimited(multispace0, f, multispace0) } fn factor(i: &str) -> IResult<&str, Expression> { alt((number, func_call, ident, parens))(i) } fn func_call(i: &str) -> IResult<&str, Expression> { let (r, ident) = space_delimited(identifier)(i)?; let (r, args) = space_delimited(delimited( tag("("), many0(delimited( multispace0, expr, space_delimited(opt(tag(","))), )), tag(")"), ))(r)?; Ok((r, Expression::FnInvoke(ident, args))) } fn ident(input: &str) -> IResult<&str, Expression> { let (r, res) = space_delimited(identifier)(input)?; Ok((r, Expression::Ident(res))) } fn identifier(input: &str) -> IResult<&str, &str> { recognize(pair( alt((alpha1, tag("_"))), many0(alt((alphanumeric1, tag("_")))), ))(input) } fn number(input: &str) -> IResult<&str, Expression> { let (r, v) = space_delimited(recognize_float)(input)?; Ok(( r, Expression::NumLiteral(v.parse().map_err( |_| { nom::Err::Error(nom::error::Error { input, code: nom::error::ErrorKind::Digit, }) }, )?), )) } fn parens(i: &str) -> IResult<&str, Expression> { space_delimited( delimited(tag("("), expr, tag(")")) )(i) } fn term(i: &str) -> IResult<&str, Expression> { let (i, init) = factor(i)?; fold_many0( pair(space_delimited(alt((char('*'), char('/')))), factor), move || init.clone(), |acc, (op, val): (char, Expression)| { match op { '*' => Expression::Mul(Box::new(acc), Box::new(val)), '/' => Expression::Div(Box::new(acc), Box::new(val)), _ => panic!( "Multiplicative expression should have '*' or '/' operator" ), } }, )(i) } fn num_expr(i: &str) -> IResult<&str, Expression> { let (i, init) = term(i)?; fold_many0( pair(space_delimited(alt((char('+'), char('-')))), term), move || init.clone(), |acc, (op, val): (char, Expression)| match op { '+' => Expression::Add(Box::new(acc), Box::new(val)), '-' => Expression::Sub(Box::new(acc), Box::new(val)), _ => panic!( "Additive expression should have '+' or '-' operator" ), }, )(i) } fn open_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('{'))(i)?; Ok((i, ())) } fn close_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('}'))(i)?; Ok((i, ())) } fn if_expr(i: &str) -> IResult<&str, Expression> { let (i, _) = space_delimited(tag("if"))(i)?; let (i, cond) = expr(i)?; let (i, t_case) = delimited(open_brace, expr, close_brace)(i)?; let (i, f_case) = opt(preceded( space_delimited(tag("else")), delimited(open_brace, expr, close_brace), ))(i)?; Ok(( i, Expression::If( Box::new(cond), Box::new(t_case), f_case.map(Box::new), ), )) } fn expr(i: &str) -> IResult<&str, Expression> { alt((if_expr, num_expr))(i) } fn var_def(i: &str) -> IResult<&str, Statement> { let (i, _) = delimited(multispace0, tag("var"), multispace1)(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarDef(name, expr))) } fn var_assign(i: &str) -> IResult<&str, Statement> { let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarAssign(name, expr))) } fn expr_statement(i: &str) -> IResult<&str, Statement> { let (i, res) = expr(i)?; Ok((i, Statement::Expression(res))) } fn statement(i: &str) -> IResult<&str, Statement> { alt((var_def, var_assign, expr_statement))(i) } fn statements(i: &str) -> Result<Statements, nom::error::Error<&str>> { let (_, res) = separated_list0(tag(";"), statement)(i).finish()?; Ok(res) }
input.rscl を作成し、内容を以下のようにする。
var one = 1; var a = if one { 10 } else { 20 }; a + 2;
以下のように実行できる。 >cargo run -- < input.rscl eval: 12.0 ■繰り返し src/main.rs の内容を以下のようにする。
use std::{ collections::HashMap, io::Read }; use nom::{ branch::alt, bytes::complete::tag, character::complete::{ alpha1, alphanumeric1, char, multispace0, multispace1, }, combinator::{opt, recognize}, error::ParseError, multi::{fold_many0, many0}, number::complete::recognize_float, sequence::{delimited, pair, preceded, terminated}, Finish, IResult, Parser, }; fn main() { let mut buf = String::new(); if !std::io::stdin().read_to_string(&mut buf).is_ok() { panic!("Failed to read from stdin"); } let parsed_statements = match statements_finish(&buf) { Ok(parsed_statements) => parsed_statements, Err(e) => { eprintln!("Parse error: {e:?}"); return; } }; let mut variables = HashMap::new(); eval_stmts(&parsed_statements, &mut variables); } type Variables = HashMap<String, f64>; fn eval_stmts(stmts: &[Statement], variables: &mut Variables) { for statement in stmts { match statement { Statement::Expression(expr) => { println!("eval: {:?}", eval(expr, variables)) } Statement::VarDef(name, expr) => { let value = eval(expr, variables); variables.insert(name.to_string(), value); } Statement::VarAssign(name, expr) => { if !variables.contains_key(*name) { panic!("Variable is not defined"); } let value = eval(expr, variables); variables.insert(name.to_string(), value); } Statement::For { loop_var, start, end, stmts, } => { let start = eval(start, variables) as isize; let end = eval(end, variables) as isize; for i in start..end { variables.insert(loop_var.to_string(), i as f64); eval_stmts(stmts, variables); } } } } } #[derive(Debug, PartialEq, Clone)] enum Expression<'src> { Ident(&'src str), NumLiteral(f64), FnInvoke(&'src str, Vec<Expression<'src>>), Add(Box<Expression<'src>>, Box<Expression<'src>>), Sub(Box<Expression<'src>>, Box<Expression<'src>>), Mul(Box<Expression<'src>>, Box<Expression<'src>>), Div(Box<Expression<'src>>, Box<Expression<'src>>), If( Box<Expression<'src>>, Box<Expression<'src>>, Option<Box<Expression<'src>>>, ), } #[derive(Debug, PartialEq, Clone)] enum Statement<'src> { Expression(Expression<'src>), VarDef(&'src str, Expression<'src>), VarAssign(&'src str, Expression<'src>), For { loop_var: &'src str, start: Expression<'src>, end: Expression<'src>, stmts: Statements<'src>, }, } type Statements<'a> = Vec<Statement<'a>>; fn unary_fn( f: fn(f64) -> f64, ) -> impl Fn(&[Expression], &Variables) -> f64 { move |args, variables| { f(eval( args.into_iter().next().expect("function missing argument"), variables, )) } } fn binary_fn( f: fn(f64, f64) -> f64, ) -> impl Fn(&[Expression], &Variables) -> f64 { move |args, variables| { let mut args = args.into_iter(); let lhs = eval( args.next().expect("function missing the first argument"), variables, ); let rhs = eval( args.next().expect("function missing the second argument"), variables, ); f(lhs, rhs) } } fn eval(expr: &Expression, vars: &Variables) -> f64 { use Expression::*; match expr { Ident("pi") => std::f64::consts::PI, Ident(id) => *vars.get(*id).expect("Variable not found"), NumLiteral(n) => *n, FnInvoke("sqrt", args) => unary_fn(f64::sqrt)(args, vars), FnInvoke("sin", args) => unary_fn(f64::sin)(args, vars), FnInvoke("cos", args) => unary_fn(f64::cos)(args, vars), FnInvoke("tan", args) => unary_fn(f64::tan)(args, vars), FnInvoke("asin", args) => unary_fn(f64::asin)(args, vars), FnInvoke("acos", args) => unary_fn(f64::acos)(args, vars), FnInvoke("atan", args) => unary_fn(f64::atan)(args, vars), FnInvoke("atan2", args) => binary_fn(f64::atan2)(args, vars), FnInvoke("pow", args) => binary_fn(f64::powf)(args, vars), FnInvoke("exp", args) => unary_fn(f64::exp)(args, vars), FnInvoke("log", args) => binary_fn(f64::log)(args, vars), FnInvoke("log10", args) => unary_fn(f64::log10)(args, vars), FnInvoke(name, _) => { panic!("Unknown function {name:?}") }, Add(lhs, rhs) => eval(lhs, vars) + eval(rhs, vars), Sub(lhs, rhs) => eval(lhs, vars) - eval(rhs, vars), Mul(lhs, rhs) => eval(lhs, vars) * eval(rhs, vars), Div(lhs, rhs) => eval(lhs, vars) / eval(rhs, vars), If(cond, t_case, f_case) => { if eval(cond, vars) != 0. { eval(t_case, vars) } else if let Some(f_case) = f_case { eval(f_case, vars) } else { 0. } } } } fn space_delimited<'src, O, E>( f: impl Parser<&'src str, O, E>, ) -> impl FnMut(&'src str) -> IResult<&'src str, O, E> where E: ParseError<&'src str>, { delimited(multispace0, f, multispace0) } fn factor(i: &str) -> IResult<&str, Expression> { alt((number, func_call, ident, parens))(i) } fn func_call(i: &str) -> IResult<&str, Expression> { let (r, ident) = space_delimited(identifier)(i)?; let (r, args) = space_delimited(delimited( tag("("), many0(delimited( multispace0, expr, space_delimited(opt(tag(","))), )), tag(")"), ))(r)?; Ok((r, Expression::FnInvoke(ident, args))) } fn ident(input: &str) -> IResult<&str, Expression> { let (r, res) = space_delimited(identifier)(input)?; Ok((r, Expression::Ident(res))) } fn identifier(input: &str) -> IResult<&str, &str> { recognize(pair( alt((alpha1, tag("_"))), many0(alt((alphanumeric1, tag("_")))), ))(input) } fn number(input: &str) -> IResult<&str, Expression> { let (r, v) = space_delimited(recognize_float)(input)?; Ok(( r, Expression::NumLiteral(v.parse().map_err( |_| { nom::Err::Error(nom::error::Error { input, code: nom::error::ErrorKind::Digit, }) }, )?), )) } fn parens(i: &str) -> IResult<&str, Expression> { space_delimited( delimited(tag("("), expr, tag(")")) )(i) } fn term(i: &str) -> IResult<&str, Expression> { let (i, init) = factor(i)?; fold_many0( pair(space_delimited(alt((char('*'), char('/')))), factor), move || init.clone(), |acc, (op, val): (char, Expression)| { match op { '*' => Expression::Mul(Box::new(acc), Box::new(val)), '/' => Expression::Div(Box::new(acc), Box::new(val)), _ => panic!( "Multiplicative expression should have '*' or '/' operator" ), } }, )(i) } fn num_expr(i: &str) -> IResult<&str, Expression> { let (i, init) = term(i)?; fold_many0( pair(space_delimited(alt((char('+'), char('-')))), term), move || init.clone(), |acc, (op, val): (char, Expression)| match op { '+' => Expression::Add(Box::new(acc), Box::new(val)), '-' => Expression::Sub(Box::new(acc), Box::new(val)), _ => panic!( "Additive expression should have '+' or '-' operator" ), }, )(i) } fn open_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('{'))(i)?; Ok((i, ())) } fn close_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('}'))(i)?; Ok((i, ())) } fn if_expr(i: &str) -> IResult<&str, Expression> { let (i, _) = space_delimited(tag("if"))(i)?; let (i, cond) = expr(i)?; let (i, t_case) = delimited(open_brace, expr, close_brace)(i)?; let (i, f_case) = opt(preceded( space_delimited(tag("else")), delimited(open_brace, expr, close_brace), ))(i)?; Ok(( i, Expression::If( Box::new(cond), Box::new(t_case), f_case.map(Box::new), ), )) } fn expr(i: &str) -> IResult<&str, Expression> { alt((if_expr, num_expr))(i) } fn var_def(i: &str) -> IResult<&str, Statement> { let (i, _) = delimited(multispace0, tag("var"), multispace1)(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarDef(name, expr))) } fn var_assign(i: &str) -> IResult<&str, Statement> { let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarAssign(name, expr))) } fn expr_statement(i: &str) -> IResult<&str, Statement> { let (i, res) = expr(i)?; Ok((i, Statement::Expression(res))) } fn for_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("for"))(i)?; let (i, loop_var) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(tag("in"))(i)?; let (i, start) = space_delimited(expr)(i)?; let (i, _) = space_delimited(tag("to"))(i)?; let (i, end) = space_delimited(expr)(i)?; let (i, stmts) = delimited(open_brace, statements, close_brace)(i)?; Ok((i, Statement::For { loop_var, start, end, stmts })) } fn statement(i: &str) -> IResult<&str, Statement> { alt(( for_statement, terminated( alt((var_def, var_assign, expr_statement)), char(';'), ), ))(i) } fn statements(i: &str) -> IResult<&str, Statements> { let (i, stmts) = many0(statement)(i)?; let (i, _) = opt(char(';'))(i)?; Ok((i, stmts)) } fn statements_finish(i: &str) -> Result<Statements, nom::error::Error<&str>> { let (_, res) = statements(i).finish()?; Ok(res) }
input.rscl を作成し、内容を以下のようにする。
var a = 1; for i in 0 to 3 { a + i; }
以下のように実行できる。 >cargo run -- < input.rscl eval: 1.0 eval: 2.0 eval: 3.0 プログラム内にあるterminated関数は、パーサーの後に指定した文字が続いているかを判定するもの。 今回の場合、変数定義や変数代入や文に続けてセミコロンが書かれているか、を確認している。 ■関数の定義 src/main.rs の内容を以下のようにする。
use std::{ collections::HashMap, io::Read }; use nom::{ branch::alt, bytes::complete::tag, character::complete::{ alpha1, alphanumeric1, char, multispace0, multispace1, }, combinator::{opt, recognize}, error::ParseError, multi::{fold_many0, many0, separated_list0}, number::complete::recognize_float, sequence::{delimited, pair, preceded, terminated}, Finish, IResult, Parser, }; fn main() { let mut buf = String::new(); if !std::io::stdin().read_to_string(&mut buf).is_ok() { panic!("Failed to read from stdin"); } let parsed_statements = match statements_finish(&buf) { Ok(parsed_statements) => parsed_statements, Err(e) => { eprintln!("Parse error: {e:?}"); return; } }; let mut frame = StackFrame::new(); eval_stmts(&parsed_statements, &mut frame); } enum FnDef<'src> { User(UserFn<'src>), Native(NativeFn), } impl<'src> FnDef<'src> { fn call(&self, args: &[f64], frame: &StackFrame) -> f64 { match self { Self::User(code) => { let mut new_frame = StackFrame::push_stack(frame); new_frame.vars = args.iter().zip(code.args.iter()).map(|(arg, name)| (name.to_string(), *arg)).collect(); eval_stmts(&code.stmts, &mut new_frame) } Self::Native(code) => (code.code)(args), } } } struct UserFn<'src> { args: Vec<&'src str>, stmts: Statements<'src>, } struct NativeFn { code: Box<dyn Fn(&[f64]) -> f64>, } type Variables = HashMap<String, f64>; type Functions<'src> = HashMap<String, FnDef<'src>>; struct StackFrame<'src> { vars: Variables, funcs: Functions<'src>, uplevel: Option<&'src StackFrame<'src>>, } impl<'src> StackFrame<'src> { fn new() -> Self { let mut funcs = Functions::new(); funcs.insert("sqrt".to_string(), unary_fn(f64::sqrt)); funcs.insert("sin".to_string(), unary_fn(f64::sin)); funcs.insert("cos".to_string(), unary_fn(f64::cos)); funcs.insert("tan".to_string(), unary_fn(f64::tan)); funcs.insert("asin".to_string(), unary_fn(f64::asin)); funcs.insert("acos".to_string(), unary_fn(f64::acos)); funcs.insert("atan".to_string(), unary_fn(f64::atan)); funcs.insert("atan2".to_string(), binary_fn(f64::atan2)); funcs.insert("pow".to_string(), binary_fn(f64::powf)); funcs.insert("exp".to_string(), unary_fn(f64::exp)); funcs.insert("log".to_string(), binary_fn(f64::log)); funcs.insert("log10".to_string(), unary_fn(f64::log10)); funcs.insert("print".to_string(), unary_fn(print)); Self { vars: Variables::new(), funcs, uplevel: None, } } fn push_stack(uplevel: &'src Self) -> Self { Self { vars: HashMap::new(), funcs: HashMap::new(), uplevel: Some(uplevel), } } fn get_fn(&self, name: &str) -> Option<&FnDef<'src>> { let mut next_frame = Some(self); while let Some(frame) = next_frame { if let Some(func) = frame.funcs.get(name) { return Some(func); } next_frame = frame.uplevel; } None } } fn print(arg: f64) -> f64 { println!("print: {arg}"); 0. } fn eval_stmts<'src>(stmts: &[Statement<'src>], frame: &mut StackFrame<'src>) -> f64 { let mut last_result = 0.; for statement in stmts { match statement { Statement::Expression(expr) => { last_result = eval(expr, frame); } Statement::VarDef(name, expr) => { let value = eval(expr, frame); frame.vars.insert(name.to_string(), value); } Statement::VarAssign(name, expr) => { if !frame.vars.contains_key(*name) { panic!("Variable is not defined"); } let value = eval(expr, frame); frame.vars.insert(name.to_string(), value); } Statement::For { loop_var, start, end, stmts, } => { let start = eval(start, frame) as isize; let end = eval(end, frame) as isize; for i in start..end { frame.vars.insert(loop_var.to_string(), i as f64); eval_stmts(stmts, frame); } } Statement::FnDef { name, args, stmts, } => { frame.funcs.insert( name.to_string(), FnDef::User(UserFn { args: args.clone(), stmts: stmts.clone(), }), ); } } } last_result } #[derive(Debug, PartialEq, Clone)] enum Expression<'src> { Ident(&'src str), NumLiteral(f64), FnInvoke(&'src str, Vec<Expression<'src>>), Add(Box<Expression<'src>>, Box<Expression<'src>>), Sub(Box<Expression<'src>>, Box<Expression<'src>>), Mul(Box<Expression<'src>>, Box<Expression<'src>>), Div(Box<Expression<'src>>, Box<Expression<'src>>), Gt(Box<Expression<'src>>, Box<Expression<'src>>), Lt(Box<Expression<'src>>, Box<Expression<'src>>), If( Box<Expression<'src>>, Box<Expression<'src>>, Option<Box<Expression<'src>>>, ), } #[derive(Debug, PartialEq, Clone)] enum Statement<'src> { Expression(Expression<'src>), VarDef(&'src str, Expression<'src>), VarAssign(&'src str, Expression<'src>), For { loop_var: &'src str, start: Expression<'src>, end: Expression<'src>, stmts: Statements<'src>, }, FnDef { name: &'src str, args: Vec<&'src str>, stmts: Statements<'src>, }, } type Statements<'a> = Vec<Statement<'a>>; fn unary_fn<'a>(f: fn(f64) -> f64) -> FnDef<'a> { FnDef::Native(NativeFn { code: Box::new(move |args| { f(*args.into_iter().next().expect("function missing argument")) }) }) } fn binary_fn<'a>(f: fn(f64, f64) -> f64) -> FnDef<'a> { FnDef::Native(NativeFn { code: Box::new(move |args| { let mut args = args.into_iter(); let lhs = args.next().expect("function missing the first argument"); let rhs = args.next().expect("function missing the second argument"); f(*lhs, *rhs) }) }) } fn eval(expr: &Expression, frame: &StackFrame) -> f64 { use Expression::*; match expr { Ident("pi") => std::f64::consts::PI, Ident(id) => *frame.vars.get(*id).expect("Variable not found"), NumLiteral(n) => *n, FnInvoke(name, args) => { if let Some(func) = frame.get_fn(*name) { let args: Vec<_> = args.iter().map(|arg| eval(arg, frame)).collect(); func.call(&args, frame) } else { panic!("Unknown function {name:?}") } }, Add(lhs, rhs) => eval(lhs, frame) + eval(rhs, frame), Sub(lhs, rhs) => eval(lhs, frame) - eval(rhs, frame), Mul(lhs, rhs) => eval(lhs, frame) * eval(rhs, frame), Div(lhs, rhs) => eval(lhs, frame) / eval(rhs, frame), Gt(lhs, rhs) => { if eval(lhs, frame) > eval(rhs, frame) { 1. } else { 0. } } Lt(lhs, rhs) => { if eval(lhs, frame) < eval(rhs, frame) { 1. } else { 0. } } If(cond, t_case, f_case) => { if eval(cond, frame) != 0. { eval(t_case, frame) } else if let Some(f_case) = f_case { eval(f_case, frame) } else { 0. } } } } fn space_delimited<'src, O, E>( f: impl Parser<&'src str, O, E>, ) -> impl FnMut(&'src str) -> IResult<&'src str, O, E> where E: ParseError<&'src str>, { delimited(multispace0, f, multispace0) } fn factor(i: &str) -> IResult<&str, Expression> { alt((number, func_call, ident, parens))(i) } fn func_call(i: &str) -> IResult<&str, Expression> { let (r, ident) = space_delimited(identifier)(i)?; let (r, args) = space_delimited(delimited( tag("("), many0(delimited( multispace0, expr, space_delimited(opt(tag(","))), )), tag(")"), ))(r)?; Ok((r, Expression::FnInvoke(ident, args))) } fn ident(input: &str) -> IResult<&str, Expression> { let (r, res) = space_delimited(identifier)(input)?; Ok((r, Expression::Ident(res))) } fn identifier(input: &str) -> IResult<&str, &str> { recognize(pair( alt((alpha1, tag("_"))), many0(alt((alphanumeric1, tag("_")))), ))(input) } fn number(input: &str) -> IResult<&str, Expression> { let (r, v) = space_delimited(recognize_float)(input)?; Ok(( r, Expression::NumLiteral(v.parse().map_err( |_| { nom::Err::Error(nom::error::Error { input, code: nom::error::ErrorKind::Digit, }) }, )?), )) } fn parens(i: &str) -> IResult<&str, Expression> { space_delimited( delimited(tag("("), expr, tag(")")) )(i) } fn term(i: &str) -> IResult<&str, Expression> { let (i, init) = factor(i)?; fold_many0( pair(space_delimited(alt((char('*'), char('/')))), factor), move || init.clone(), |acc, (op, val): (char, Expression)| { match op { '*' => Expression::Mul(Box::new(acc), Box::new(val)), '/' => Expression::Div(Box::new(acc), Box::new(val)), _ => panic!( "Multiplicative expression should have '*' or '/' operator" ), } }, )(i) } fn num_expr(i: &str) -> IResult<&str, Expression> { let (i, init) = term(i)?; fold_many0( pair(space_delimited(alt((char('+'), char('-')))), term), move || init.clone(), |acc, (op, val): (char, Expression)| match op { '+' => Expression::Add(Box::new(acc), Box::new(val)), '-' => Expression::Sub(Box::new(acc), Box::new(val)), _ => panic!( "Additive expression should have '+' or '-' operator" ), }, )(i) } fn cond_expr(i: &str) -> IResult<&str, Expression> { let (i, first) = num_expr(i)?; let (i, cond) = space_delimited(alt((char('<'), char('>'))))(i)?; let (i, second) = num_expr(i)?; Ok(( i, match cond { '<' => Expression::Lt(Box::new(first), Box::new(second)), '>' => Expression::Gt(Box::new(first), Box::new(second)), _ => unreachable!(), }, )) } fn open_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('{'))(i)?; Ok((i, ())) } fn close_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('}'))(i)?; Ok((i, ())) } fn if_expr(i: &str) -> IResult<&str, Expression> { let (i, _) = space_delimited(tag("if"))(i)?; let (i, cond) = expr(i)?; let (i, t_case) = delimited(open_brace, expr, close_brace)(i)?; let (i, f_case) = opt(preceded( space_delimited(tag("else")), delimited(open_brace, expr, close_brace), ))(i)?; Ok(( i, Expression::If( Box::new(cond), Box::new(t_case), f_case.map(Box::new), ), )) } fn expr(i: &str) -> IResult<&str, Expression> { alt((if_expr, cond_expr, num_expr))(i) } fn var_def(i: &str) -> IResult<&str, Statement> { let (i, _) = delimited(multispace0, tag("var"), multispace1)(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarDef(name, expr))) } fn var_assign(i: &str) -> IResult<&str, Statement> { let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarAssign(name, expr))) } fn expr_statement(i: &str) -> IResult<&str, Statement> { let (i, res) = expr(i)?; Ok((i, Statement::Expression(res))) } fn for_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("for"))(i)?; let (i, loop_var) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(tag("in"))(i)?; let (i, start) = space_delimited(expr)(i)?; let (i, _) = space_delimited(tag("to"))(i)?; let (i, end) = space_delimited(expr)(i)?; let (i, stmts) = delimited(open_brace, statements, close_brace)(i)?; Ok((i, Statement::For { loop_var, start, end, stmts })) } fn fn_def_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("fn"))(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(tag("("))(i)?; let (i, args) = separated_list0(char(','), space_delimited(identifier))(i)?; let (i, _) = space_delimited(tag(")"))(i)?; let (i, stmts) = delimited(open_brace, statements, close_brace)(i)?; Ok((i, Statement::FnDef { name, args, stmts })) } fn statement(i: &str) -> IResult<&str, Statement> { alt(( for_statement, fn_def_statement, terminated( alt((var_def, var_assign, expr_statement)), char(';'), ), ))(i) } fn statements(i: &str) -> IResult<&str, Statements> { let (i, stmts) = many0(statement)(i)?; let (i, _) = opt(char(';'))(i)?; Ok((i, stmts)) } fn statements_finish(i: &str) -> Result<Statements, nom::error::Error<&str>> { let (_, res) = statements(i).finish()?; Ok(res) }
input.rscl を作成し、内容を以下のようにする。
fn add(a, b) { a + b; } print(add(1, 2));
以下のように実行できる。 >cargo run -- < input.rscl print: 3 なおmain関数内にある。
let mut frame = StackFrame::new();
の直前に以下を追加すると、
println!("parsed_statements: {:?}", parsed_statements);
以下のように構文解析の結果が出力される。(実際は改行なしで出力される。)
parsed_statements: [ FnDef { name: "add", args: ["a", "b"], stmts: [Expression(Add(Ident("a"), Ident("b")))] }, Expression ( FnInvoke("print", [FnInvoke("add", [NumLiteral(1.0), NumLiteral(2.0)])]) ) ]
■関数の定義(iter().zip()の挙動を確認) 【Rust】二つのイテレータを同時に回す - yiskw note https://yiskw713.hatenablog.com/entry/rust-iterator-zip
fn main() { let arr1 = vec![1, 2, 3]; let arr2 = vec![4, 5, 6]; for (a, b) in arr1.iter().zip(arr2.iter()) { println!("{} {}", a, b); } }
以下のように実行できる。 >cargo run 1 4 2 5 3 6 ■return文 src/main.rs の内容を以下のようにする。
use std::{ collections::HashMap, io::Read, ops::ControlFlow, }; use nom::{ branch::alt, bytes::complete::tag, character::complete::{ alpha1, alphanumeric1, char, multispace0, multispace1, }, combinator::{opt, recognize}, error::ParseError, multi::{fold_many0, many0, separated_list0}, number::complete::recognize_float, sequence::{delimited, pair, preceded, terminated}, Finish, IResult, Parser, }; fn main() { let mut buf = String::new(); if !std::io::stdin().read_to_string(&mut buf).is_ok() { panic!("Failed to read from stdin"); } let parsed_statements = match statements_finish(&buf) { Ok(parsed_statements) => parsed_statements, Err(e) => { eprintln!("Parse error: {e:?}"); return; } }; let mut frame = StackFrame::new(); eval_stmts(&parsed_statements, &mut frame); } enum FnDef<'src> { User(UserFn<'src>), Native(NativeFn), } impl<'src> FnDef<'src> { fn call(&self, args: &[f64], frame: &StackFrame) -> f64 { match self { Self::User(code) => { let mut new_frame = StackFrame::push_stack(frame); new_frame.vars = args.iter().zip(code.args.iter()).map(|(arg, name)| (name.to_string(), *arg)).collect(); match eval_stmts(&code.stmts, &mut new_frame) { EvalResult::Continue(val) | EvalResult::Break(val) => val, } } Self::Native(code) => (code.code)(args), } } } struct UserFn<'src> { args: Vec<&'src str>, stmts: Statements<'src>, } struct NativeFn { code: Box<dyn Fn(&[f64]) -> f64>, } type Variables = HashMap<String, f64>; type Functions<'src> = HashMap<String, FnDef<'src>>; struct StackFrame<'src> { vars: Variables, funcs: Functions<'src>, uplevel: Option<&'src StackFrame<'src>>, } impl<'src> StackFrame<'src> { fn new() -> Self { let mut funcs = Functions::new(); funcs.insert("sqrt".to_string(), unary_fn(f64::sqrt)); funcs.insert("sin".to_string(), unary_fn(f64::sin)); funcs.insert("cos".to_string(), unary_fn(f64::cos)); funcs.insert("tan".to_string(), unary_fn(f64::tan)); funcs.insert("asin".to_string(), unary_fn(f64::asin)); funcs.insert("acos".to_string(), unary_fn(f64::acos)); funcs.insert("atan".to_string(), unary_fn(f64::atan)); funcs.insert("atan2".to_string(), binary_fn(f64::atan2)); funcs.insert("pow".to_string(), binary_fn(f64::powf)); funcs.insert("exp".to_string(), unary_fn(f64::exp)); funcs.insert("log".to_string(), binary_fn(f64::log)); funcs.insert("log10".to_string(), unary_fn(f64::log10)); funcs.insert("print".to_string(), unary_fn(print)); Self { vars: Variables::new(), funcs, uplevel: None, } } fn push_stack(uplevel: &'src Self) -> Self { Self { vars: HashMap::new(), funcs: HashMap::new(), uplevel: Some(uplevel), } } fn get_fn(&self, name: &str) -> Option<&FnDef<'src>> { let mut next_frame = Some(self); while let Some(frame) = next_frame { if let Some(func) = frame.funcs.get(name) { return Some(func); } next_frame = frame.uplevel; } None } } fn print(arg: f64) -> f64 { println!("print: {arg}"); 0. } fn eval_stmts<'src>(stmts: &[Statement<'src>], frame: &mut StackFrame<'src>) -> EvalResult { let mut last_result = EvalResult::Continue(0.); for statement in stmts { match statement { Statement::Expression(expr) => { last_result = EvalResult::Continue(eval(expr, frame)?); } Statement::VarDef(name, expr) => { let value = eval(expr, frame)?; frame.vars.insert(name.to_string(), value); } Statement::VarAssign(name, expr) => { if !frame.vars.contains_key(*name) { panic!("Variable is not defined"); } let value = eval(expr, frame)?; frame.vars.insert(name.to_string(), value); } Statement::For { loop_var, start, end, stmts, } => { let start = eval(start, frame)? as isize; let end = eval(end, frame)? as isize; for i in start..end { frame.vars.insert(loop_var.to_string(), i as f64); eval_stmts(stmts, frame)?; } } Statement::FnDef { name, args, stmts, } => { frame.funcs.insert( name.to_string(), FnDef::User(UserFn { args: args.clone(), stmts: stmts.clone(), }), ); } Statement::Return(expr) => { return EvalResult::Break(eval(expr, frame)?); } } } last_result } #[derive(Debug, PartialEq, Clone)] enum Expression<'src> { Ident(&'src str), NumLiteral(f64), FnInvoke(&'src str, Vec<Expression<'src>>), Add(Box<Expression<'src>>, Box<Expression<'src>>), Sub(Box<Expression<'src>>, Box<Expression<'src>>), Mul(Box<Expression<'src>>, Box<Expression<'src>>), Div(Box<Expression<'src>>, Box<Expression<'src>>), Gt(Box<Expression<'src>>, Box<Expression<'src>>), Lt(Box<Expression<'src>>, Box<Expression<'src>>), If( Box<Expression<'src>>, Box<Statements<'src>>, Option<Box<Statements<'src>>>, ), } #[derive(Debug, PartialEq, Clone)] enum Statement<'src> { Expression(Expression<'src>), VarDef(&'src str, Expression<'src>), VarAssign(&'src str, Expression<'src>), For { loop_var: &'src str, start: Expression<'src>, end: Expression<'src>, stmts: Statements<'src>, }, FnDef { name: &'src str, args: Vec<&'src str>, stmts: Statements<'src>, }, Return(Expression<'src>), } type Statements<'a> = Vec<Statement<'a>>; fn unary_fn<'a>(f: fn(f64) -> f64) -> FnDef<'a> { FnDef::Native(NativeFn { code: Box::new(move |args| { f(*args.into_iter().next().expect("function missing argument")) }) }) } fn binary_fn<'a>(f: fn(f64, f64) -> f64) -> FnDef<'a> { FnDef::Native(NativeFn { code: Box::new(move |args| { let mut args = args.into_iter(); let lhs = args.next().expect("function missing the first argument"); let rhs = args.next().expect("function missing the second argument"); f(*lhs, *rhs) }) }) } type EvalResult = ControlFlow<f64, f64>; fn eval<'src>(expr: &Expression<'src>, frame: &mut StackFrame<'src>) -> EvalResult { use Expression::*; let res = match expr { Ident("pi") => std::f64::consts::PI, Ident(id) => *frame.vars.get(*id).expect("Variable not found"), NumLiteral(n) => *n, FnInvoke(name, args) => { let mut arg_vals = vec![]; for arg in args.iter() { arg_vals.push(eval(arg, frame)?) } if let Some(func) = frame.get_fn(*name) { func.call(&arg_vals, frame) } else { panic!("Unknown function {name:?}") } }, Add(lhs, rhs) => eval(lhs, frame)? + eval(rhs, frame)?, Sub(lhs, rhs) => eval(lhs, frame)? - eval(rhs, frame)?, Mul(lhs, rhs) => eval(lhs, frame)? * eval(rhs, frame)?, Div(lhs, rhs) => eval(lhs, frame)? / eval(rhs, frame)?, Gt(lhs, rhs) => { if eval(lhs, frame)? > eval(rhs, frame)? { 1. } else { 0. } } Lt(lhs, rhs) => { if eval(lhs, frame)? < eval(rhs, frame)? { 1. } else { 0. } } If(cond, t_case, f_case) => { if eval(cond, frame)? != 0. { eval_stmts(t_case, frame)? } else if let Some(f_case) = f_case { eval_stmts(f_case, frame)? } else { 0. } } }; EvalResult::Continue(res) } fn space_delimited<'src, O, E>( f: impl Parser<&'src str, O, E>, ) -> impl FnMut(&'src str) -> IResult<&'src str, O, E> where E: ParseError<&'src str>, { delimited(multispace0, f, multispace0) } fn factor(i: &str) -> IResult<&str, Expression> { alt((number, func_call, ident, parens))(i) } fn func_call(i: &str) -> IResult<&str, Expression> { let (r, ident) = space_delimited(identifier)(i)?; let (r, args) = space_delimited(delimited( tag("("), many0(delimited( multispace0, expr, space_delimited(opt(tag(","))), )), tag(")"), ))(r)?; Ok((r, Expression::FnInvoke(ident, args))) } fn ident(input: &str) -> IResult<&str, Expression> { let (r, res) = space_delimited(identifier)(input)?; Ok((r, Expression::Ident(res))) } fn identifier(input: &str) -> IResult<&str, &str> { recognize(pair( alt((alpha1, tag("_"))), many0(alt((alphanumeric1, tag("_")))), ))(input) } fn number(input: &str) -> IResult<&str, Expression> { let (r, v) = space_delimited(recognize_float)(input)?; Ok(( r, Expression::NumLiteral(v.parse().map_err( |_| { nom::Err::Error(nom::error::Error { input, code: nom::error::ErrorKind::Digit, }) }, )?), )) } fn parens(i: &str) -> IResult<&str, Expression> { space_delimited( delimited(tag("("), expr, tag(")")) )(i) } fn term(i: &str) -> IResult<&str, Expression> { let (i, init) = factor(i)?; fold_many0( pair(space_delimited(alt((char('*'), char('/')))), factor), move || init.clone(), |acc, (op, val): (char, Expression)| { match op { '*' => Expression::Mul(Box::new(acc), Box::new(val)), '/' => Expression::Div(Box::new(acc), Box::new(val)), _ => panic!( "Multiplicative expression should have '*' or '/' operator" ), } }, )(i) } fn num_expr(i: &str) -> IResult<&str, Expression> { let (i, init) = term(i)?; fold_many0( pair(space_delimited(alt((char('+'), char('-')))), term), move || init.clone(), |acc, (op, val): (char, Expression)| match op { '+' => Expression::Add(Box::new(acc), Box::new(val)), '-' => Expression::Sub(Box::new(acc), Box::new(val)), _ => panic!( "Additive expression should have '+' or '-' operator" ), }, )(i) } fn cond_expr(i: &str) -> IResult<&str, Expression> { let (i, first) = num_expr(i)?; let (i, cond) = space_delimited(alt((char('<'), char('>'))))(i)?; let (i, second) = num_expr(i)?; Ok(( i, match cond { '<' => Expression::Lt(Box::new(first), Box::new(second)), '>' => Expression::Gt(Box::new(first), Box::new(second)), _ => unreachable!(), }, )) } fn open_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('{'))(i)?; Ok((i, ())) } fn close_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('}'))(i)?; Ok((i, ())) } fn if_expr(i: &str) -> IResult<&str, Expression> { let (i, _) = space_delimited(tag("if"))(i)?; let (i, cond) = expr(i)?; let (i, t_case) = delimited(open_brace, statements, close_brace)(i)?; let (i, f_case) = opt(preceded( space_delimited(tag("else")), delimited(open_brace, statements, close_brace), ))(i)?; Ok(( i, Expression::If( Box::new(cond), Box::new(t_case), f_case.map(Box::new), ), )) } fn expr(i: &str) -> IResult<&str, Expression> { alt((if_expr, cond_expr, num_expr))(i) } fn var_def(i: &str) -> IResult<&str, Statement> { let (i, _) = delimited(multispace0, tag("var"), multispace1)(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarDef(name, expr))) } fn var_assign(i: &str) -> IResult<&str, Statement> { let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarAssign(name, expr))) } fn expr_statement(i: &str) -> IResult<&str, Statement> { let (i, res) = expr(i)?; Ok((i, Statement::Expression(res))) } fn for_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("for"))(i)?; let (i, loop_var) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(tag("in"))(i)?; let (i, start) = space_delimited(expr)(i)?; let (i, _) = space_delimited(tag("to"))(i)?; let (i, end) = space_delimited(expr)(i)?; let (i, stmts) = delimited(open_brace, statements, close_brace)(i)?; Ok((i, Statement::For { loop_var, start, end, stmts })) } fn fn_def_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("fn"))(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(tag("("))(i)?; let (i, args) = separated_list0(char(','), space_delimited(identifier))(i)?; let (i, _) = space_delimited(tag(")"))(i)?; let (i, stmts) = delimited(open_brace, statements, close_brace)(i)?; Ok((i, Statement::FnDef { name, args, stmts })) } fn return_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("return"))(i)?; let (i, ex) = space_delimited(expr)(i)?; Ok((i, Statement::Return(ex))) } fn general_statement<'a>(last: bool) -> impl Fn(&'a str) -> IResult<&'a str, Statement> { let terminator = move |i| -> IResult<&str, ()> { let mut semicolon = pair(tag(";"), multispace0); if last { Ok((opt(semicolon)(i)?.0, ())) } else { Ok((semicolon(i)?.0, ())) } }; move |i| { alt(( var_def, var_assign, fn_def_statement, for_statement, terminated(return_statement, terminator), terminated(expr_statement, terminator), ))(i) } } pub(crate) fn last_statement(i: &str) -> IResult<&str, Statement> { general_statement(true)(i) } pub(crate) fn statement(i: &str) -> IResult<&str, Statement> { general_statement(false)(i) } fn statements(i: &str) -> IResult<&str, Statements> { let (i, mut stmts) = many0(statement)(i)?; let (i, last) = opt(last_statement)(i)?; let (i, _) = opt(multispace0)(i)?; if let Some(last) = last { stmts.push(last); } Ok((i, stmts)) } fn statements_finish(i: &str) -> Result<Statements, nom::error::Error<&str>> { let (_, res) = statements(i).finish()?; Ok(res) }
input.rscl を作成し、内容を以下のようにする。
fn early_return(a, b) { if a < b { return a; }; b; } print(early_return(1, 2));
以下のように実行できる。 >cargo run -- < input.rscl print: 1 ■break文、continue文 src/main.rs の内容を以下のようにする。
use std::{ collections::HashMap, io::Read, ops::ControlFlow, }; use nom::{ branch::alt, bytes::complete::tag, character::complete::{ alpha1, alphanumeric1, char, multispace0, multispace1, }, combinator::{opt, recognize}, error::ParseError, multi::{fold_many0, many0, separated_list0}, number::complete::recognize_float, sequence::{delimited, pair, preceded, terminated}, Finish, IResult, Parser, }; fn main() { let mut buf = String::new(); if !std::io::stdin().read_to_string(&mut buf).is_ok() { panic!("Failed to read from stdin"); } let parsed_statements = match statements_finish(&buf) { Ok(parsed_statements) => parsed_statements, Err(e) => { eprintln!("Parse error: {e:?}"); return; } }; let mut frame = StackFrame::new(); eval_stmts(&parsed_statements, &mut frame); } enum FnDef<'src> { User(UserFn<'src>), Native(NativeFn), } impl<'src> FnDef<'src> { fn call(&self, args: &[f64], frame: &StackFrame) -> f64 { match self { Self::User(code) => { let mut new_frame = StackFrame::push_stack(frame); new_frame.vars = args.iter().zip(code.args.iter()).map(|(arg, name)| (name.to_string(), *arg)).collect(); match eval_stmts(&code.stmts, &mut new_frame) { EvalResult::Continue(val) | EvalResult::Break(BreakResult::Return(val)) => val, EvalResult::Break(BreakResult::Break) => { panic!("Breaking outside loop is prohibited") } EvalResult::Break(BreakResult::Continue) => { panic!("Continuing outside loop is prohibited") } } } Self::Native(code) => (code.code)(args), } } } struct UserFn<'src> { args: Vec<&'src str>, stmts: Statements<'src>, } struct NativeFn { code: Box<dyn Fn(&[f64]) -> f64>, } type Variables = HashMap<String, f64>; type Functions<'src> = HashMap<String, FnDef<'src>>; struct StackFrame<'src> { vars: Variables, funcs: Functions<'src>, uplevel: Option<&'src StackFrame<'src>>, } impl<'src> StackFrame<'src> { fn new() -> Self { let mut funcs = Functions::new(); funcs.insert("sqrt".to_string(), unary_fn(f64::sqrt)); funcs.insert("sin".to_string(), unary_fn(f64::sin)); funcs.insert("cos".to_string(), unary_fn(f64::cos)); funcs.insert("tan".to_string(), unary_fn(f64::tan)); funcs.insert("asin".to_string(), unary_fn(f64::asin)); funcs.insert("acos".to_string(), unary_fn(f64::acos)); funcs.insert("atan".to_string(), unary_fn(f64::atan)); funcs.insert("atan2".to_string(), binary_fn(f64::atan2)); funcs.insert("pow".to_string(), binary_fn(f64::powf)); funcs.insert("exp".to_string(), unary_fn(f64::exp)); funcs.insert("log".to_string(), binary_fn(f64::log)); funcs.insert("log10".to_string(), unary_fn(f64::log10)); funcs.insert("print".to_string(), unary_fn(print)); Self { vars: Variables::new(), funcs, uplevel: None, } } fn push_stack(uplevel: &'src Self) -> Self { Self { vars: HashMap::new(), funcs: HashMap::new(), uplevel: Some(uplevel), } } fn get_fn(&self, name: &str) -> Option<&FnDef<'src>> { let mut next_frame = Some(self); while let Some(frame) = next_frame { if let Some(func) = frame.funcs.get(name) { return Some(func); } next_frame = frame.uplevel; } None } } fn print(arg: f64) -> f64 { println!("print: {arg}"); 0. } fn eval_stmts<'src>(stmts: &[Statement<'src>], frame: &mut StackFrame<'src>) -> EvalResult { let mut last_result = EvalResult::Continue(0.); for statement in stmts { match statement { Statement::Expression(expr) => { last_result = EvalResult::Continue(eval(expr, frame)?); } Statement::VarDef(name, expr) => { let value = eval(expr, frame)?; frame.vars.insert(name.to_string(), value); } Statement::VarAssign(name, expr) => { if !frame.vars.contains_key(*name) { panic!("Variable is not defined"); } let value = eval(expr, frame)?; frame.vars.insert(name.to_string(), value); } Statement::For { loop_var, start, end, stmts, } => { let start = eval(start, frame)? as isize; let end = eval(end, frame)? as isize; for i in start..end { frame.vars.insert(loop_var.to_string(), i as f64); match eval_stmts(stmts, frame) { EvalResult::Continue(val) => { last_result = EvalResult::Continue(val) } EvalResult::Break(BreakResult::Return(val)) => { return EvalResult::Break(BreakResult::Return(val)) } EvalResult::Break(BreakResult::Break) => break, EvalResult::Break(BreakResult::Continue) => { continue } }; } } Statement::FnDef { name, args, stmts, } => { frame.funcs.insert( name.to_string(), FnDef::User(UserFn { args: args.clone(), stmts: stmts.clone(), }), ); } Statement::Return(expr) => { return EvalResult::Break(BreakResult::Return(eval(expr, frame)?)); } Statement::Break => { return EvalResult::Break(BreakResult::Break); } Statement::Continue => { return EvalResult::Break(BreakResult::Continue); } } } last_result } #[derive(Debug, PartialEq, Clone)] enum Expression<'src> { Ident(&'src str), NumLiteral(f64), FnInvoke(&'src str, Vec<Expression<'src>>), Add(Box<Expression<'src>>, Box<Expression<'src>>), Sub(Box<Expression<'src>>, Box<Expression<'src>>), Mul(Box<Expression<'src>>, Box<Expression<'src>>), Div(Box<Expression<'src>>, Box<Expression<'src>>), Gt(Box<Expression<'src>>, Box<Expression<'src>>), Lt(Box<Expression<'src>>, Box<Expression<'src>>), If( Box<Expression<'src>>, Box<Statements<'src>>, Option<Box<Statements<'src>>>, ), } #[derive(Debug, PartialEq, Clone)] enum Statement<'src> { Expression(Expression<'src>), VarDef(&'src str, Expression<'src>), VarAssign(&'src str, Expression<'src>), For { loop_var: &'src str, start: Expression<'src>, end: Expression<'src>, stmts: Statements<'src>, }, FnDef { name: &'src str, args: Vec<&'src str>, stmts: Statements<'src>, }, Return(Expression<'src>), Break, Continue, } type Statements<'a> = Vec<Statement<'a>>; fn unary_fn<'a>(f: fn(f64) -> f64) -> FnDef<'a> { FnDef::Native(NativeFn { code: Box::new(move |args| { f(*args.into_iter().next().expect("function missing argument")) }) }) } fn binary_fn<'a>(f: fn(f64, f64) -> f64) -> FnDef<'a> { FnDef::Native(NativeFn { code: Box::new(move |args| { let mut args = args.into_iter(); let lhs = args.next().expect("function missing the first argument"); let rhs = args.next().expect("function missing the second argument"); f(*lhs, *rhs) }) }) } #[derive(Debug)] enum BreakResult { Return(f64), Break, Continue, } type EvalResult = ControlFlow<BreakResult, f64>; fn eval<'src>(expr: &Expression<'src>, frame: &mut StackFrame<'src>) -> EvalResult { use Expression::*; let res = match expr { Ident("pi") => std::f64::consts::PI, Ident(id) => *frame.vars.get(*id).expect("Variable not found"), NumLiteral(n) => *n, FnInvoke(name, args) => { let mut arg_vals = vec![]; for arg in args.iter() { arg_vals.push(eval(arg, frame)?) } if let Some(func) = frame.get_fn(*name) { func.call(&arg_vals, frame) } else { panic!("Unknown function {name:?}") } }, Add(lhs, rhs) => eval(lhs, frame)? + eval(rhs, frame)?, Sub(lhs, rhs) => eval(lhs, frame)? - eval(rhs, frame)?, Mul(lhs, rhs) => eval(lhs, frame)? * eval(rhs, frame)?, Div(lhs, rhs) => eval(lhs, frame)? / eval(rhs, frame)?, Gt(lhs, rhs) => { if eval(lhs, frame)? > eval(rhs, frame)? { 1. } else { 0. } } Lt(lhs, rhs) => { if eval(lhs, frame)? < eval(rhs, frame)? { 1. } else { 0. } } If(cond, t_case, f_case) => { if eval(cond, frame)? != 0. { eval_stmts(t_case, frame)? } else if let Some(f_case) = f_case { eval_stmts(f_case, frame)? } else { 0. } } }; EvalResult::Continue(res) } fn space_delimited<'src, O, E>( f: impl Parser<&'src str, O, E>, ) -> impl FnMut(&'src str) -> IResult<&'src str, O, E> where E: ParseError<&'src str>, { delimited(multispace0, f, multispace0) } fn factor(i: &str) -> IResult<&str, Expression> { alt((number, func_call, ident, parens))(i) } fn func_call(i: &str) -> IResult<&str, Expression> { let (r, ident) = space_delimited(identifier)(i)?; let (r, args) = space_delimited(delimited( tag("("), many0(delimited( multispace0, expr, space_delimited(opt(tag(","))), )), tag(")"), ))(r)?; Ok((r, Expression::FnInvoke(ident, args))) } fn ident(input: &str) -> IResult<&str, Expression> { let (r, res) = space_delimited(identifier)(input)?; Ok((r, Expression::Ident(res))) } fn identifier(input: &str) -> IResult<&str, &str> { recognize(pair( alt((alpha1, tag("_"))), many0(alt((alphanumeric1, tag("_")))), ))(input) } fn number(input: &str) -> IResult<&str, Expression> { let (r, v) = space_delimited(recognize_float)(input)?; Ok(( r, Expression::NumLiteral(v.parse().map_err( |_| { nom::Err::Error(nom::error::Error { input, code: nom::error::ErrorKind::Digit, }) }, )?), )) } fn parens(i: &str) -> IResult<&str, Expression> { space_delimited( delimited(tag("("), expr, tag(")")) )(i) } fn term(i: &str) -> IResult<&str, Expression> { let (i, init) = factor(i)?; fold_many0( pair(space_delimited(alt((char('*'), char('/')))), factor), move || init.clone(), |acc, (op, val): (char, Expression)| { match op { '*' => Expression::Mul(Box::new(acc), Box::new(val)), '/' => Expression::Div(Box::new(acc), Box::new(val)), _ => panic!( "Multiplicative expression should have '*' or '/' operator" ), } }, )(i) } fn num_expr(i: &str) -> IResult<&str, Expression> { let (i, init) = term(i)?; fold_many0( pair(space_delimited(alt((char('+'), char('-')))), term), move || init.clone(), |acc, (op, val): (char, Expression)| match op { '+' => Expression::Add(Box::new(acc), Box::new(val)), '-' => Expression::Sub(Box::new(acc), Box::new(val)), _ => panic!( "Additive expression should have '+' or '-' operator" ), }, )(i) } fn cond_expr(i: &str) -> IResult<&str, Expression> { let (i, first) = num_expr(i)?; let (i, cond) = space_delimited(alt((char('<'), char('>'))))(i)?; let (i, second) = num_expr(i)?; Ok(( i, match cond { '<' => Expression::Lt(Box::new(first), Box::new(second)), '>' => Expression::Gt(Box::new(first), Box::new(second)), _ => unreachable!(), }, )) } fn open_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('{'))(i)?; Ok((i, ())) } fn close_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('}'))(i)?; Ok((i, ())) } fn if_expr(i: &str) -> IResult<&str, Expression> { let (i, _) = space_delimited(tag("if"))(i)?; let (i, cond) = expr(i)?; let (i, t_case) = delimited(open_brace, statements, close_brace)(i)?; let (i, f_case) = opt(preceded( space_delimited(tag("else")), delimited(open_brace, statements, close_brace), ))(i)?; Ok(( i, Expression::If( Box::new(cond), Box::new(t_case), f_case.map(Box::new), ), )) } fn expr(i: &str) -> IResult<&str, Expression> { alt((if_expr, cond_expr, num_expr))(i) } fn var_def(i: &str) -> IResult<&str, Statement> { let (i, _) = delimited(multispace0, tag("var"), multispace1)(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarDef(name, expr))) } fn var_assign(i: &str) -> IResult<&str, Statement> { let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarAssign(name, expr))) } fn expr_statement(i: &str) -> IResult<&str, Statement> { let (i, res) = expr(i)?; Ok((i, Statement::Expression(res))) } fn for_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("for"))(i)?; let (i, loop_var) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(tag("in"))(i)?; let (i, start) = space_delimited(expr)(i)?; let (i, _) = space_delimited(tag("to"))(i)?; let (i, end) = space_delimited(expr)(i)?; let (i, stmts) = delimited(open_brace, statements, close_brace)(i)?; Ok((i, Statement::For { loop_var, start, end, stmts })) } fn fn_def_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("fn"))(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(tag("("))(i)?; let (i, args) = separated_list0(char(','), space_delimited(identifier))(i)?; let (i, _) = space_delimited(tag(")"))(i)?; let (i, stmts) = delimited(open_brace, statements, close_brace)(i)?; Ok((i, Statement::FnDef { name, args, stmts })) } fn return_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("return"))(i)?; let (i, ex) = space_delimited(expr)(i)?; Ok((i, Statement::Return(ex))) } fn break_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("break"))(i)?; Ok((i, Statement::Break)) } fn continue_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("continue"))(i)?; Ok((i, Statement::Continue)) } fn general_statement<'a>(last: bool) -> impl Fn(&'a str) -> IResult<&'a str, Statement> { let terminator = move |i| -> IResult<&str, ()> { let mut semicolon = pair(tag(";"), multispace0); if last { Ok((opt(semicolon)(i)?.0, ())) } else { Ok((semicolon(i)?.0, ())) } }; move |i| { alt(( var_def, var_assign, fn_def_statement, for_statement, terminated(return_statement, terminator), terminated(break_statement, terminator), terminated(continue_statement, terminator), terminated(expr_statement, terminator), ))(i) } } pub(crate) fn last_statement(i: &str) -> IResult<&str, Statement> { general_statement(true)(i) } pub(crate) fn statement(i: &str) -> IResult<&str, Statement> { general_statement(false)(i) } fn statements(i: &str) -> IResult<&str, Statements> { let (i, mut stmts) = many0(statement)(i)?; let (i, last) = opt(last_statement)(i)?; let (i, _) = opt(multispace0)(i)?; if let Some(last) = last { stmts.push(last); } Ok((i, stmts)) } fn statements_finish(i: &str) -> Result<Statements, nom::error::Error<&str>> { let (_, res) = statements(i).finish()?; Ok(res) }
input.rscl を作成し、内容を以下のようにする。
for i in 0 to 3 { for j in 0 to 3 { if j > 1 { break; }; print(i * 10 + j); } }
以下のように実行できる。 >cargo run -- < input.rscl print: 0 print: 1 print: 10 print: 11 print: 20 print: 21 input.rscl を作成し、内容を以下のようにする。
for i in 0 to 3 { if i < 1 { continue; }; print(i); }
以下のように実行できる。 >cargo run -- < input.rscl print: 1 print: 2 ■動的型の導入 src/main.rs の内容を以下のようにする。
use std::{ cmp::Ordering, collections::HashMap, io::Read, ops::ControlFlow, }; use nom::{ branch::alt, bytes::complete::tag, character::complete::{ alpha1, alphanumeric1, char, multispace0, multispace1, none_of }, combinator::{map_res, opt, recognize}, error::ParseError, multi::{fold_many0, many0, separated_list0}, number::complete::recognize_float, sequence::{delimited, pair, preceded, terminated}, Finish, IResult, Parser, }; fn main() { let mut buf = String::new(); if !std::io::stdin().read_to_string(&mut buf).is_ok() { panic!("Failed to read from stdin"); } let parsed_statements = match statements_finish(&buf) { Ok(parsed_statements) => parsed_statements, Err(e) => { eprintln!("Parse error: {e:?}"); return; } }; let mut frame = StackFrame::new(); eval_stmts(&parsed_statements, &mut frame); } #[derive(Debug, PartialEq, Clone)] enum Value { F64(f64), I64(i64), Str(String), } impl std::fmt::Display for Value { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::F64(v) => write!(f, "{v}"), Self::I64(v) => write!(f, "{v}"), Self::Str(v) => write!(f, "{v}"), } } } impl PartialOrd for Value { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { use Value::*; match (self, other) { (F64(lhs), F64(rhs)) => lhs.partial_cmp(rhs), (I64(lhs), I64(rhs)) => lhs.partial_cmp(rhs), (F64(lhs), I64(rhs)) => lhs.partial_cmp(&(*rhs as f64)), (I64(lhs), F64(rhs)) => (*lhs as f64).partial_cmp(rhs), (Str(lhs), Str(rhs)) => lhs.partial_cmp(rhs), _ => None, } } } impl Value { fn as_i64(&self) -> Option<i64> { match self { Self::F64(val) => Some(*val as i64), Self::I64(val) => Some(*val), Self::Str(val) => val.parse().ok(), } } } fn coerce_f64(a: &Value) -> f64 { match a { Value::F64(v) => *v as f64, Value::I64(v) => *v as f64, _ => panic!("The string could not be parsed as f64"), } } fn coerce_i64(a: &Value) -> i64 { match a { Value::F64(v) => *v as i64, Value::I64(v) => *v as i64, _ => panic!("The string could not be parsed as i64"), } } fn coerce_str(a: &Value) -> String { match a { Value::F64(v) => v.to_string(), Value::I64(v) => v.to_string(), Value::Str(v) => v.clone(), } } pub(crate) fn binary_op_str( lhs: &Value, rhs: &Value, d: impl Fn(f64, f64) -> f64, i: impl Fn(i64, i64) -> i64, s: impl Fn(&str, &str) -> String, ) -> Value { use Value::*; match (lhs, rhs) { (F64(lhs), rhs) => F64(d(*lhs, coerce_f64(&rhs))), (lhs, F64(rhs)) => F64(d(coerce_f64(&lhs), *rhs)), (I64(lhs), I64(rhs)) => I64(i(*lhs, *rhs)), (Str(lhs), Str(rhs)) => Str(s(lhs, rhs)), _ => { panic!("Unsupported operator between {:?} and {:?}",lhs, rhs) } } } impl std::ops::Add for Value { type Output = Value; fn add(self, rhs: Self) -> Self::Output { binary_op_str( &self, &rhs, |lhs, rhs| lhs + rhs, |lhs, rhs| lhs + rhs, |lhs, rhs| lhs.to_owned() + rhs, ) } } impl std::ops::Sub for Value { type Output = Value; fn sub(self, rhs: Self) -> Self::Output { binary_op_str( &self, &rhs, |lhs, rhs| lhs - rhs, |lhs, rhs| lhs - rhs, |_, _| panic!("Strings cannot be subtracted"), ) } } impl std::ops::Mul for Value { type Output = Value; fn mul(self, rhs: Self) -> Self::Output { binary_op_str( &self, &rhs, |lhs, rhs| lhs * rhs, |lhs, rhs| lhs * rhs, |_, _| panic!("Strings cannot be multiplied"), ) } } impl std::ops::Div for Value { type Output = Value; fn div(self, rhs: Self) -> Self::Output { binary_op_str( &self, &rhs, |lhs, rhs| lhs / rhs, |lhs, rhs| lhs / rhs, |_, _| panic!("Strings cannot be divided"), ) } } enum FnDef<'src> { User(UserFn<'src>), Native(NativeFn), } impl<'src> FnDef<'src> { fn call(&self, args: &[Value], frame: &StackFrame) -> Value { match self { Self::User(code) => { let mut new_frame = StackFrame::push_stack(frame); new_frame.vars = args.iter().zip(code.args.iter()).map(|(arg, name)| (name.to_string(), arg.clone())).collect(); match eval_stmts(&code.stmts, &mut new_frame) { EvalResult::Continue(val) | EvalResult::Break(BreakResult::Return(val)) => val, EvalResult::Break(BreakResult::Break) => { panic!("Breaking outside loop is prohibited") } EvalResult::Break(BreakResult::Continue) => { panic!("Continuing outside loop is prohibited") } } } Self::Native(code) => (code.code)(args), } } } struct UserFn<'src> { args: Vec<&'src str>, stmts: Statements<'src>, } struct NativeFn { code: Box<dyn Fn(&[Value]) -> Value>, } type Variables = HashMap<String, Value>; type Functions<'src> = HashMap<String, FnDef<'src>>; struct StackFrame<'src> { vars: Variables, funcs: Functions<'src>, uplevel: Option<&'src StackFrame<'src>>, } impl<'src> StackFrame<'src> { fn new() -> Self { let mut funcs = Functions::new(); funcs.insert("sqrt".to_string(), unary_fn(f64::sqrt)); funcs.insert("sin".to_string(), unary_fn(f64::sin)); funcs.insert("cos".to_string(), unary_fn(f64::cos)); funcs.insert("tan".to_string(), unary_fn(f64::tan)); funcs.insert("asin".to_string(), unary_fn(f64::asin)); funcs.insert("acos".to_string(), unary_fn(f64::acos)); funcs.insert("atan".to_string(), unary_fn(f64::atan)); funcs.insert("atan2".to_string(), binary_fn(f64::atan2)); funcs.insert("pow".to_string(), binary_fn(f64::powf)); funcs.insert("exp".to_string(), unary_fn(f64::exp)); funcs.insert("log".to_string(), binary_fn(f64::log)); funcs.insert("log10".to_string(), unary_fn(f64::log10)); funcs.insert( "print".to_string(), FnDef::Native(NativeFn { code: Box::new(print), }), ); funcs.insert( "puts".to_string(), FnDef::Native(NativeFn { code: Box::new(puts_fn), }), ); funcs.insert( "dbg".to_string(), FnDef::Native(NativeFn { code: Box::new(p_dbg), }), ); funcs.insert( "i64".to_string(), FnDef::Native(NativeFn { code: Box::new(move |args| { Value::I64(coerce_i64( args.first().expect("function missing argument"), )) }), }), ); funcs.insert( "f64".to_string(), FnDef::Native(NativeFn { code: Box::new(move |args| { Value::F64(coerce_f64( args.first().expect("function missing argument"), )) }), }), ); funcs.insert( "str".to_string(), FnDef::Native(NativeFn { code: Box::new(move |args| { Value::Str(coerce_str( args.first().expect("function missing argument"), )) }), }), ); Self { vars: Variables::new(), funcs, uplevel: None, } } fn push_stack(uplevel: &'src Self) -> Self { Self { vars: HashMap::new(), funcs: HashMap::new(), uplevel: Some(uplevel), } } fn get_fn(&self, name: &str) -> Option<&FnDef<'src>> { let mut next_frame = Some(self); while let Some(frame) = next_frame { if let Some(func) = frame.funcs.get(name) { return Some(func); } next_frame = frame.uplevel; } None } } fn print(values: &[Value]) -> Value { println!("print: {}", values[0]); Value::I64(0) } fn puts_fn(args: &[Value]) -> Value { for arg in args { print!("{}", arg) } Value::F64(0.) } fn p_dbg(values: &[Value]) -> Value { println!("dbg: {:?}", values[0]); Value::I64(0) } fn eval_stmts<'src>(stmts: &[Statement<'src>], frame: &mut StackFrame<'src>) -> EvalResult { let mut last_result = EvalResult::Continue(Value::I64(0)); for statement in stmts { match statement { Statement::Expression(expr) => { last_result = EvalResult::Continue(eval(expr, frame)?); } Statement::VarDef(name, expr) => { let value = eval(expr, frame)?; frame.vars.insert(name.to_string(), value); } Statement::VarAssign(name, expr) => { if !frame.vars.contains_key(*name) { panic!("Variable is not defined"); } let value = eval(expr, frame)?; frame.vars.insert(name.to_string(), value); } Statement::For { loop_var, start, end, stmts, } => { let start = eval(start, frame)?.as_i64().expect("Start needs to be an integer"); let end = eval(end, frame)?.as_i64().expect("Start needs to be an integer"); for i in start..end { frame.vars.insert(loop_var.to_string(), Value::I64(i)); match eval_stmts(stmts, frame) { EvalResult::Continue(val) => { last_result = EvalResult::Continue(val) } EvalResult::Break(BreakResult::Return(val)) => { return EvalResult::Break(BreakResult::Return(val)) } EvalResult::Break(BreakResult::Break) => break, EvalResult::Break(BreakResult::Continue) => { continue } }; } } Statement::FnDef { name, args, stmts, } => { frame.funcs.insert( name.to_string(), FnDef::User(UserFn { args: args.clone(), stmts: stmts.clone(), }), ); } Statement::Return(expr) => { return EvalResult::Break(BreakResult::Return(eval(expr, frame)?)); } Statement::Break => { return EvalResult::Break(BreakResult::Break); } Statement::Continue => { return EvalResult::Break(BreakResult::Continue); } } } last_result } #[derive(Debug, PartialEq, Clone)] enum Expression<'src> { Ident(&'src str), NumLiteral(f64), StrLiteral(String), FnInvoke(&'src str, Vec<Expression<'src>>), Add(Box<Expression<'src>>, Box<Expression<'src>>), Sub(Box<Expression<'src>>, Box<Expression<'src>>), Mul(Box<Expression<'src>>, Box<Expression<'src>>), Div(Box<Expression<'src>>, Box<Expression<'src>>), Gt(Box<Expression<'src>>, Box<Expression<'src>>), Lt(Box<Expression<'src>>, Box<Expression<'src>>), If( Box<Expression<'src>>, Box<Statements<'src>>, Option<Box<Statements<'src>>>, ), } #[derive(Debug, PartialEq, Clone)] enum Statement<'src> { Expression(Expression<'src>), VarDef(&'src str, Expression<'src>), VarAssign(&'src str, Expression<'src>), For { loop_var: &'src str, start: Expression<'src>, end: Expression<'src>, stmts: Statements<'src>, }, FnDef { name: &'src str, args: Vec<&'src str>, stmts: Statements<'src>, }, Return(Expression<'src>), Break, Continue, } type Statements<'a> = Vec<Statement<'a>>; fn unary_fn<'a>(f: fn(f64) -> f64) -> FnDef<'a> { FnDef::Native(NativeFn { code: Box::new(move |args| { Value::F64(f(coerce_f64( args.into_iter().next().expect("function missing argument") ))) }) }) } fn binary_fn<'a>(f: fn(f64, f64) -> f64) -> FnDef<'a> { FnDef::Native(NativeFn { code: Box::new(move |args| { let mut args = args.into_iter(); let lhs = coerce_f64( args.next().expect("function missing the first argument"), ); let rhs = coerce_f64( args.next().expect("function missing the second argument"), ); Value::F64(f(lhs, rhs)) }) }) } #[derive(Debug)] enum BreakResult { Return(Value), Break, Continue, } type EvalResult = ControlFlow<BreakResult, Value>; fn eval<'src>(expr: &Expression<'src>, frame: &mut StackFrame<'src>) -> EvalResult { use Expression::*; let res = match expr { Ident("pi") => Value::F64(std::f64::consts::PI), Ident(id) => frame.vars.get(*id).cloned().expect(&format!("Variable not found: {id}")), NumLiteral(n) => Value::F64(*n), StrLiteral(s) => Value::Str(s.clone()), FnInvoke(name, args) => { let mut arg_vals = vec![]; for arg in args.iter() { arg_vals.push(eval(arg, frame)?) } if let Some(func) = frame.get_fn(*name) { func.call(&arg_vals, frame) } else { panic!("Unknown function {name:?}") } }, Add(lhs, rhs) => eval(lhs, frame)? + eval(rhs, frame)?, Sub(lhs, rhs) => eval(lhs, frame)? - eval(rhs, frame)?, Mul(lhs, rhs) => eval(lhs, frame)? * eval(rhs, frame)?, Div(lhs, rhs) => eval(lhs, frame)? / eval(rhs, frame)?, Gt(lhs, rhs) => { if eval(lhs, frame)? > eval(rhs, frame)? { Value::I64(1) } else { Value::I64(0) } } Lt(lhs, rhs) => { if eval(lhs, frame)? < eval(rhs, frame)? { Value::I64(1) } else { Value::I64(0) } } If(cond, t_case, f_case) => { if coerce_i64(&eval(cond, frame)?) != 0 { eval_stmts(t_case, frame)? } else if let Some(f_case) = f_case { eval_stmts(f_case, frame)? } else { Value::I64(0) } } }; EvalResult::Continue(res) } fn space_delimited<'src, O, E>( f: impl Parser<&'src str, O, E>, ) -> impl FnMut(&'src str) -> IResult<&'src str, O, E> where E: ParseError<&'src str>, { delimited(multispace0, f, multispace0) } fn factor(i: &str) -> IResult<&str, Expression> { alt((str_literal, num_literal, func_call, ident, parens))(i) } fn func_call(i: &str) -> IResult<&str, Expression> { let (r, ident) = space_delimited(identifier)(i)?; let (r, args) = space_delimited(delimited( tag("("), many0(delimited( multispace0, expr, space_delimited(opt(tag(","))), )), tag(")"), ))(r)?; Ok((r, Expression::FnInvoke(ident, args))) } fn ident(input: &str) -> IResult<&str, Expression> { let (r, res) = space_delimited(identifier)(input)?; Ok((r, Expression::Ident(res))) } fn identifier(input: &str) -> IResult<&str, &str> { recognize(pair( alt((alpha1, tag("_"))), many0(alt((alphanumeric1, tag("_")))), ))(input) } fn str_literal(input: &str) -> IResult<&str, Expression> { let (r0, _) = preceded(multispace0, char('\"'))(input)?; let (r, val) = many0(none_of("\""))(r0)?; let (r, _) = terminated(char('"'), multispace0)(r)?; Ok(( r, Expression::StrLiteral(val.iter().collect::<String>().replace("\\\\", "\\").replace("\\n", "\n")) )) } fn num_literal(input: &str) -> IResult<&str, Expression> { let (r, v) = space_delimited(recognize_float)(input)?; Ok(( r, Expression::NumLiteral(v.parse().map_err( |_| { nom::Err::Error(nom::error::Error { input, code: nom::error::ErrorKind::Digit, }) }, )?), )) } fn parens(i: &str) -> IResult<&str, Expression> { space_delimited( delimited(tag("("), expr, tag(")")) )(i) } fn term(i: &str) -> IResult<&str, Expression> { let (i, init) = factor(i)?; fold_many0( pair(space_delimited(alt((char('*'), char('/')))), factor), move || init.clone(), |acc, (op, val): (char, Expression)| { match op { '*' => Expression::Mul(Box::new(acc), Box::new(val)), '/' => Expression::Div(Box::new(acc), Box::new(val)), _ => panic!( "Multiplicative expression should have '*' or '/' operator" ), } }, )(i) } fn num_expr(i: &str) -> IResult<&str, Expression> { let (i, init) = term(i)?; fold_many0( pair(space_delimited(alt((char('+'), char('-')))), term), move || init.clone(), |acc, (op, val): (char, Expression)| match op { '+' => Expression::Add(Box::new(acc), Box::new(val)), '-' => Expression::Sub(Box::new(acc), Box::new(val)), _ => panic!( "Additive expression should have '+' or '-' operator" ), }, )(i) } fn cond_expr(i: &str) -> IResult<&str, Expression> { let (i, first) = num_expr(i)?; let (i, cond) = space_delimited(alt((char('<'), char('>'))))(i)?; let (i, second) = num_expr(i)?; Ok(( i, match cond { '<' => Expression::Lt(Box::new(first), Box::new(second)), '>' => Expression::Gt(Box::new(first), Box::new(second)), _ => unreachable!(), }, )) } fn open_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('{'))(i)?; Ok((i, ())) } fn close_brace(i: &str) -> IResult<&str, ()> { let (i, _) = space_delimited(char('}'))(i)?; Ok((i, ())) } fn if_expr(i: &str) -> IResult<&str, Expression> { let (i, _) = space_delimited(tag("if"))(i)?; let (i, cond) = expr(i)?; let (i, t_case) = delimited(open_brace, statements, close_brace)(i)?; let (i, f_case) = opt(preceded( space_delimited(tag("else")), alt(( delimited(open_brace, statements, close_brace), map_res( if_expr, |v| -> Result<Vec<Statement>, nom::error::Error<&str>> { Ok(vec![Statement::Expression(v)]) } ) )), ))(i)?; Ok(( i, Expression::If( Box::new(cond), Box::new(t_case), f_case.map(Box::new), ), )) } fn expr(i: &str) -> IResult<&str, Expression> { alt((if_expr, cond_expr, num_expr))(i) } fn var_def(i: &str) -> IResult<&str, Statement> { let (i, _) = delimited(multispace0, tag("var"), multispace1)(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarDef(name, expr))) } fn var_assign(i: &str) -> IResult<&str, Statement> { let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(char('='))(i)?; let (i, expr) = space_delimited(expr)(i)?; Ok((i, Statement::VarAssign(name, expr))) } fn expr_statement(i: &str) -> IResult<&str, Statement> { let (i, res) = expr(i)?; Ok((i, Statement::Expression(res))) } fn for_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("for"))(i)?; let (i, loop_var) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(tag("in"))(i)?; let (i, start) = space_delimited(expr)(i)?; let (i, _) = space_delimited(tag("to"))(i)?; let (i, end) = space_delimited(expr)(i)?; let (i, stmts) = delimited(open_brace, statements, close_brace)(i)?; Ok((i, Statement::For { loop_var, start, end, stmts })) } fn fn_def_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("fn"))(i)?; let (i, name) = space_delimited(identifier)(i)?; let (i, _) = space_delimited(tag("("))(i)?; let (i, args) = separated_list0(char(','), space_delimited(identifier))(i)?; let (i, _) = space_delimited(tag(")"))(i)?; let (i, stmts) = delimited(open_brace, statements, close_brace)(i)?; Ok((i, Statement::FnDef { name, args, stmts })) } fn return_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("return"))(i)?; let (i, ex) = space_delimited(expr)(i)?; Ok((i, Statement::Return(ex))) } fn break_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("break"))(i)?; Ok((i, Statement::Break)) } fn continue_statement(i: &str) -> IResult<&str, Statement> { let (i, _) = space_delimited(tag("continue"))(i)?; Ok((i, Statement::Continue)) } fn general_statement<'a>(last: bool) -> impl Fn(&'a str) -> IResult<&'a str, Statement> { let terminator = move |i| -> IResult<&str, ()> { let mut semicolon = pair(tag(";"), multispace0); if last { Ok((opt(semicolon)(i)?.0, ())) } else { Ok((semicolon(i)?.0, ())) } }; move |i| { alt(( terminated(var_def, terminator), terminated(var_assign, terminator), fn_def_statement, for_statement, terminated(return_statement, terminator), terminated(break_statement, terminator), terminated(continue_statement, terminator), terminated(expr_statement, terminator), ))(i) } } pub(crate) fn last_statement(i: &str) -> IResult<&str, Statement> { general_statement(true)(i) } pub(crate) fn statement(i: &str) -> IResult<&str, Statement> { general_statement(false)(i) } fn statements(i: &str) -> IResult<&str, Statements> { let (i, mut stmts) = many0(statement)(i)?; let (i, last) = opt(last_statement)(i)?; let (i, _) = opt(multispace0)(i)?; if let Some(last) = last { stmts.push(last); } Ok((i, stmts)) } fn statements_finish(i: &str) -> Result<Statements, nom::error::Error<&str>> { let (_, res) = statements(i).finish()?; Ok(res) }
input.rscl を作成し、内容を以下のようにする。
var i = i64(123); var f = f64(123.456); var s = "Hello, world!"; print(i); dbg(i); print(f); dbg(f); print(s); dbg(s); print(i + f); print(i / f); print(s + s);
以下のように実行できる。 >cargo run -- < input.rscl print: 123 dbg: I64(123) print: 123.456 dbg: F64(123.456) print: Hello, world! dbg: Str("Hello, world!") print: 246.45600000000002 print: 0.9963063763608087 print: Hello, world!Hello, world! ■動的型の導入(print!、println!、write!について確認) print! … 標準入力に文字列を出力する。出力後に自動で改行されない。 println! … 標準入力に文字列を出力する。出力後に自動で改行される。 write! … 指定した出力先(標準出力やファイルなど)に文字列を出力する。出力後に自動で改行されない。 Rustのprintln!の中身 #Rust - Qiita https://qiita.com/4hiziri/items/1aed9e264630f90e3dec Rustのソースコードは以下にある。 rust-lang/rust: Empowering everyone to build reliable and efficient software. https://github.com/rust-lang/rust printのソースコードは以下の部分か。 https://github.com/rust-lang/rust/blob/df1b5d3cc2117f1ee96abca25678bc5f5604d450/library/std/src/macr... printlnのソースコードは以下の部分か。 https://github.com/rust-lang/rust/blob/df1b5d3cc2117f1ee96abca25678bc5f5604d450/library/std/src/macr... 以下などを参考に、ソースコードも読んでみたい。 本家Rustコンパイラのソースを読もうとしてみる(1) #Rust - Qiita https://qiita.com/0yoyoyo/items/eba97a019d0e60324263 Rustソースコードのざっくりとした歩き方 - Speaker Deck https://speakerdeck.com/tako8ki/rustsosukotonosatukuritositabu-kifang-3f597e58-222e-4c70-9b1b-e644ea... ■動的型の導入(Displayの拡張について確認) 「impl std::fmt::Display for Value { 〜略〜 }」というのは、 「Displayを拡張し、その内容はValueで使用できる」ということ…だと思われる。 main関数を以下のようなコードにすると、それぞれの型に対応したフォーマット値が出力される。
fn main() { let val1 = Value::F64(3.14); let val2 = Value::I64(42); let val3 = Value::Str(String::from("Hello")); println!("{}", val1); // 3.14 println!("{}", val2); // 42 println!("{}", val3); // Hello }
以下のように実行できる。 >cargo run 3.14 42 Hello

Advertisement