メモ > 技術 > プログラミング言語: 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)])])
)
]