Skip to content

Commit

Permalink
hashmap (but doesnt work with pointers atm eg strings or lists)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomtheCoder2 committed Nov 4, 2023
1 parent 9b493d7 commit b334f98
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 21 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "phoenix-lang"
description = "A simple, fast programming language"
version = "1.3.1"
version = "1.3.2"
edition = "2021"
license = "MIT"
repository = "https://github.com/TomtheCoder2/phoenix"
Expand Down
5 changes: 5 additions & 0 deletions phoenix_examples/hash.phx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var hashmap = { "one": 1, "two": 2, "three": 3 };
print(hashmap);
print(hashmap.len());
print(hashmap.get("one"));
print(hashmap.get("four"));
2 changes: 2 additions & 0 deletions src/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub enum OpCode {
OpCreateList(usize),
// Push the string on the stack to the heap and replace it with a pointer to the heap
OpCreateString,
/// Creates a hashmap of the last 2n items, where the first value is the key and the second one is the value
OpCreateHashMap(usize)
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
Expand Down
26 changes: 24 additions & 2 deletions src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ impl Compiler {
ParseFn::Literal => self.literal(),
ParseFn::String => self.string(),
ParseFn::List => self.list(can_assign),
ParseFn::HashMap => self.hashmap(can_assign),
ParseFn::Variable => self.variable(can_assign),
ParseFn::And => self.and_operator(),
ParseFn::Or => self.or_operator(),
Expand Down Expand Up @@ -923,7 +924,7 @@ impl Compiler {
self.emit_instr(OpPop);
self.parse_precedence(Precedence::And); // Parse right hand side of the infix expression
self.patch_jump(end_jump); // Jump to after it if the first argument was already false, leaving the false value on the top of the stack to be the result
// Otherwise the first argument is true, so the value of the whole and is equal to the value of the second argument, so just proceed as normal
// Otherwise the first argument is true, so the value of the whole and is equal to the value of the second argument, so just proceed as normal
}

fn or_operator(&mut self) {
Expand Down Expand Up @@ -956,7 +957,7 @@ impl Compiler {
} else {
s
}
.parse::<f32>()
.parse::<f32>()
{
self.emit_constant(Value::Float(value));
} else {
Expand Down Expand Up @@ -985,6 +986,7 @@ impl Compiler {
}

fn list(&mut self, _can_assign: bool) {
// * list: [1, 2, 3, 4, ...]
let mut size = 0;
if !self.check(TokenType::RightBracket) {
loop {
Expand All @@ -999,6 +1001,26 @@ impl Compiler {
self.emit_instr(OpCreateList(size));
}

fn hashmap(&mut self, _can_assign: bool) {
// hashmap looks like this: var hashmap = { "key": "value", "key2": "value2" }
// and we should push all the values in this order on the stack and then emit OpCode::CreateHashMap(size)
// with size equal to the amount of keys
let mut size = 0;
if !self.check(TokenType::RightBrace) {
loop {
self.expression();
self.consume(TokenType::Colon, "Expected ':' after key");
self.expression();
size += 1;
if !self.match_cur(TokenType::Comma) {
break;
}
}
}
self.consume(TokenType::RightBrace, "Expected '}' after hashmap");
self.emit_instr(OpCreateHashMap(size));
}

/// Parse an identifier that we know to be a variable
///
/// Eventually emits a get instr or a set instr + the instructions to process the expr
Expand Down
10 changes: 10 additions & 0 deletions src/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ impl GC {
HeapObjVal::PhoenixString(_string) => {
// to_mark.push(string.ptr);
}
HeapObjVal::PhoenixHashMap(map) => {
for val in &map.map {
if let Value::PhoenixPointer(ptr) = val.0 {
to_mark.push(*ptr);
}
if let Value::PhoenixPointer(ptr) = val.1 {
to_mark.push(*ptr);
}
}
}
}
}
None => panic!("VM panic! Why is there an unallocated pointer?"),
Expand Down
23 changes: 22 additions & 1 deletion src/native/native_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ lazy_static! {
HeapObjVal::PhoenixString(ref mut string) => {
Some(Ok(Long(string.value.len() as i64)))
}
HeapObjVal::PhoenixHashMap(ref mut hashmap) => {
Some(Ok(Long(hashmap.map.len() as i64)))
}
_ => None
}
}
Expand Down Expand Up @@ -119,7 +122,25 @@ lazy_static! {
_ => None
}
})
)
),
(
"get",
(Some(1), |this,args,_,state,_| {
match this {
PhoenixPointer(ptr) => {
let obj = state.deref_mut(ptr);
match obj.obj {
HeapObjVal::PhoenixHashMap(ref mut map) => {
println!("{:?}, arg: {:?}", map.map, args);
Some(Ok(map.map.get(&args[0]).unwrap_or(&Nil).clone()))
}
_ => None
}
}
_ => None
}
})
),
]));
}

