slightly improved arrays
This commit is contained in:
parent
353b556734
commit
4cc77ce885
4 changed files with 75 additions and 241 deletions
|
@ -1,8 +1,10 @@
|
|||
use crate::parser;
|
||||
use crate::types::{Value, ValueType};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Bytecode {
|
||||
pub code: Vec<Instruction>,
|
||||
pub data: Vec<Data>,
|
||||
pub data: Vec<Value>,
|
||||
}
|
||||
|
||||
type Register = u8;
|
||||
|
@ -40,26 +42,6 @@ pub enum Instruction {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Data {
|
||||
Unsigned(u64),
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
String(String),
|
||||
Boolean(bool),
|
||||
Array(Vec<Data>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum DataType {
|
||||
Unsigned,
|
||||
Integer,
|
||||
Float,
|
||||
String,
|
||||
Boolean,
|
||||
Array(Box<DataType>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BinaryOperation {
|
||||
Add, Subtract, Multiply, Divide,
|
||||
|
@ -73,32 +55,32 @@ pub enum UnaryOperation {
|
|||
|
||||
pub struct Compiler {
|
||||
code: Vec<Instruction>,
|
||||
data: Vec<Data>,
|
||||
data: Vec<Value>,
|
||||
registers: [bool; 256],
|
||||
}
|
||||
|
||||
fn get_binary_type(operation: &BinaryOperation, data_type_left: &DataType, data_type_right: &DataType) -> DataType {
|
||||
fn get_binary_type(operation: &BinaryOperation, data_type_left: &ValueType, data_type_right: &ValueType) -> ValueType {
|
||||
use BinaryOperation::*;
|
||||
match (operation, data_type_left, data_type_right) {
|
||||
(Add | Subtract | Multiply | Divide, DataType::Float, DataType::Float) => DataType::Float,
|
||||
(In, item, DataType::Array(generic)) if item == generic.as_ref() => DataType::Boolean,
|
||||
(Add, DataType::String, DataType::String) => DataType::String,
|
||||
(Add | Subtract | Multiply | Divide, ValueType::Float, ValueType::Float) => ValueType::Float,
|
||||
(In, item, ValueType::Array(generic)) if item == generic.as_ref() => ValueType::Boolean,
|
||||
(Add, ValueType::String, ValueType::String) => ValueType::String,
|
||||
_ => panic!("invalid types"),
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
fn tree(&mut self, tree: &parser::Tree) -> (DataType, Register) {
|
||||
fn tree(&mut self, tree: &parser::Tree) -> (ValueType, Register) {
|
||||
match tree {
|
||||
parser::Tree::Value { term, kind } => {
|
||||
use crate::lexer::TokenType;
|
||||
let (data_type, data) = match kind {
|
||||
TokenType::Float => {
|
||||
let float: f64 = term.parse().unwrap();
|
||||
(DataType::Float, Data::Float(float))
|
||||
(ValueType::Float, Value::Float(float))
|
||||
},
|
||||
TokenType::String => {
|
||||
(DataType::String, Data::String(term[1..term.len() - 1].to_string()))
|
||||
(ValueType::String, Value::String(term[1..term.len() - 1].to_string()))
|
||||
},
|
||||
_ => todo!(),
|
||||
};
|
||||
|
@ -146,21 +128,23 @@ impl Compiler {
|
|||
(data_type, reg_dest)
|
||||
},
|
||||
parser::Tree::Array { terms } => {
|
||||
// TODO: 0 length arrays
|
||||
let reg = self.next_register();
|
||||
let mut array_kind = None;
|
||||
self.code.push(Instruction::Array { dest: reg });
|
||||
for term in terms {
|
||||
let (kind, reg_term) = self.tree(term);
|
||||
if *array_kind.get_or_insert_with(|| kind.clone()) != kind {
|
||||
let mut terms = terms.iter();
|
||||
let (kind, first_reg) = self.tree(terms.next().expect("needs at least 1 item for now"));
|
||||
let mut array = vec![self.data[first_reg as usize].clone()];
|
||||
while let Some(term) = terms.next() {
|
||||
let (term_kind, term_reg) = self.tree(term);
|
||||
if kind != kind {
|
||||
panic!("nope");
|
||||
}
|
||||
self.code.push(Instruction::ArrayAppend {
|
||||
source: reg_term,
|
||||
dest: reg,
|
||||
});
|
||||
array.push(self.data[term_reg as usize].clone());
|
||||
}
|
||||
(DataType::Array(Box::new(array_kind.unwrap())), reg)
|
||||
let reg = self.next_register();
|
||||
let data = self.push_data(Value::Array { kind: kind.clone(), data: array });
|
||||
self.code.push(Instruction::Load {
|
||||
input: data,
|
||||
dest: reg,
|
||||
});
|
||||
(ValueType::Array(Box::new(kind)), reg)
|
||||
},
|
||||
unknown => todo!("unknown token {:?}", unknown),
|
||||
}
|
||||
|
@ -168,7 +152,7 @@ impl Compiler {
|
|||
|
||||
fn next_register(&mut self) -> Register {
|
||||
let index = self.registers.iter()
|
||||
.position(|b| *b == false)
|
||||
.position(|b| !b)
|
||||
.expect("ran out of registers");
|
||||
self.registers[index] = true;
|
||||
index as u8
|
||||
|
@ -178,7 +162,7 @@ impl Compiler {
|
|||
self.registers[reg as usize] = false;
|
||||
}
|
||||
|
||||
fn push_data(&mut self, data: Data) -> u8 {
|
||||
fn push_data(&mut self, data: Value) -> u8 {
|
||||
let len = self.data.len();
|
||||
self.data.push(data);
|
||||
len as u8
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -8,8 +8,9 @@ mod lexer;
|
|||
mod parser;
|
||||
mod compiler;
|
||||
mod runner;
|
||||
mod types;
|
||||
|
||||
use runner::Value;
|
||||
use types::Value;
|
||||
use parser::Kind;
|
||||
|
||||
fn test(code: &str) -> Value {
|
||||
|
@ -20,6 +21,7 @@ fn test(code: &str) -> Value {
|
|||
.collect();
|
||||
let tree = parser::expr(&mut tokens.iter().peekable(), 0);
|
||||
let bytecode = compiler::expr(&tree);
|
||||
dbg!(&bytecode);
|
||||
let result = runner::execute(&bytecode);
|
||||
dbg!(&result);
|
||||
result
|
||||
|
@ -55,22 +57,22 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_number() {
|
||||
assert_eq!(test("123"), Value::Number(123.0));
|
||||
assert_eq!(test("123"), Value::Float(123.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negate() {
|
||||
assert_eq!(test("-123"), Value::Number(-123.0));
|
||||
assert_eq!(test("-123"), Value::Float(-123.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr() {
|
||||
assert_eq!(test("2 + 3 * 4"), Value::Number(14.0));
|
||||
assert_eq!(test("2 + 3 * 4"), Value::Float(14.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_paran() {
|
||||
assert_eq!(test("(2 + 3) * 4"), Value::Number(20.0));
|
||||
assert_eq!(test("(2 + 3) * 4"), Value::Float(20.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
212
src/runner.rs
212
src/runner.rs
|
@ -1,177 +1,13 @@
|
|||
use crate::parser;
|
||||
use crate::lexer;
|
||||
use crate::types;
|
||||
use crate::compiler;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ValueType {
|
||||
Number,
|
||||
String,
|
||||
Boolean,
|
||||
Array(Box<ValueType>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Value {
|
||||
Number(f64),
|
||||
String(String),
|
||||
Boolean(bool),
|
||||
Array { kind: ValueType, values: Vec<Value> },
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn get_type(&self) -> ValueType {
|
||||
match self {
|
||||
Self::Number(_) => ValueType::Number,
|
||||
Self::String(_) => ValueType::String,
|
||||
Self::Boolean(_) => ValueType::Boolean,
|
||||
Self::Array { kind, .. } => ValueType::Array(Box::new(kind.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_old(tree: &parser::Tree) -> Value {
|
||||
use parser::Tree;
|
||||
match tree {
|
||||
Tree::Value { term, kind } => {
|
||||
match kind {
|
||||
lexer::TokenType::Float => Value::Number(term.parse().expect("invalid number")),
|
||||
lexer::TokenType::Int => Value::Number(term.parse().expect("invalid number")),
|
||||
lexer::TokenType::String => Value::String(term[1..term.len() - 1].to_string()),
|
||||
lexer::TokenType::Ident => match term.as_str() {
|
||||
"true" => Value::Boolean(true),
|
||||
"false" => Value::Boolean(false),
|
||||
_ => todo!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
Tree::Unary { op, term } => {
|
||||
let term = execute_old(term.as_ref());
|
||||
match op.as_str() {
|
||||
"-" => match term {
|
||||
Value::Number(n) => Value::Number(-n),
|
||||
_ => panic!("cannot negate non number"),
|
||||
},
|
||||
"!" => match term {
|
||||
Value::Boolean(b) => Value::Boolean(!b),
|
||||
_ => panic!("cannot invert non boolean"),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
Tree::Binary { op, left, right } => {
|
||||
let left = execute_old(left.as_ref());
|
||||
let right = execute_old(right.as_ref());
|
||||
match op.as_str() {
|
||||
"+" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left + right),
|
||||
(Value::String(left), Value::String(right)) => Value::String(left + right.as_str()),
|
||||
_ => panic!("cannot negate add non numbers or non strings"),
|
||||
},
|
||||
"-" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left - right),
|
||||
_ => panic!("cannot subtract non number"),
|
||||
},
|
||||
"*" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left * right),
|
||||
_ => panic!("cannot multiply non number"),
|
||||
},
|
||||
"/" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left / right),
|
||||
_ => panic!("cannot divide non number"),
|
||||
},
|
||||
"%" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left % right),
|
||||
_ => panic!("cannot modulo non number"),
|
||||
},
|
||||
"**" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Number(left.powf(right)),
|
||||
_ => panic!("cannot exponentiate non number"),
|
||||
},
|
||||
"==" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left == right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left == right),
|
||||
(Value::Boolean(left), Value::Boolean(right)) => Value::Boolean(left == right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
"!=" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left != right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left != right),
|
||||
(Value::Boolean(left), Value::Boolean(right)) => Value::Boolean(left != right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
">" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left > right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left > right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
"<" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left < right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left < right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
">=" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left <= right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left <= right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
"<=" => match (left, right) {
|
||||
(Value::Number(left), Value::Number(right)) => Value::Boolean(left <= right),
|
||||
(Value::String(left), Value::String(right)) => Value::Boolean(left <= right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
"&&" => match (left, right) {
|
||||
(Value::Boolean(left), Value::Boolean(right)) => Value::Boolean(left && right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
"||" => match (left, right) {
|
||||
(Value::Boolean(left), Value::Boolean(right)) => Value::Boolean(left || right),
|
||||
_ => panic!("cannot compare different types"),
|
||||
},
|
||||
"in" => match (left, right) {
|
||||
(val @ Value::Boolean(_), Value::Array { kind: ValueType::Boolean, values }) => Value::Boolean(values.contains(&val)),
|
||||
(val @ Value::Number(_), Value::Array { kind: ValueType::Number, values }) => Value::Boolean(values.contains(&val)),
|
||||
(val @ Value::String(_), Value::Array { kind: ValueType::String, values }) => Value::Boolean(values.contains(&val)),
|
||||
_ => panic!("cannot compare different arrays"),
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
},
|
||||
Tree::Ternary { op, left, middle, right } => {
|
||||
let left = execute_old(left.as_ref());
|
||||
match op.as_str() {
|
||||
"?" => {
|
||||
let Value::Boolean(cond) = left else {
|
||||
panic!("cannot use non boolean as boolean");
|
||||
};
|
||||
if cond {
|
||||
execute_old(middle.as_ref())
|
||||
} else {
|
||||
execute_old(right.as_ref())
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
Tree::Array { terms } => {
|
||||
if terms.is_empty() {
|
||||
Value::Array { kind: ValueType::Number, values: vec![] }
|
||||
} else {
|
||||
let values: Vec<_> = terms.iter().map(execute_old).collect();
|
||||
let kind = values[0].get_type();
|
||||
Value::Array { kind, values }
|
||||
}
|
||||
},
|
||||
// _ => todo!(),
|
||||
idk => panic!("idk: {:?}", idk),
|
||||
}
|
||||
}
|
||||
|
||||
use super::compiler;
|
||||
use types::Value;
|
||||
|
||||
pub fn execute(bytecode: &compiler::Bytecode) -> Value {
|
||||
use compiler::Data;
|
||||
// let mut data: [f64; 256] = [0.0; 256];
|
||||
let mut data: [Data; 8] = [0; 8].map(|_| Data::Float(0.0));
|
||||
let mut data: [Value; 8] = [0; 8].map(|_| Value::Float(0.0));
|
||||
for instruction in &bytecode.code {
|
||||
use compiler::Instruction::*;
|
||||
match instruction {
|
||||
|
@ -180,19 +16,19 @@ pub fn execute(bytecode: &compiler::Bytecode) -> Value {
|
|||
let left = &data[*left as usize];
|
||||
let right = &data[*right as usize];
|
||||
data[*dest as usize] = match (left, right) {
|
||||
(Data::Float(left), Data::Float(right)) => match operator {
|
||||
Add => Data::Float(left + right),
|
||||
Subtract => Data::Float(left - right),
|
||||
Multiply => Data::Float(left * right),
|
||||
Divide => Data::Float(left / right),
|
||||
(Value::Float(left), Value::Float(right)) => match operator {
|
||||
Add => Value::Float(left + right),
|
||||
Subtract => Value::Float(left - right),
|
||||
Multiply => Value::Float(left * right),
|
||||
Divide => Value::Float(left / right),
|
||||
_ => todo!(),
|
||||
},
|
||||
(Data::String(left), Data::String(right)) => match operator {
|
||||
Add => Data::String(left.clone() + &right),
|
||||
(Value::String(left), Value::String(right)) => match operator {
|
||||
Add => Value::String(left.clone() + right),
|
||||
_ => todo!(),
|
||||
},
|
||||
(left, Data::Array(array)) => match operator {
|
||||
In => Data::Boolean(array.contains(left)),
|
||||
(left, Value::Array { data: array, .. }) => match operator {
|
||||
In => Value::Boolean(array.contains(left)),
|
||||
_ => todo!(),
|
||||
},
|
||||
_ => panic!("invalid types"),
|
||||
|
@ -200,9 +36,9 @@ pub fn execute(bytecode: &compiler::Bytecode) -> Value {
|
|||
},
|
||||
UnaryOperator { operator, input, dest } => {
|
||||
use compiler::UnaryOperation::*;
|
||||
let Data::Float(input) = data[*input as usize] else { panic!("not a float") };
|
||||
let Value::Float(input) = data[*input as usize] else { panic!("not a float") };
|
||||
data[*dest as usize] = match operator {
|
||||
Negate => Data::Float(-input),
|
||||
Negate => Value::Float(-input),
|
||||
_ => todo!(),
|
||||
}
|
||||
},
|
||||
|
@ -210,25 +46,21 @@ pub fn execute(bytecode: &compiler::Bytecode) -> Value {
|
|||
data[*dest as usize] = bytecode.data[*input as usize].clone();
|
||||
},
|
||||
Return { source } => {
|
||||
dbg!(source);
|
||||
// dbg!(&d);
|
||||
return match data[*source as usize].clone() {
|
||||
Data::Float(f) => Value::Number(f),
|
||||
Data::String(s) => Value::String(s),
|
||||
Data::Boolean(b) => Value::Boolean(b),
|
||||
_ => todo!(),
|
||||
};
|
||||
return data[*source as usize].clone();
|
||||
},
|
||||
Array { dest } => {
|
||||
data[*dest as usize] = Data::Array(Vec::new())
|
||||
data[*dest as usize] = Value::Array {
|
||||
kind: types::ValueType::Boolean,
|
||||
data: Vec::new(),
|
||||
};
|
||||
},
|
||||
ArrayAppend { source, dest } => {
|
||||
let item = data[*source as usize].clone();
|
||||
let Data::Array(ref mut arr) = data[*dest as usize] else { panic!("not an array") };
|
||||
let Value::Array { data: ref mut arr, .. } = data[*dest as usize] else { panic!("not an array") };
|
||||
arr.push(item);
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
Value::Number(10.0)
|
||||
Value::Float(10.0)
|
||||
}
|
||||
|
|
22
src/types.rs
22
src/types.rs
|
@ -5,15 +5,31 @@ pub enum Value {
|
|||
Float(f64),
|
||||
String(String),
|
||||
Boolean(bool),
|
||||
Array(Vec<Data>),
|
||||
|
||||
// TODO: surely there's a better way than this?
|
||||
// every item will always be the same size/type - i don't want to waste memory on tags
|
||||
Array { kind: ValueType, data: Vec<Value> },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum ValueType {
|
||||
pub enum ValueType {
|
||||
Unsigned,
|
||||
Integer,
|
||||
Float,
|
||||
String,
|
||||
Boolean,
|
||||
Array(Box<DataType>),
|
||||
Array(Box<ValueType>),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn get_type(&self) -> ValueType {
|
||||
match self {
|
||||
Self::Unsigned(_) => ValueType::Unsigned,
|
||||
Self::Integer(_) => ValueType::Integer,
|
||||
Self::Float(_) => ValueType::Float,
|
||||
Self::String(_) => ValueType::String,
|
||||
Self::Boolean(_) => ValueType::Boolean,
|
||||
Self::Array { kind, .. } => ValueType::Array(Box::new(kind.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue