Skip to content

Commit

Permalink
✨ 累乗計算とaether型/omen型を実装
Browse files Browse the repository at this point in the history
  • Loading branch information
liebe-magi committed Aug 16, 2024
1 parent 5071d29 commit 3d2d267
Show file tree
Hide file tree
Showing 9 changed files with 631 additions and 44 deletions.
4 changes: 3 additions & 1 deletion examples/hello.aby
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
forge hello: rune = "Hello World from AbySS!";
unveil(hello);
unveil(hello, "\n");

unveil("Calc test");
unveil("1 + 2 + 3 = ", 1 + 2 + 3);
unveil("1 + 2 * 3 = ", 1 + 2 * 3);
unveil("1 - 2 - 3 = ", 1 - 2 - 3);
unveil("8 - 6 / 3 = ", 8 - 6 / 3);
unveil("3 * (2 + 3) = ", 3 * (2 + 3));
unveil("3 ^ (2 + 3) = ", 3 ^ (2 + 3));
unveil("3.0 ** (-2.0) = ", 3.0 ** (-2.0));
15 changes: 11 additions & 4 deletions src/abyss.pest
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ forge_var = { "forge" ~ identifier ~ ":" ~ type ~ "=" ~ expression }
unveil = { "unveil" ~ "(" ~ (expression ~ ("," ~ expression)*)? ~ ")" }

identifier = @{ ASCII_ALPHANUMERIC+ }
type = { "arcana" | "rune" }
arcana = @{ ASCII_DIGIT+ }
type = { "omen" | "aether" | "arcana" | "rune" }
omen = @{ "boon" | "hex" }
aether = @{ sign? ~ ASCII_DIGIT+ ~ "." ~ ASCII_DIGIT+ }
arcana = @{ sign? ~ ASCII_DIGIT+ }
rune = @{ "\"" ~ (!"\"" ~ ANY)* ~ "\"" }

expression = { term ~ (add_op ~ term)* }
sign = { "+" | "-" }

expression = { trans_expr | power ~ (add_op ~ power)* }
trans_expr = { "trans" ~ "(" ~ expression ~ "as" ~ type ~ ")" }
power = { term ~ (pow_op ~ term)* }
term = { factor ~ (mul_op ~ factor)* }
factor = { arcana | rune | identifier | "(" ~ expression ~ ")" }
factor = { omen | aether | arcana | rune | identifier | "(" ~ expression ~ ")" }

add_op = { "+" | "-" }
mul_op = { "*" | "/" }
pow_op = { "^" | "**" }
4 changes: 4 additions & 0 deletions src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#[derive(Debug, Clone)]
pub enum AST {
Omen(bool),
Arcana(i64),
Aether(f64),
Rune(String),
Add(Box<AST>, Box<AST>),
Subtract(Box<AST>, Box<AST>),
Multiply(Box<AST>, Box<AST>),
Divide(Box<AST>, Box<AST>),
PowArcana(Box<AST>, Box<AST>),
PowAether(Box<AST>, Box<AST>),
VarAssign(String, Box<AST>),
Var(String),
Unveil(Vec<AST>),
Expand Down
128 changes: 99 additions & 29 deletions src/eval.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
use crate::ast::AST;
use std::collections::HashMap;
use std::fmt;

pub enum EvalResult {
Omen(bool),
Arcana(i64),
Aether(f64),
Rune(String),
Abyss,
}

#[derive(Debug)]
pub enum EvalError {
UndefinedVariable(String),
InvalidOperation(String),
NegativeExponent,
}

impl fmt::Display for EvalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EvalError::UndefinedVariable(var) => write!(f, "Variable {} is not defined!", var),
EvalError::InvalidOperation(op) => write!(f, "Invalid operation: {}", op),
EvalError::NegativeExponent => {
write!(f, "PowArcana operation requires a non-negative exponent!")
}
}
}
}

impl std::error::Error for EvalError {}

pub enum Value {
Omen(bool),
Arcana(i64),
Aether(f64),
Rune(String),
}

Expand All @@ -32,56 +58,100 @@ impl Environment {
}
}