Expand Down
12 changes: 10 additions & 2 deletions src/precedence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub enum ParseFn {
String,
Variable,
List,
HashMap,
And,
Or,
Call,
Expand Down Expand Up @@ -234,16 +235,23 @@ const PARSE_RULE_SUPER: ParseRule = ParseRule {
infix: ParseFn::None,
precedence: Precedence::None,
};
const PARSE_RULE_LB: ParseRule = ParseRule {
const PARSE_RULE_LBRACKET: ParseRule = ParseRule {
prefix: ParseFn::List,
infix: ParseFn::None,
precedence: Precedence::None,
};

const PARSE_RULE_LBRACE: ParseRule = ParseRule {
prefix: ParseFn::HashMap,
infix: ParseFn::None,
precedence: Precedence::None,
};

pub fn get_rule(operator: TokenType) -> ParseRule {
match operator {
TokenType::LeftParen => PARSE_RULE_LP,
TokenType::LeftBracket => PARSE_RULE_LB,
TokenType::LeftBracket => PARSE_RULE_LBRACKET,
TokenType::LeftBrace => PARSE_RULE_LBRACE,
TokenType::Minus => PARSE_RULE_MINUS,
TokenType::MinusMinus => PARSE_RULE_MINUS_MINUS,
TokenType::MinusAssign => PARSE_RULE_MINUS_ASSIGN,
Expand Down
2 changes: 2 additions & 0 deletions src/scanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub enum TokenType {
GreaterEqual,
Less,
LessEqual,
Colon,

Identifier,
String,
Expand Down Expand Up @@ -213,6 +214,7 @@ impl Scanner {
}
}
b'"' => self.string(),
b':' => self.create_token(TokenType::Colon),
_ => self.error_token("Unexpected character."),
}
}
Expand Down
65 changes: 63 additions & 2 deletions src/value.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::collections::HashMap;
use std::hash::{Hash, Hasher};

use crate::chunk::ModuleChunk;
use serde::{Deserialize, Serialize};
Expand All @@ -15,7 +16,7 @@ pub enum Value {
#[default]
Nil,
// todo: fix this, cause this type is only used for initialisation of strings
// todo: create static strings, for example for prints, so we can allocate them once and have multiple references to them
// todo: create static strings, for example for prints, so we can allocate them once and have multiple immutable references to them
PhoenixString(String),
// Index of the function in the functions Vec in VM // Fixme: Is this even reachable? Can this be completely removed and the parameter put in OpClosure?
PhoenixFunction(usize),
Expand All @@ -31,6 +32,26 @@ pub enum Value {
PhoenixBoundMethod(ObjBoundMethod),
}

// todo fix this (dont hash the pointer, but the value)
impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Value::Float(x) => x.to_bits().hash(state),
Value::Long(x) => x.hash(state),
Value::Bool(x) => x.hash(state),
Value::Nil => 0.hash(state),
Value::PhoenixString(x) => x.hash(state),
Value::PhoenixFunction(x) => x.hash(state),
Value::NativeFunction(x, _) => x.hash(state),
Value::NativeMethod(x, _) => x.hash(state),
Value::PhoenixClass(x) => x.hash(state),
Value::PhoenixModule(x) => x.hash(state),
Value::PhoenixPointer(x) => x.hash(state),
Value::PhoenixBoundMethod(x) => x.hash(state),
}
}
}

impl Eq for Value {}

impl PartialEq for Value {
Expand Down Expand Up @@ -86,6 +107,8 @@ impl Value {
state.deref(*pointer).to_string(vm, state, modules)
} else if let HeapObjVal::PhoenixString(_) = &state.deref(*pointer).obj {
state.deref(*pointer).to_string(vm, state, modules)
} else if let HeapObjVal::PhoenixHashMap(_) = &state.deref(*pointer).obj {
state.deref(*pointer).to_string(vm, state, modules)
} else {
format!(
"<pointer {}> to {}",
Expand Down Expand Up @@ -258,7 +281,7 @@ pub fn values_equal(t: (&Value, &Value)) -> bool {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Ord, PartialOrd, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Ord, PartialOrd, Eq, Hash)]
pub struct ObjBoundMethod {
pub method: usize,
// Index into the functions vec for which function to call
Expand All @@ -276,6 +299,7 @@ pub enum HeapObjType {
PhoenixClosure,
PhoenixList,
PhoenixString,
PhoenixHashMap,
}

#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
Expand Down Expand Up @@ -329,6 +353,14 @@ impl HeapObj {
is_marked: false,
}
}

pub fn new_hashmap(map: ObjHashMap) -> HeapObj {
HeapObj {
obj: HeapObjVal::PhoenixHashMap(map),
obj_type: HeapObjType::PhoenixHashMap,
is_marked: false,
}
}
}

// I swear i really tried to not have this be duplicate with HeapObjType, but couldn't figure out a way to do it
Expand All @@ -339,6 +371,7 @@ pub enum HeapObjVal {
PhoenixClosure(ObjClosure),
PhoenixString(ObjString),
PhoenixList(ObjList),
PhoenixHashMap(ObjHashMap),
}

impl HeapObjVal {
Expand Down Expand Up @@ -375,6 +408,14 @@ impl HeapObjVal {
panic!("VM panic! How did a placeholder value get here?")
}
HeapObjVal::PhoenixString(string) => string.value.clone(),
HeapObjVal::PhoenixHashMap(map) => format!(
"{{{}}}",
map.map
.iter()
.map(|(k, v)| format!("{}: {}", k.to_string(vm, state, modules), v.to_string(vm, state, modules)))
.collect::<Vec<String>>()
.join(", ")
),
}
}

Expand Down Expand Up @@ -441,6 +482,14 @@ impl HeapObjVal {
panic!("VM panic!")
}
}

pub fn as_hashmap(&self) -> &ObjHashMap {
if let HeapObjVal::PhoenixHashMap(map) = self {
map
} else {
panic!("VM panic!")
}
}
}

/// Runtime instantiation of class definitions
Expand Down Expand Up @@ -499,3 +548,15 @@ impl ObjString {
ObjString { value }
}
}

/// Runtime representation of a HashMap
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Default)]
pub struct ObjHashMap {
pub map: HashMap<Value, Value>,
}

impl ObjHashMap {
pub fn new(map: HashMap<Value, Value>) -> ObjHashMap {
ObjHashMap { map }
}
}
Loading

0 comments on commit b334f98

Please sign in to comment.