mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2024-11-21 21:22:44 +00:00
wip: expression parser
This commit is contained in:
parent
067376c74f
commit
d41fa57ca2
22
src/util/expression/ast.rs
Normal file
22
src/util/expression/ast.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum BinaryOp {
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
Modulus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum UnaryOp {
|
||||
Negate,
|
||||
Not,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Node {
|
||||
Float(f64),
|
||||
Integer(i64),
|
||||
BinaryExpr { left: Box<Node>, op: BinaryOp, right: Box<Node> },
|
||||
UnaryExpr { op: UnaryOp, expr: Box<Node> },
|
||||
}
|
266
src/util/expression/lexer.rs
Normal file
266
src/util/expression/lexer.rs
Normal file
|
@ -0,0 +1,266 @@
|
|||
use std::{
|
||||
fmt::{Debug, Display, Formatter},
|
||||
iter::Peekable,
|
||||
str::Chars,
|
||||
};
|
||||
|
||||
use super::token::{ArithmeticOp, SourceLocation, Token};
|
||||
|
||||
pub(super) struct Cursor<'a> {
|
||||
input: &'a str,
|
||||
iter: Peekable<Chars<'a>>,
|
||||
/// Character position in the input string
|
||||
pos: usize,
|
||||
location: SourceLocation,
|
||||
}
|
||||
|
||||
impl<'a> Cursor<'a> {
|
||||
pub fn new(input: &str) -> Cursor {
|
||||
Cursor { input, iter: input.chars().peekable(), pos: 0, location: SourceLocation { line: 1, column: 0 } }
|
||||
}
|
||||
|
||||
pub fn pos(&self) -> usize {
|
||||
self.pos
|
||||
}
|
||||
|
||||
fn inc_pos(&mut self) {
|
||||
let c = self.iter.peek();
|
||||
if c == Some(&'\n') {
|
||||
self.location.line += 1;
|
||||
self.location.column = 0;
|
||||
} else {
|
||||
self.location.column += 1;
|
||||
}
|
||||
self.pos += 1;
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Option<char> {
|
||||
let c = self.iter.next()?;
|
||||
self.inc_pos();
|
||||
Some(c)
|
||||
}
|
||||
|
||||
pub fn next_if(&mut self, func: impl FnOnce(&char) -> bool) -> Option<char> {
|
||||
let c = self.iter.peek()?;
|
||||
if func(c) {
|
||||
self.next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn peek(&mut self) -> Option<char> {
|
||||
self.iter.peek().copied()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct Tokenizer<'a> {
|
||||
cursor: Cursor<'a>,
|
||||
pub tokens: Vec<(Token, SourceLocation)>,
|
||||
}
|
||||
|
||||
impl Tokenizer<'_> {
|
||||
pub fn new(input: &str) -> Tokenizer {
|
||||
Tokenizer { cursor: Cursor::new(input), tokens: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn scan(&mut self) -> LexResult {
|
||||
while let Some(c) = self.cursor.peek() {
|
||||
self.scan_item(c)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next(&mut self) -> LexResult<char> {
|
||||
self.cursor.next().ok_or_else(|| LexError::UnexpectedEndOfInput)
|
||||
}
|
||||
|
||||
fn next_if(&mut self, func: impl FnOnce(&char) -> bool) -> LexResult<char> {
|
||||
self.cursor.next_if(func).ok_or_else(|| LexError::UnexpectedEndOfInput)
|
||||
}
|
||||
|
||||
fn scan_item(&mut self, c: char) -> LexResult {
|
||||
match c {
|
||||
' ' | '\t' | '\n' => {
|
||||
self.cursor.next();
|
||||
return Ok(());
|
||||
}
|
||||
'0'..='9' => self.number(),
|
||||
'+' | '-' | '*' | '/' | '%' => self.operator(),
|
||||
'(' | ')' => self.bracket(),
|
||||
_ => return self.unexpected(c),
|
||||
}
|
||||
}
|
||||
|
||||
fn number(&mut self) -> LexResult {
|
||||
let mut value = String::new();
|
||||
let mut is_fractional = false;
|
||||
let loc = self.cursor.location;
|
||||
|
||||
while let Ok(c) = self.next_if(|c| matches!(c, '0'..='9' | '.')) {
|
||||
if c == '.' {
|
||||
if is_fractional {
|
||||
return self.unexpected(c);
|
||||
}
|
||||
is_fractional = true;
|
||||
}
|
||||
|
||||
value.push(c);
|
||||
}
|
||||
|
||||
self.tokens.push((Token::Number { value, is_fractional }, loc));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn operator(&mut self) -> LexResult {
|
||||
let loc = self.cursor.location;
|
||||
let c = self.next()?;
|
||||
match c {
|
||||
'+' => self.tokens.push((Token::ArithmeticOp(ArithmeticOp::Add), loc)),
|
||||
'-' => self.tokens.push((Token::ArithmeticOp(ArithmeticOp::Subtract), loc)),
|
||||
'*' => self.tokens.push((Token::ArithmeticOp(ArithmeticOp::Multiply), loc)),
|
||||
'/' => self.tokens.push((Token::ArithmeticOp(ArithmeticOp::Divide), loc)),
|
||||
'%' => self.tokens.push((Token::ArithmeticOp(ArithmeticOp::Modulus), loc)),
|
||||
_ => return self.unexpected(c),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bracket(&mut self) -> LexResult {
|
||||
let loc = self.cursor.location;
|
||||
let c = self.next()?;
|
||||
match c {
|
||||
'(' => self.tokens.push((Token::LeftParen, loc)),
|
||||
')' => self.tokens.push((Token::RightParen, loc)),
|
||||
_ => return self.unexpected(c),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unexpected(&self, c: char) -> LexResult {
|
||||
let loc = self.cursor.location;
|
||||
Err(LexError::UnexpectedChar { c, loc })
|
||||
}
|
||||
}
|
||||
|
||||
pub type LexResult<T = ()> = Result<T, LexError>;
|
||||
|
||||
pub enum LexError {
|
||||
UnexpectedChar { c: char, loc: SourceLocation },
|
||||
UnexpectedEndOfInput,
|
||||
}
|
||||
|
||||
impl Display for LexError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
LexError::UnexpectedChar { c, loc } => {
|
||||
write!(f, "Unexpected character '{}' at {}", c, loc)
|
||||
}
|
||||
LexError::UnexpectedEndOfInput => write!(f, "Unexpected end of input"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for LexError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn test_expression(input: &str, result: Vec<Token>) {
|
||||
let mut tok = Tokenizer::new(input);
|
||||
tok.scan().unwrap();
|
||||
// println!("tokens: {:?}", tok.tokens);
|
||||
let tokens = tok.tokens.into_iter().map(|(t, _)| t).collect::<Vec<_>>();
|
||||
assert_eq!(tokens, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_number() {
|
||||
test_expression("1", vec![Token::Number { value: "1".to_string(), is_fractional: false }]);
|
||||
test_expression("1.0", vec![Token::Number { value: "1.0".to_string(), is_fractional: true }]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arithmetic_ops() {
|
||||
test_expression(
|
||||
"1 + 22 * 33 / 4444 + 55555",
|
||||
vec![
|
||||
Token::Number { value: "1".to_string(), is_fractional: false },
|
||||
Token::ArithmeticOp(ArithmeticOp::Add),
|
||||
Token::Number { value: "22".to_string(), is_fractional: false },
|
||||
Token::ArithmeticOp(ArithmeticOp::Multiply),
|
||||
Token::Number { value: "33".to_string(), is_fractional: false },
|
||||
Token::ArithmeticOp(ArithmeticOp::Divide),
|
||||
Token::Number { value: "4444".to_string(), is_fractional: false },
|
||||
Token::ArithmeticOp(ArithmeticOp::Add),
|
||||
Token::Number { value: "55555".to_string(), is_fractional: false },
|
||||
],
|
||||
);
|
||||
|
||||
test_expression(
|
||||
"1.0 + 2.0",
|
||||
vec![
|
||||
Token::Number { value: "1.0".to_string(), is_fractional: true },
|
||||
Token::ArithmeticOp(ArithmeticOp::Add),
|
||||
Token::Number { value: "2.0".to_string(), is_fractional: true },
|
||||
],
|
||||
);
|
||||
test_expression(
|
||||
"2.0 - 3",
|
||||
vec![
|
||||
Token::Number { value: "2.0".to_string(), is_fractional: true },
|
||||
Token::ArithmeticOp(ArithmeticOp::Subtract),
|
||||
Token::Number { value: "3".to_string(), is_fractional: false },
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_brackets() {
|
||||
test_expression(
|
||||
"(1 + 2)",
|
||||
vec![
|
||||
Token::LeftParen,
|
||||
Token::Number { value: "1".to_string(), is_fractional: false },
|
||||
Token::ArithmeticOp(ArithmeticOp::Add),
|
||||
Token::Number { value: "2".to_string(), is_fractional: false },
|
||||
Token::RightParen,
|
||||
],
|
||||
);
|
||||
test_expression(
|
||||
"2 * (3 + 4)",
|
||||
vec![
|
||||
Token::Number { value: "2".to_string(), is_fractional: false },
|
||||
Token::ArithmeticOp(ArithmeticOp::Multiply),
|
||||
Token::LeftParen,
|
||||
Token::Number { value: "3".to_string(), is_fractional: false },
|
||||
Token::ArithmeticOp(ArithmeticOp::Add),
|
||||
Token::Number { value: "4".to_string(), is_fractional: false },
|
||||
Token::RightParen,
|
||||
],
|
||||
);
|
||||
test_expression(
|
||||
"1 + ((2 * 3) + 4)",
|
||||
vec![
|
||||
Token::Number { value: "1".to_string(), is_fractional: false },
|
||||
Token::ArithmeticOp(ArithmeticOp::Add),
|
||||
Token::LeftParen,
|
||||
Token::LeftParen,
|
||||
Token::Number { value: "2".to_string(), is_fractional: false },
|
||||
Token::ArithmeticOp(ArithmeticOp::Multiply),
|
||||
Token::Number { value: "3".to_string(), is_fractional: false },
|
||||
Token::RightParen,
|
||||
Token::ArithmeticOp(ArithmeticOp::Add),
|
||||
Token::Number { value: "4".to_string(), is_fractional: false },
|
||||
Token::RightParen,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
5
src/util/expression/mod.rs
Normal file
5
src/util/expression/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
pub mod ast;
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod token;
|
||||
pub mod vm;
|
206
src/util/expression/parser.rs
Normal file
206
src/util/expression/parser.rs
Normal file
|
@ -0,0 +1,206 @@
|
|||
use std::fmt::{Debug, Display};
|
||||
|
||||
use super::{
|
||||
ast::{BinaryOp, Node, UnaryOp},
|
||||
token::{ArithmeticOp, SourceLocation, Token},
|
||||
};
|
||||
|
||||
pub struct Parser {
|
||||
ast: Vec<Node>,
|
||||
tokens: Vec<(Token, SourceLocation)>,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
// -- the grammar --
|
||||
// expression -> term
|
||||
// term -> factor ( ('+' | '-') factor )*
|
||||
// factor -> unary ( ('*' | '/' | '%') unary )*
|
||||
// unary -> ('!' | '-') unary | primary
|
||||
// primary -> NUMBER | '(' expression ')'
|
||||
|
||||
impl Parser {
|
||||
pub fn new(tokens: Vec<(Token, SourceLocation)>) -> Parser {
|
||||
Parser { tokens, pos: 0, ast: Vec::new() }
|
||||
}
|
||||
|
||||
fn current(&self) -> ParseResult<&(Token, SourceLocation)> {
|
||||
self.tokens.get(self.pos).ok_or_else(|| ParseError::UnexpectedEnd)
|
||||
}
|
||||
|
||||
fn advance(&mut self) {
|
||||
self.pos += 1;
|
||||
}
|
||||
|
||||
fn is_end(&self) -> bool {
|
||||
self.pos >= self.tokens.len()
|
||||
}
|
||||
|
||||
fn expect(&self, token: Token) -> bool {
|
||||
!self.is_end() && self.current().map(|(t, _)| t == &token).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn parse_expr(&mut self) -> ParseResult<Node> {
|
||||
self.parse_term()
|
||||
}
|
||||
|
||||
fn parse_term(&mut self) -> ParseResult<Node> {
|
||||
let mut node = self.parse_factor()?;
|
||||
|
||||
while !self.is_end() {
|
||||
let op = if let (Token::ArithmeticOp(op), _) = self.current()? {
|
||||
match op {
|
||||
ArithmeticOp::Add => BinaryOp::Add,
|
||||
ArithmeticOp::Subtract => BinaryOp::Subtract,
|
||||
_ => break,
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
|
||||
self.advance();
|
||||
let right = self.parse_factor()?;
|
||||
node = Node::BinaryExpr { left: Box::new(node), op, right: Box::new(right) };
|
||||
}
|
||||
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
fn parse_factor(&mut self) -> ParseResult<Node> {
|
||||
let mut node = self.parse_unary()?;
|
||||
|
||||
while !self.is_end() {
|
||||
let op = if let (Token::ArithmeticOp(op), _) = self.current()? {
|
||||
match op {
|
||||
ArithmeticOp::Multiply => BinaryOp::Multiply,
|
||||
ArithmeticOp::Divide => BinaryOp::Divide,
|
||||
ArithmeticOp::Modulus => BinaryOp::Modulus,
|
||||
_ => break,
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
|
||||
self.advance();
|
||||
let right = self.parse_unary()?;
|
||||
node = Node::BinaryExpr { left: Box::new(node), op, right: Box::new(right) };
|
||||
}
|
||||
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
fn parse_unary(&mut self) -> ParseResult<Node> {
|
||||
let current = self.current()?;
|
||||
if let (Token::ArithmeticOp(ArithmeticOp::Subtract), _) = current {
|
||||
self.advance();
|
||||
let expr = self.parse_unary()?;
|
||||
Ok(Node::UnaryExpr { op: UnaryOp::Negate, expr: Box::new(expr) })
|
||||
} else {
|
||||
self.parse_primary()
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_primary(&mut self) -> ParseResult<Node> {
|
||||
if let (Token::Number { .. }, _) = self.current()? {
|
||||
let (current, loc) = self.current()?;
|
||||
let node = match current {
|
||||
Token::Number { value, is_fractional } => {
|
||||
if *is_fractional {
|
||||
Node::Float(value.parse().unwrap())
|
||||
} else {
|
||||
Node::Integer(value.parse().unwrap())
|
||||
}
|
||||
}
|
||||
_ => return Err(ParseError::UnexpectedToken { token: current.clone(), loc: *loc }),
|
||||
};
|
||||
|
||||
self.advance();
|
||||
Ok(node)
|
||||
} else if self.expect(Token::LeftParen) {
|
||||
self.advance();
|
||||
let node = self.parse_expr()?;
|
||||
if !self.expect(Token::RightParen) {
|
||||
return self.unexpected();
|
||||
}
|
||||
self.advance();
|
||||
Ok(node)
|
||||
} else {
|
||||
self.unexpected()
|
||||
}
|
||||
}
|
||||
|
||||
fn unexpected<T>(&self) -> ParseResult<T> {
|
||||
let (token, loc) = self.current()?;
|
||||
Err(ParseError::UnexpectedToken { token: token.clone(), loc: loc.clone() })
|
||||
}
|
||||
|
||||
pub fn parse(&mut self) -> ParseResult {
|
||||
let mut ast = Vec::new();
|
||||
while !self.is_end() {
|
||||
ast.push(self.parse_expr()?);
|
||||
}
|
||||
|
||||
self.ast = ast;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
type ParseResult<T = ()> = Result<T, ParseError>;
|
||||
|
||||
pub enum ParseError {
|
||||
UnexpectedToken { token: Token, loc: SourceLocation },
|
||||
UnexpectedEnd,
|
||||
}
|
||||
|
||||
impl Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ParseError::UnexpectedToken { token, loc } => {
|
||||
write!(f, "Unexpected token {:?} at {}", token, loc)
|
||||
}
|
||||
ParseError::UnexpectedEnd => write!(f, "Unexpected end of input"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::util::expression::{ast::BinaryOp, lexer::Tokenizer};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn parse(input: &str) -> Vec<Node> {
|
||||
let mut tokenizer = Tokenizer::new(input);
|
||||
tokenizer.scan().unwrap();
|
||||
|
||||
let mut parser = Parser::new(tokenizer.tokens);
|
||||
parser.parse().unwrap();
|
||||
|
||||
println!("{:#?}", parser.ast);
|
||||
parser.ast
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser() {
|
||||
parse("-4 + (2 * 3) % 5 + (2 + 2) * 2");
|
||||
|
||||
assert_eq!(
|
||||
parse("1 + 2 * 3"),
|
||||
vec![Node::BinaryExpr {
|
||||
left: Box::new(Node::Integer(1)),
|
||||
op: BinaryOp::Add,
|
||||
right: Box::new(Node::BinaryExpr {
|
||||
left: Box::new(Node::Integer(2)),
|
||||
op: BinaryOp::Multiply,
|
||||
right: Box::new(Node::Integer(3)),
|
||||
}),
|
||||
}]
|
||||
);
|
||||
}
|
||||
}
|
60
src/util/expression/token.rs
Normal file
60
src/util/expression/token.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ArithmeticOp {
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
Modulus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum LogicalOp {
|
||||
And,
|
||||
Or,
|
||||
Not,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ComparisonOp {
|
||||
Equal,
|
||||
NotEqual,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
LessThanOrEqual,
|
||||
GreaterThanOrEqual,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum BitwiseOp {
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
Not,
|
||||
ShiftLeft,
|
||||
ShiftRight,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Token {
|
||||
Number { value: String, is_fractional: bool },
|
||||
ArithmeticOp(ArithmeticOp),
|
||||
LogicalOp(LogicalOp),
|
||||
ComparisonOp(ComparisonOp),
|
||||
BitwiseOp(BitwiseOp),
|
||||
LeftParen,
|
||||
RightParen,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct SourceLocation {
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
}
|
||||
|
||||
impl Display for SourceLocation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "line {}, column {}", self.line, self.column)
|
||||
}
|
||||
}
|
68
src/util/expression/vm.rs
Normal file
68
src/util/expression/vm.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum VMOpcode {
|
||||
// No operation
|
||||
NOP,
|
||||
// Pop the top value off the stack
|
||||
STPOP,
|
||||
// Loads a specified variable onto the stack
|
||||
STLD(u16),
|
||||
// Pushes a number onto the stack
|
||||
STPSH(f64),
|
||||
// Adds the top (int, int) from the stack
|
||||
ADDII,
|
||||
// Adds the top (float, float) from the stack
|
||||
ADDFF,
|
||||
// Subtracts the (int, int) from the stack
|
||||
SUBII,
|
||||
// Subtracts the (float, float) from the stack
|
||||
SUBFF,
|
||||
// Multiplies the top (int, int) from the stack
|
||||
MULII,
|
||||
// Multiplies the top (float, float) from the stack
|
||||
MULFF,
|
||||
// Divides the top (int, int) from the stack
|
||||
DIVII,
|
||||
// Divides the top (float, float) from the stack
|
||||
DIVFF,
|
||||
// Modulus of the top (int, int) from the stack
|
||||
MODII,
|
||||
// Modulus of the top (float, float) from the stack
|
||||
MODFF,
|
||||
// Calls a function with specified index and passes 1 argument from the stack
|
||||
CALL1(u16),
|
||||
// Calls a function with specified index and passes 2 arguments from the stack
|
||||
CALL2(u16),
|
||||
// Calls a function with specified index and passes 3 arguments from the stack
|
||||
CALL3(u16),
|
||||
|
||||
// Intrinsics
|
||||
// f64(int) -> f64
|
||||
INFLOAT2INT,
|
||||
// int(f64) -> int
|
||||
ININT2FLOAT,
|
||||
// frandom() -> f64
|
||||
INFRAND,
|
||||
// irandom() -> int
|
||||
INIRAND,
|
||||
// floor(f64) -> f64
|
||||
INFLOOR,
|
||||
// ceil(f64) -> f64
|
||||
INCEIL,
|
||||
// round(f64) -> f64
|
||||
INROUND,
|
||||
// abs(f64) -> f64
|
||||
INABS,
|
||||
// min(f64, f64) -> f64
|
||||
INMIN,
|
||||
// max(f64, f64) -> f64
|
||||
INMAX,
|
||||
// sqrt(f64) -> f64
|
||||
INSQRT,
|
||||
// cos(f64) -> f64
|
||||
INCOS,
|
||||
// sin(f64) -> f64
|
||||
INSIN,
|
||||
}
|
||||
|
||||
pub struct VM {
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
pub mod atom;
|
||||
pub mod bitvec;
|
||||
pub mod browser;
|
||||
pub mod expression;
|
||||
pub mod rng;
|
||||
|
|
Loading…
Reference in a new issue