pub fn evaluate(ast: &AST, env: &mut Environment) -> EvalResult {
pub fn evaluate(ast: &AST, env: &mut Environment) -> Result<EvalResult, EvalError> {
match ast {
AST::Arcana(n) => EvalResult::Arcana(*n),
AST::Rune(s) => EvalResult::Rune(s.clone()),
AST::Add(left, right) => match (evaluate(left, env), evaluate(right, env)) {
(EvalResult::Arcana(l), EvalResult::Arcana(r)) => EvalResult::Arcana(l + r),
(EvalResult::Rune(l), EvalResult::Rune(r)) => EvalResult::Rune(format!("{}{}", l, r)),
_ => panic!("Add operation requires either two Arcana or Rune!"),
AST::Omen(b) => Ok(EvalResult::Omen(*b)),
AST::Arcana(n) => Ok(EvalResult::Arcana(*n)),
AST::Aether(n) => Ok(EvalResult::Aether(*n)),
AST::Rune(s) => Ok(EvalResult::Rune(s.clone())),
AST::Add(left, right) => match (evaluate(left, env)?, evaluate(right, env)?) {
(EvalResult::Arcana(l), EvalResult::Arcana(r)) => Ok(EvalResult::Arcana(l + r)),
(EvalResult::Aether(l), EvalResult::Aether(r)) => Ok(EvalResult::Aether(l + r)),
(EvalResult::Rune(l), EvalResult::Rune(r)) => {
Ok(EvalResult::Rune(format!("{}{}", l, r)))
}
_ => Err(EvalError::InvalidOperation(
"Add operation requires either two Arcana, two Aether, or two Rune!".to_string(),
)),
},
AST::Subtract(left, right) => match (evaluate(left, env)?, evaluate(right, env)?) {
(EvalResult::Arcana(l), EvalResult::Arcana(r)) => Ok(EvalResult::Arcana(l - r)),
(EvalResult::Aether(l), EvalResult::Aether(r)) => Ok(EvalResult::Aether(l - r)),
_ => Err(EvalError::InvalidOperation(
"Subtract operation requires either two Arcana or two Aether!".to_string(),
)),
},
AST::Multiply(left, right) => match (evaluate(left, env)?, evaluate(right, env)?) {
(EvalResult::Arcana(l), EvalResult::Arcana(r)) => Ok(EvalResult::Arcana(l * r)),
(EvalResult::Aether(l), EvalResult::Aether(r)) => Ok(EvalResult::Aether(l * r)),
_ => Err(EvalError::InvalidOperation(
"Multiply operation requires either two Arcana or two Aether!".to_string(),
)),
},
AST::Subtract(left, right) => match (evaluate(left, env), evaluate(right, env)) {
(EvalResult::Arcana(l), EvalResult::Arcana(r)) => EvalResult::Arcana(l - r),
_ => panic!("Subtract operation requires either two Arcana!"),
AST::Divide(left, right) => match (evaluate(left, env)?, evaluate(right, env)?) {
(EvalResult::Arcana(l), EvalResult::Arcana(r)) => Ok(EvalResult::Arcana(l / r)),
(EvalResult::Aether(l), EvalResult::Aether(r)) => Ok(EvalResult::Aether(l / r)),
_ => Err(EvalError::InvalidOperation(
"Divide operation requires either two Arcana or two Aether!".to_string(),
)),
},
AST::Multiply(left, right) => match (evaluate(left, env), evaluate(right, env)) {
(EvalResult::Arcana(l), EvalResult::Arcana(r)) => EvalResult::Arcana(l * r),
_ => panic!("Multiply operation requires either two Arcana!"),
AST::PowArcana(left, right) => match (evaluate(left, env)?, evaluate(right, env)?) {
(EvalResult::Arcana(l), EvalResult::Arcana(r)) => {
if r < 0 {
return Err(EvalError::NegativeExponent);
}
Ok(EvalResult::Arcana(l.pow(r as u32)))
}
_ => Err(EvalError::InvalidOperation(
"PowArcana operation requires two Arcana!".to_string(),
)),
},
AST::Divide(left, right) => match (evaluate(left, env), evaluate(right, env)) {
(EvalResult::Arcana(l), EvalResult::Arcana(r)) => EvalResult::Arcana(l / r),
_ => panic!("Divide operation requires either two Arcana!"),
AST::PowAether(left, right) => match (evaluate(left, env)?, evaluate(right, env)?) {
(EvalResult::Aether(l), EvalResult::Aether(r)) => Ok(EvalResult::Aether(l.powf(r))),
_ => Err(EvalError::InvalidOperation(
"PowAether operation requires two Aether!".to_string(),
)),
},
AST::VarAssign(name, value) => {
let value = match evaluate(value, env) {
let value = match evaluate(value, env)? {
EvalResult::Omen(b) => Value::Omen(b),
EvalResult::Arcana(n) => Value::Arcana(n),
EvalResult::Aether(n) => Value::Aether(n),
EvalResult::Rune(s) => Value::Rune(s),
_ => panic!("VarAssign operation requires either Arcana or Rune!"),
_ => {
return Err(EvalError::InvalidOperation(
"VarAssign operation requires either Omen, Arcana, Aether, or Rune!"
.to_string(),
))
}
};
env.set_var(name.clone(), value);
EvalResult::Abyss
Ok(EvalResult::Abyss)
}
AST::Var(name) => match env.get_var(name) {
Some(Value::Arcana(n)) => EvalResult::Arcana(*n),
Some(Value::Rune(s)) => EvalResult::Rune(s.clone()),
None => panic!("Variable {} is not defined!", name),
Some(Value::Omen(b)) => Ok(EvalResult::Omen(*b)),
Some(Value::Arcana(n)) => Ok(EvalResult::Arcana(*n)),
Some(Value::Aether(n)) => Ok(EvalResult::Aether(*n)),
Some(Value::Rune(s)) => Ok(EvalResult::Rune(s.clone())),
None => Err(EvalError::UndefinedVariable(name.clone())),
},
AST::Unveil(args) => {
let outputs: Vec<String> = args
let outputs: Result<Vec<String>, EvalError> = args
.iter()
.map(|arg| evaluate(arg, env))
.collect::<Vec<EvalResult>>()
.collect::<Result<Vec<EvalResult>, EvalError>>()?
.iter()
.map(|result| match result {
EvalResult::Arcana(n) => n.to_string(),
EvalResult::Rune(s) => s.clone(),
EvalResult::Abyss => "".to_string(),
EvalResult::Omen(b) => Ok(b.to_string()),
EvalResult::Arcana(n) => Ok(n.to_string()),
EvalResult::Aether(n) => Ok(n.to_string()),
EvalResult::Rune(s) => Ok(s.replace("\\n", "\n")),
EvalResult::Abyss => Ok("".to_string()),
})
.collect();
let output_str = outputs.join("");
let output_str = outputs?.join("");
println!("{}", output_str);
EvalResult::Abyss
Ok(EvalResult::Abyss)
}
}
}
16 changes: 12 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ fn execute_script(script: &str) {
for inner_pair in pair.into_inner() {
if inner_pair.as_rule() != abyss::parser::Rule::EOI {
let ast = build_ast(inner_pair);
evaluate(&ast, &mut env);
match evaluate(&ast, &mut env) {
Ok(_) => {}
Err(e) => panic!("Error: {}", e),
}
}
}
}
Expand Down Expand Up @@ -75,9 +78,14 @@ fn start_interpreter() {
if inner_pair.as_rule() != abyss::parser::Rule::EOI {
let ast = build_ast(inner_pair);
match evaluate(&ast, &mut env) {
EvalResult::Arcana(n) => println!("{}", n),
EvalResult::Rune(s) => println!("{}", s),
EvalResult::Abyss => {}
Ok(result) => match result {
EvalResult::Omen(b) => println!("{}", b),
EvalResult::Arcana(n) => println!("{}", n),
EvalResult::Aether(n) => println!("{}", n),
EvalResult::Rune(s) => println!("{}", s),
EvalResult::Abyss => {}
},
Err(e) => println!("Error: {}", e),
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@ pub fn build_ast(pair: Pair<Rule>) -> AST {

ast
}
Rule::power => {
let mut inner = pair.into_inner();
let mut ast = build_ast(inner.next().unwrap());

while let Some(operator) = inner.next() {
let right = build_ast(inner.next().unwrap());
ast = match operator.as_str() {
"^" => AST::PowArcana(Box::new(ast), Box::new(right)),
"**" => AST::PowAether(Box::new(ast), Box::new(right)),
_ => unreachable!(),
};
}

ast
}
Rule::term => {
let mut inner = pair.into_inner();
let mut ast = build_ast(inner.next().unwrap()); // 最初のfactorを取得
Expand All @@ -52,10 +67,18 @@ pub fn build_ast(pair: Pair<Rule>) -> AST {
ast
}
Rule::factor => build_ast(pair.into_inner().next().unwrap()),
Rule::omen => {
let value = pair.as_str().parse().unwrap();
AST::Omen(value)
}
Rule::arcana => {
let value = pair.as_str().parse().unwrap();
AST::Arcana(value)
}
Rule::aether => {
let value = pair.as_str().parse().unwrap();
AST::Aether(value)
}
Rule::rune => {
let value = pair.as_str().trim_matches('"').to_string();
AST::Rune(value)
Expand Down
Loading

0 comments on commit 3d2d267

Please sign in to comment.