slightly improved arrays

This commit is contained in:
tezlm 2023-05-10 02:41:30 -07:00
parent 353b556734
commit 4cc77ce885
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
4 changed files with 75 additions and 241 deletions

View file

@ -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

View file

@ -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]

View file

@ -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)
}

View file

@ -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())),
}
}
}