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