runtime prototype
This commit is contained in:
parent
29b8d9644f
commit
c81c728d54
13
src/lexer.rs
13
src/lexer.rs
|
@ -1,16 +1,21 @@
|
||||||
use std::{fmt, iter::Peekable};
|
use std::{fmt, iter::Peekable, rc::Rc};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Ident(String);
|
pub struct Ident(String);
|
||||||
impl fmt::Display for Ident {
|
impl fmt::Display for Ident {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.0.fmt(f)
|
self.0.fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AsRef<str> for Ident {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
String(String),
|
String(Rc<String>),
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Nil,
|
Nil,
|
||||||
|
@ -234,7 +239,7 @@ where
|
||||||
},
|
},
|
||||||
c if c == delim => {
|
c if c == delim => {
|
||||||
self.eat();
|
self.eat();
|
||||||
break Ok(Token::Literal(Literal::String(str)));
|
break Ok(Token::Literal(Literal::String(Rc::new(str))));
|
||||||
}
|
}
|
||||||
_ => str.push(self.next_unwrap()),
|
_ => str.push(self.next_unwrap()),
|
||||||
}
|
}
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -1,9 +1,13 @@
|
||||||
use std::time::Instant;
|
use std::{rc::Rc, time::Instant};
|
||||||
|
|
||||||
use crate::{lexer::Lexer, parser::Parser};
|
use crate::{
|
||||||
|
lexer::Lexer,
|
||||||
|
parser::{Expr, Parser},
|
||||||
|
};
|
||||||
|
|
||||||
mod lexer;
|
mod lexer;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
mod runtime;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let script = std::fs::read_to_string("./start.leaf").unwrap();
|
let script = std::fs::read_to_string("./start.leaf").unwrap();
|
||||||
|
@ -12,5 +16,6 @@ fn main() {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let block = parser.parse().unwrap();
|
let block = parser.parse().unwrap();
|
||||||
println!("Parse took {:?}", start.elapsed());
|
println!("Parse took {:?}", start.elapsed());
|
||||||
parser::util::display(parser::Expr::Block(block));
|
// parser::util::display(&Rc::new(Expr::Block(Rc::new(block))));
|
||||||
|
runtime::exec(&block).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{fmt, iter::Peekable};
|
use std::{fmt, iter::Peekable, rc::Rc};
|
||||||
|
|
||||||
use crate::lexer::{Associativity, LexError, Literal, Precedence, Token};
|
use crate::lexer::{Associativity, LexError, Literal, Precedence, Token};
|
||||||
|
|
||||||
|
@ -7,37 +7,37 @@ pub mod util;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
// Data and variables
|
// Data and variables
|
||||||
Assignment(Box<Expr>, Box<Expr>),
|
Assignment(Rc<Expr>, Rc<Expr>),
|
||||||
Literal(Literal),
|
Literal(Literal),
|
||||||
// Runtime datatypes
|
// Runtime datatypes
|
||||||
Block(Block),
|
Block(Rc<Block>),
|
||||||
// Control flow
|
// Control flow
|
||||||
Return(Box<Expr>),
|
Return(Rc<Expr>),
|
||||||
Call(Box<Expr>, Vec<Expr>),
|
Call(Rc<Expr>, Vec<Rc<Expr>>),
|
||||||
// Unary operations
|
// Unary operations
|
||||||
Negate(Box<Expr>),
|
Negate(Rc<Expr>),
|
||||||
Not(Box<Expr>),
|
Not(Rc<Expr>),
|
||||||
// Binary operations: logical
|
// Binary operations: logical
|
||||||
EqualTo(Box<Expr>, Box<Expr>),
|
EqualTo(Rc<Expr>, Rc<Expr>),
|
||||||
NotEqualTo(Box<Expr>, Box<Expr>),
|
NotEqualTo(Rc<Expr>, Rc<Expr>),
|
||||||
And(Box<Expr>, Box<Expr>),
|
And(Rc<Expr>, Rc<Expr>),
|
||||||
Or(Box<Expr>, Box<Expr>),
|
Or(Rc<Expr>, Rc<Expr>),
|
||||||
// Binary operations: comparison
|
// Binary operations: comparison
|
||||||
LessThan(Box<Expr>, Box<Expr>),
|
LessThan(Rc<Expr>, Rc<Expr>),
|
||||||
LessThanOrEqualTo(Box<Expr>, Box<Expr>),
|
LessThanOrEqualTo(Rc<Expr>, Rc<Expr>),
|
||||||
GreaterThan(Box<Expr>, Box<Expr>),
|
GreaterThan(Rc<Expr>, Rc<Expr>),
|
||||||
GreaterThanOrEqualTo(Box<Expr>, Box<Expr>),
|
GreaterThanOrEqualTo(Rc<Expr>, Rc<Expr>),
|
||||||
// Binary operations: arithmetic
|
// Binary operations: arithmetic
|
||||||
Add(Box<Expr>, Box<Expr>),
|
Add(Rc<Expr>, Rc<Expr>),
|
||||||
Subtract(Box<Expr>, Box<Expr>),
|
Subtract(Rc<Expr>, Rc<Expr>),
|
||||||
Multiply(Box<Expr>, Box<Expr>),
|
Multiply(Rc<Expr>, Rc<Expr>),
|
||||||
Divide(Box<Expr>, Box<Expr>),
|
Divide(Rc<Expr>, Rc<Expr>),
|
||||||
Exponent(Box<Expr>, Box<Expr>),
|
Exponent(Rc<Expr>, Rc<Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Block {
|
pub struct Block {
|
||||||
pub exprs: Vec<Expr>,
|
pub exprs: Vec<Rc<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -93,7 +93,7 @@ where
|
||||||
did_skip = true;
|
did_skip = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return did_skip;
|
did_skip
|
||||||
}
|
}
|
||||||
fn try_peek(&mut self) -> Result<&Token> {
|
fn try_peek(&mut self) -> Result<&Token> {
|
||||||
// Peek doesn't advance the token stream, so
|
// Peek doesn't advance the token stream, so
|
||||||
|
@ -108,10 +108,10 @@ where
|
||||||
self.tokens.next().ok_or(ParseError::UnexpectedEnd)
|
self.tokens.next().ok_or(ParseError::UnexpectedEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expr(&mut self, min_prec: Precedence, in_group: bool) -> Result<Box<Expr>> {
|
fn parse_expr(&mut self, min_prec: Precedence, in_group: bool) -> Result<Rc<Expr>> {
|
||||||
let mut lhs = match self.try_next()? {
|
let mut lhs = match self.try_next()? {
|
||||||
// literal
|
// literal
|
||||||
Token::Literal(lit) => Box::new(Expr::Literal(lit)),
|
Token::Literal(lit) => Rc::new(Expr::Literal(lit)),
|
||||||
|
|
||||||
// start of group
|
// start of group
|
||||||
Token::ParenOpen => {
|
Token::ParenOpen => {
|
||||||
|
@ -126,14 +126,14 @@ where
|
||||||
let b = self.parse_block(true)?;
|
let b = self.parse_block(true)?;
|
||||||
// skip curly brace
|
// skip curly brace
|
||||||
self.eat();
|
self.eat();
|
||||||
Box::new(Expr::Block(b))
|
Rc::new(Expr::Block(Rc::new(b)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// unary ops!! (prefix)
|
// unary ops!! (prefix)
|
||||||
t if t.prefix_precedence().is_some() => {
|
t if t.prefix_precedence().is_some() => {
|
||||||
let prec = t.prefix_precedence().unwrap();
|
let prec = t.prefix_precedence().unwrap();
|
||||||
let rhs = self.parse_expr(prec, in_group)?;
|
let rhs = self.parse_expr(prec, in_group)?;
|
||||||
Box::new(match t {
|
Rc::new(match t {
|
||||||
Token::Minus => Expr::Negate(rhs),
|
Token::Minus => Expr::Negate(rhs),
|
||||||
Token::Not => Expr::Not(rhs),
|
Token::Not => Expr::Not(rhs),
|
||||||
Token::Return => Expr::Return(rhs),
|
Token::Return => Expr::Return(rhs),
|
||||||
|
@ -168,7 +168,7 @@ where
|
||||||
|
|
||||||
let mut exprs = Vec::new();
|
let mut exprs = Vec::new();
|
||||||
while !matches!(self.try_peek()?, Token::ParenClose) {
|
while !matches!(self.try_peek()?, Token::ParenClose) {
|
||||||
exprs.push(*self.parse_expr(Precedence::Min, false)?);
|
exprs.push(self.parse_expr(Precedence::Min, false)?);
|
||||||
|
|
||||||
// Continue if there is a comma,
|
// Continue if there is a comma,
|
||||||
// ignore closing parens
|
// ignore closing parens
|
||||||
|
@ -181,7 +181,7 @@ where
|
||||||
// eat closing paren
|
// eat closing paren
|
||||||
self.eat();
|
self.eat();
|
||||||
|
|
||||||
lhs = Box::new(Expr::Call(lhs, exprs));
|
lhs = Rc::new(Expr::Call(lhs, exprs));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ where
|
||||||
let rhs = self.parse_expr(prec, in_group)?;
|
let rhs = self.parse_expr(prec, in_group)?;
|
||||||
|
|
||||||
// join to lhs
|
// join to lhs
|
||||||
lhs = Box::new(match op {
|
lhs = Rc::new(match op {
|
||||||
// equality
|
// equality
|
||||||
Token::EqualTo => Expr::EqualTo(lhs, rhs),
|
Token::EqualTo => Expr::EqualTo(lhs, rhs),
|
||||||
Token::NotEqualTo => Expr::NotEqualTo(lhs, rhs),
|
Token::NotEqualTo => Expr::NotEqualTo(lhs, rhs),
|
||||||
|
@ -243,7 +243,7 @@ where
|
||||||
Err(ParseError::UnexpectedEnd) if !in_block => break,
|
Err(ParseError::UnexpectedEnd) if !in_block => break,
|
||||||
|
|
||||||
// try to parse expr
|
// try to parse expr
|
||||||
Ok(_) => exprs.push(*self.parse_expr(Precedence::Min, false)?),
|
Ok(_) => exprs.push(self.parse_expr(Precedence::Min, false)?),
|
||||||
|
|
||||||
// invalid
|
// invalid
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::parser::Expr;
|
use crate::parser::Expr;
|
||||||
|
|
||||||
pub fn display(e: Expr) {
|
pub fn display(e: &Rc<Expr>) {
|
||||||
let result = fmt_expr(e, 0);
|
let result = fmt_expr(e, 0);
|
||||||
println!("{result}");
|
println!("{result}");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_binop(left: Expr, right: Expr, op: &str, depth: usize) -> String {
|
fn fmt_binop(left: &Rc<Expr>, right: &Rc<Expr>, op: &str, depth: usize) -> String {
|
||||||
format!(
|
format!(
|
||||||
"({} {} {})",
|
"({} {} {})",
|
||||||
fmt_expr(left, depth),
|
fmt_expr(left, depth),
|
||||||
|
@ -14,15 +16,15 @@ fn fmt_binop(left: Expr, right: Expr, op: &str, depth: usize) -> String {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_expr(e: Expr, depth: usize) -> String {
|
fn fmt_expr(e: &Rc<Expr>, depth: usize) -> String {
|
||||||
match e {
|
match &**e {
|
||||||
Expr::Assignment(l, r) => fmt_binop(*l, *r, "=", depth),
|
Expr::Assignment(l, r) => fmt_binop(l, r, "=", depth),
|
||||||
Expr::Literal(l) => l.to_string(),
|
Expr::Literal(l) => l.to_string(),
|
||||||
Expr::Call(l, r) => {
|
Expr::Call(l, r) => {
|
||||||
let mut result = fmt_expr(*l, depth);
|
let mut result = fmt_expr(l, depth);
|
||||||
result.push('(');
|
result.push('(');
|
||||||
let len = r.len();
|
let len = r.len();
|
||||||
for (i, e) in r.into_iter().enumerate() {
|
for (i, e) in r.iter().enumerate() {
|
||||||
result.push_str(&fmt_expr(e, depth));
|
result.push_str(&fmt_expr(e, depth));
|
||||||
if i + 1 != len {
|
if i + 1 != len {
|
||||||
result.push_str(", ");
|
result.push_str(", ");
|
||||||
|
@ -31,11 +33,11 @@ fn fmt_expr(e: Expr, depth: usize) -> String {
|
||||||
result.push(')');
|
result.push(')');
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
Expr::Return(l) => format!("return {}", fmt_expr(*l, depth)),
|
Expr::Return(r) => format!("return {}", fmt_expr(r, depth)),
|
||||||
Expr::Block(b) => {
|
Expr::Block(b) => {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
let len = b.exprs.len();
|
let len = b.exprs.len();
|
||||||
for (i, expr) in b.exprs.into_iter().enumerate() {
|
for (i, expr) in b.exprs.iter().enumerate() {
|
||||||
result.push_str(&" ".repeat(depth));
|
result.push_str(&" ".repeat(depth));
|
||||||
result.push_str(&fmt_expr(expr, depth + 1));
|
result.push_str(&fmt_expr(expr, depth + 1));
|
||||||
if depth != 0 || i + 1 != len {
|
if depth != 0 || i + 1 != len {
|
||||||
|
@ -47,20 +49,20 @@ fn fmt_expr(e: Expr, depth: usize) -> String {
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
Expr::Negate(l) => format!("(-{})", fmt_expr(*l, depth)),
|
Expr::Negate(r) => format!("(-{})", fmt_expr(r, depth)),
|
||||||
Expr::Not(l) => format!("(!{})", fmt_expr(*l, depth)),
|
Expr::Not(r) => format!("(!{})", fmt_expr(r, depth)),
|
||||||
Expr::EqualTo(l, r) => fmt_binop(*l, *r, "==", depth),
|
Expr::EqualTo(l, r) => fmt_binop(l, r, "==", depth),
|
||||||
Expr::NotEqualTo(l, r) => fmt_binop(*l, *r, "!=", depth),
|
Expr::NotEqualTo(l, r) => fmt_binop(l, r, "!=", depth),
|
||||||
Expr::And(l, r) => fmt_binop(*l, *r, "&&", depth),
|
Expr::And(l, r) => fmt_binop(l, r, "&&", depth),
|
||||||
Expr::Or(l, r) => fmt_binop(*l, *r, "||", depth),
|
Expr::Or(l, r) => fmt_binop(l, r, "||", depth),
|
||||||
Expr::LessThan(l, r) => fmt_binop(*l, *r, "<", depth),
|
Expr::LessThan(l, r) => fmt_binop(l, r, "<", depth),
|
||||||
Expr::LessThanOrEqualTo(l, r) => fmt_binop(*l, *r, "<=", depth),
|
Expr::LessThanOrEqualTo(l, r) => fmt_binop(l, r, "<=", depth),
|
||||||
Expr::GreaterThan(l, r) => fmt_binop(*l, *r, ">", depth),
|
Expr::GreaterThan(l, r) => fmt_binop(l, r, ">", depth),
|
||||||
Expr::GreaterThanOrEqualTo(l, r) => fmt_binop(*l, *r, ">=", depth),
|
Expr::GreaterThanOrEqualTo(l, r) => fmt_binop(l, r, ">=", depth),
|
||||||
Expr::Add(l, r) => fmt_binop(*l, *r, "+", depth),
|
Expr::Add(l, r) => fmt_binop(l, r, "+", depth),
|
||||||
Expr::Subtract(l, r) => fmt_binop(*l, *r, "-", depth),
|
Expr::Subtract(l, r) => fmt_binop(l, r, "-", depth),
|
||||||
Expr::Multiply(l, r) => fmt_binop(*l, *r, "*", depth),
|
Expr::Multiply(l, r) => fmt_binop(l, r, "*", depth),
|
||||||
Expr::Divide(l, r) => fmt_binop(*l, *r, "/", depth),
|
Expr::Divide(l, r) => fmt_binop(l, r, "/", depth),
|
||||||
Expr::Exponent(l, r) => fmt_binop(*l, *r, "^", depth),
|
Expr::Exponent(l, r) => fmt_binop(l, r, "^", depth),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,303 @@
|
||||||
|
use std::{cell::LazyCell, collections::HashMap, fmt, fmt::Write as _, rc::Rc};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
lexer::{Ident, Literal},
|
||||||
|
parser::{Block, Expr},
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! type_error {
|
||||||
|
() => {
|
||||||
|
return Err(RuntimeError::Type.into())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
macro_rules! uncallable {
|
||||||
|
() => {
|
||||||
|
return Err(RuntimeError::Uncallable.into())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
use Expr as E;
|
||||||
|
use Literal as L;
|
||||||
|
use Value as V;
|
||||||
|
|
||||||
|
fn builtin_print(args: Args) -> RuntimeResult {
|
||||||
|
let mut result = String::new();
|
||||||
|
for v in args {
|
||||||
|
let _ = write!(result, "{v}\t");
|
||||||
|
}
|
||||||
|
println!("{result}");
|
||||||
|
Ok(V::nil())
|
||||||
|
}
|
||||||
|
fn builtin_if(args: Args) -> RuntimeResult {
|
||||||
|
let Some(Value::Boolean(cond)) = args.get(0).map(|v| &**v) else {
|
||||||
|
type_error!()
|
||||||
|
};
|
||||||
|
let cond = *cond;
|
||||||
|
|
||||||
|
let b_else = args.get(2).map(|v| &**v);
|
||||||
|
let b_if = args.get(1).map(|v| &**v);
|
||||||
|
|
||||||
|
let empty = Vec::new();
|
||||||
|
Ok(match (b_if, b_else) {
|
||||||
|
(Some(V::Block(b_if)), _) if cond => exec_block(&b_if, empty)?,
|
||||||
|
(_, Some(V::Block(b_else))) if !cond => exec_block(&b_else, empty)?,
|
||||||
|
(Some(V::Nil) | None, _) => V::nil(),
|
||||||
|
(_, Some(V::Nil) | None) => V::nil(),
|
||||||
|
(_, _) => uncallable!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static NIL: LazyCell<Rc<Value>> = const { LazyCell::new(|| Rc::new(V::Nil)) };
|
||||||
|
static TRUE: LazyCell<Rc<Value>> = const { LazyCell::new(|| Rc::new(V::Boolean(true))) };
|
||||||
|
static FALSE: LazyCell<Rc<Value>> = const { LazyCell::new(|| Rc::new(V::Boolean(false))) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Value {
|
||||||
|
String(Rc<String>),
|
||||||
|
Integer(i64),
|
||||||
|
Boolean(bool),
|
||||||
|
Block(Rc<Block>),
|
||||||
|
BuiltinFn(fn(Args) -> RuntimeResult),
|
||||||
|
Nil,
|
||||||
|
}
|
||||||
|
impl Value {
|
||||||
|
fn bool(b: bool) -> Rc<Self> {
|
||||||
|
if b {
|
||||||
|
Self::bool_true()
|
||||||
|
} else {
|
||||||
|
Self::bool_false()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn int(i: i64) -> Rc<Self> {
|
||||||
|
Rc::new(V::Integer(i))
|
||||||
|
}
|
||||||
|
fn str(s: Rc<String>) -> Rc<Self> {
|
||||||
|
Rc::new(V::String(s))
|
||||||
|
}
|
||||||
|
fn block(b: Rc<Block>) -> Rc<Self> {
|
||||||
|
Rc::new(V::Block(b))
|
||||||
|
}
|
||||||
|
fn builtin_fn(f: fn(Args) -> RuntimeResult) -> Rc<Self> {
|
||||||
|
Rc::new(V::BuiltinFn(f))
|
||||||
|
}
|
||||||
|
fn bool_true() -> Rc<Self> {
|
||||||
|
TRUE.with(|v| (**v).clone())
|
||||||
|
}
|
||||||
|
fn bool_false() -> Rc<Self> {
|
||||||
|
FALSE.with(|v| (**v).clone())
|
||||||
|
}
|
||||||
|
fn nil() -> Rc<Self> {
|
||||||
|
NIL.with(|v| (**v).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PartialEq for Value {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(V::String(s1), V::String(s2)) => s1 == s2,
|
||||||
|
(V::Integer(i1), V::Integer(i2)) => i1 == i2,
|
||||||
|
(V::Boolean(b1), V::Boolean(b2)) => b1 == b2,
|
||||||
|
(V::Nil, V::Nil) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl fmt::Display for Value {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
V::String(s) => write!(f, "{s}"),
|
||||||
|
V::Integer(i) => write!(f, "{i}"),
|
||||||
|
V::Boolean(b) => write!(f, "{b}"),
|
||||||
|
V::Block(_b) => write!(f, "<block>"),
|
||||||
|
V::BuiltinFn(_f) => write!(f, "<builtin>"),
|
||||||
|
V::Nil => write!(f, "nil"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RuntimeError {
|
||||||
|
Type,
|
||||||
|
Uncallable,
|
||||||
|
}
|
||||||
|
impl fmt::Display for RuntimeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Type => write!(f, "type error"),
|
||||||
|
Self::Uncallable => write!(f, "uncallable"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub type RuntimeResult = Result<Rc<Value>, RuntimeError>;
|
||||||
|
|
||||||
|
type Args = Vec<Rc<Value>>;
|
||||||
|
|
||||||
|
enum EvalOutcome {
|
||||||
|
EarlyReturn(Rc<Value>),
|
||||||
|
Error(RuntimeError),
|
||||||
|
}
|
||||||
|
impl From<RuntimeError> for EvalOutcome {
|
||||||
|
fn from(err: RuntimeError) -> Self {
|
||||||
|
Self::Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(scope: &mut HashMap<Ident, Rc<Value>>, e: &Rc<Expr>) -> Result<Rc<Value>, EvalOutcome> {
|
||||||
|
Ok(match &**e {
|
||||||
|
E::Assignment(l, r) => {
|
||||||
|
let Expr::Literal(Literal::Ident(id)) = &**l else {
|
||||||
|
type_error!()
|
||||||
|
};
|
||||||
|
let v = eval(scope, r)?;
|
||||||
|
scope.insert(id.clone(), v.clone());
|
||||||
|
v
|
||||||
|
}
|
||||||
|
E::Literal(L::Ident(id)) => match id.as_ref() {
|
||||||
|
"print" => V::builtin_fn(builtin_print),
|
||||||
|
"if" => V::builtin_fn(builtin_if),
|
||||||
|
_ => scope.get(id).cloned().unwrap_or_else(V::nil),
|
||||||
|
},
|
||||||
|
E::Literal(L::String(s)) => V::str(s.clone()),
|
||||||
|
E::Literal(L::Integer(i)) => V::int(*i),
|
||||||
|
E::Literal(L::Boolean(b)) => V::bool(*b),
|
||||||
|
E::Literal(L::Nil) => V::nil(),
|
||||||
|
E::Block(b) => V::block(b.clone()),
|
||||||
|
E::Return(r) => {
|
||||||
|
let r = eval(scope, r)?;
|
||||||
|
return Err(EvalOutcome::EarlyReturn(r));
|
||||||
|
}
|
||||||
|
E::Call(l, r) => {
|
||||||
|
let l = eval(scope, l)?;
|
||||||
|
let args = r.iter().map(|e| eval(scope, e)).collect::<Result<_, _>>()?;
|
||||||
|
match &*l {
|
||||||
|
V::BuiltinFn(f) => f(args)?,
|
||||||
|
V::Block(b) => exec_block(b, args)?,
|
||||||
|
_ => uncallable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
E::Negate(r) => {
|
||||||
|
let l = eval(scope, r)?;
|
||||||
|
let V::Integer(i) = *l else { type_error!() };
|
||||||
|
V::int(-i)
|
||||||
|
}
|
||||||
|
E::Not(r) => {
|
||||||
|
let l = eval(scope, r)?;
|
||||||
|
let V::Boolean(b) = *l else { type_error!() };
|
||||||
|
V::bool(!b)
|
||||||
|
}
|
||||||
|
E::EqualTo(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::bool(*l == *r)
|
||||||
|
}
|
||||||
|
E::NotEqualTo(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::bool(*l != *r)
|
||||||
|
}
|
||||||
|
E::And(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::bool(if let (V::Boolean(b1), V::Boolean(b2)) = (&*l, &*r) {
|
||||||
|
*b1 && *b2
|
||||||
|
} else {
|
||||||
|
type_error!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E::Or(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::bool(if let (V::Boolean(b1), V::Boolean(b2)) = (&*l, &*r) {
|
||||||
|
*b1 || *b2
|
||||||
|
} else {
|
||||||
|
type_error!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E::LessThan(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::bool(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) {
|
||||||
|
i1 < i2
|
||||||
|
} else {
|
||||||
|
type_error!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E::LessThanOrEqualTo(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::bool(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) {
|
||||||
|
i1 <= i2
|
||||||
|
} else {
|
||||||
|
type_error!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E::GreaterThan(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::bool(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) {
|
||||||
|
i1 > i2
|
||||||
|
} else {
|
||||||
|
type_error!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E::GreaterThanOrEqualTo(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::bool(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) {
|
||||||
|
i1 >= i2
|
||||||
|
} else {
|
||||||
|
type_error!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E::Add(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::int(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) {
|
||||||
|
i1 + i2
|
||||||
|
} else {
|
||||||
|
type_error!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E::Subtract(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::int(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) {
|
||||||
|
i1 + i2
|
||||||
|
} else {
|
||||||
|
type_error!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E::Multiply(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::int(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) {
|
||||||
|
i1 * i2
|
||||||
|
} else {
|
||||||
|
type_error!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E::Divide(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::int(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) {
|
||||||
|
i1 / i2
|
||||||
|
} else {
|
||||||
|
type_error!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
E::Exponent(l, r) => {
|
||||||
|
let (l, r) = (eval(scope, l)?, eval(scope, r)?);
|
||||||
|
V::int(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) {
|
||||||
|
i1.pow(*i2 as u32)
|
||||||
|
} else {
|
||||||
|
type_error!()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec_block(b: &Block, _args: Args) -> RuntimeResult {
|
||||||
|
let mut scope = HashMap::new();
|
||||||
|
|
||||||
|
for e in &b.exprs {
|
||||||
|
match eval(&mut scope, e) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(EvalOutcome::EarlyReturn(v)) => return Ok(v),
|
||||||
|
Err(EvalOutcome::Error(err)) => return Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(V::nil())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exec(b: &Block) -> RuntimeResult {
|
||||||
|
exec_block(b, Vec::new())
|
||||||
|
}
|
Loading…
Reference in New Issue