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)])]) ) ]

Advertisement