Skip to content

Commit 5af0e4f

Browse files
Feat: Add objects and maps (#8)
* Implement object literals and property access in parser and runtime * Add support for object bracket access in runtime and integration tests * Add depth-first search implementation with graph representation * fixed formatting issue
1 parent fae6c72 commit 5af0e4f

7 files changed

Lines changed: 562 additions & 67 deletions

File tree

example/graph_dfs.rts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
let graph = {
2+
A: ["B", "C"],
3+
B: ["D"],
4+
C: ["E"],
5+
D: ["F"],
6+
E: [],
7+
F: []
8+
};
9+
10+
let sizes = {A: 2, B: 1, C: 1, D: 1, E: 0, F: 0};
11+
12+
let visited = {
13+
A: 0, B: 0, C: 0, D: 0, E: 0, F: 0
14+
};
15+
16+
function dfs(graph, sizes, visited, node) {
17+
if (visited[node] == 1) {
18+
return 0;
19+
}
20+
visited[node] = 1;
21+
print node;
22+
23+
let neighbors = graph[node];
24+
let count = sizes[node];
25+
for (let i = 0; i < count; i++) {
26+
dfs(graph, sizes, visited, neighbors[i]);
27+
}
28+
return 0;
29+
}
30+
31+
dfs(graph, sizes, visited, "A");

src/ast/mod.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::HashMap;
12
use std::{cell::RefCell, rc::Rc};
23

34
#[derive(Debug)]
@@ -44,6 +45,11 @@ pub enum Statement {
4445
index: Vec<Expression>,
4546
value: Expression,
4647
},
48+
AssignmentProperty {
49+
object: String,
50+
property: Vec<String>,
51+
value: Expression,
52+
},
4753
}
4854

4955
#[derive(Debug, Clone, PartialEq)]
@@ -83,11 +89,27 @@ pub enum Expression {
8389
array: Box<Expression>,
8490
index: Box<Expression>,
8591
},
92+
ObjectLiteral(Vec<(String, Expression)>),
93+
PropertyAccess {
94+
object: Box<Expression>,
95+
property: String,
96+
},
8697
}
8798

88-
#[derive(Debug, Clone, PartialEq, PartialOrd)]
99+
#[derive(Debug, Clone, PartialEq)]
89100
pub enum Value {
90101
Number(i32),
91102
String(String),
92103
Array(Rc<RefCell<Vec<Value>>>),
104+
Object(Rc<RefCell<HashMap<String, Value>>>),
105+
}
106+
107+
impl PartialOrd for Value {
108+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
109+
match (self, other) {
110+
(Value::Number(a), Value::Number(b)) => a.partial_cmp(b),
111+
(Value::String(a), Value::String(b)) => a.partial_cmp(b),
112+
_ => None,
113+
}
114+
}
93115
}

src/compiler/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ pub enum Instruction {
4646
CreateArray(usize),
4747
LoadIndex,
4848
StoreIndex,
49+
CreateObject(usize),
50+
LoadProperty,
51+
StoreProperty,
4952
}
5053

5154
fn compile_expr(instructions: &mut Vec<Instruction>, expr: &Expression) {
@@ -114,6 +117,19 @@ fn compile_expr(instructions: &mut Vec<Instruction>, expr: &Expression) {
114117
compile_expr(instructions, index);
115118
instructions.push(Instruction::LoadIndex);
116119
}
120+
Expression::ObjectLiteral(objs) => {
121+
for obj in objs {
122+
let (key, value) = obj;
123+
instructions.push(Instruction::LoadConst(Value::String(key.to_owned())));
124+
compile_expr(instructions, value);
125+
}
126+
instructions.push(Instruction::CreateObject(objs.len()));
127+
}
128+
Expression::PropertyAccess { object, property } => {
129+
compile_expr(instructions, object);
130+
instructions.push(Instruction::LoadConst(Value::String(property.to_owned())));
131+
instructions.push(Instruction::LoadProperty);
132+
}
117133
}
118134
}
119135

@@ -271,6 +287,20 @@ pub fn compile_statements(
271287
compile_expr(instructions, &value);
272288
instructions.push(Instruction::StoreIndex);
273289
}
290+
Statement::AssignmentProperty {
291+
object,
292+
property,
293+
value,
294+
} => {
295+
instructions.push(Instruction::LoadVar(object));
296+
for prop in property {
297+
instructions.push(Instruction::LoadConst(Value::String(prop.to_owned())));
298+
instructions.push(Instruction::LoadProperty);
299+
}
300+
instructions.pop();
301+
compile_expr(instructions, &value);
302+
instructions.push(Instruction::StoreProperty);
303+
}
274304
}
275305
}
276306
}

src/lexer/mod.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ pub enum Token {
3333
Return,
3434
SquareLeft,
3535
SquareRight,
36+
Dot,
37+
Colon,
3638
}
3739

3840
pub fn tokenize(input: &str) -> Vec<Token> {
@@ -221,11 +223,21 @@ pub fn tokenize(input: &str) -> Vec<Token> {
221223
}
222224
}
223225

224-
'a'..='z' | 'A'..='Z' => {
226+
'.' => {
227+
tokens.push(Token::Dot);
228+
chars.next();
229+
}
230+
231+
':' => {
232+
tokens.push(Token::Colon);
233+
chars.next();
234+
}
235+
236+
'a'..='z' | 'A'..='Z' | '_' => {
225237
let mut ident = String::new();
226238

227239
while let Some(&c) = chars.peek() {
228-
if c.is_alphanumeric() {
240+
if c.is_alphanumeric() || c == '_' {
229241
ident.push(c);
230242
chars.next();
231243
} else {

src/parser/mod.rs

Lines changed: 134 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,63 @@ impl Parser {
358358
self.parse_primary()
359359
}
360360

361+
fn parse_array_assignment(&mut self, name: String) -> Statement {
362+
let mut indices = Vec::new();
363+
while Some(&Token::SquareLeft) == self.peek() {
364+
self.advance();
365+
indices.push(self.parse_first());
366+
match self.advance() {
367+
Some(Token::SquareRight) => {}
368+
_ => panic!("Expected ']' after array index"),
369+
}
370+
}
371+
match self.advance() {
372+
Some(Token::Equals) => {}
373+
_ => panic!("Expected '=' after array index"),
374+
}
375+
let value = self.parse_first();
376+
match self.advance() {
377+
Some(Token::Semicolon) => {}
378+
_ => panic!("Expected ';' after array assignment"),
379+
}
380+
Statement::AssignmentIndex {
381+
array: name,
382+
index: indices,
383+
value,
384+
}
385+
}
386+
387+
fn parse_obj_assignment(&mut self, name: String) -> Statement {
388+
let mut properties = Vec::new();
389+
while let Some(token) = self.peek() {
390+
if *token != Token::Dot {
391+
break;
392+
}
393+
self.advance();
394+
let prop_name = match self.advance() {
395+
Some(Token::Identifier(name)) => name.to_owned(),
396+
_ => panic!("Expected identifier"),
397+
};
398+
properties.push(prop_name);
399+
}
400+
401+
match self.advance() {
402+
Some(Token::Equals) => {}
403+
_ => panic!("Expected '=' after object property"),
404+
}
405+
let value = self.parse_first();
406+
match self.advance() {
407+
Some(Token::Semicolon) => {}
408+
_ => panic!("Expected ';' after object assignment"),
409+
}
410+
411+
Statement::AssignmentProperty {
412+
object: name,
413+
property: properties,
414+
value,
415+
}
416+
}
417+
361418
fn parse_call(&mut self, name: String) -> Statement {
362419
self.advance(); // consume the identifier
363420
if let Some(Token::LeftParentheses) = self.peek() {
@@ -369,29 +426,9 @@ impl Parser {
369426
}
370427
Statement::Expression(call_expr)
371428
} else if let Some(Token::SquareLeft) = self.peek() {
372-
let mut indices = Vec::new();
373-
while Some(&Token::SquareLeft) == self.peek() {
374-
self.advance();
375-
indices.push(self.parse_first());
376-
match self.advance() {
377-
Some(Token::SquareRight) => {}
378-
_ => panic!("Expected ']' after array index"),
379-
}
380-
}
381-
match self.advance() {
382-
Some(Token::Equals) => {}
383-
_ => panic!("Expected '=' after array index"),
384-
}
385-
let value = self.parse_first();
386-
match self.advance() {
387-
Some(Token::Semicolon) => {}
388-
_ => panic!("Expected ';' after array assignment"),
389-
}
390-
Statement::AssignmentIndex {
391-
array: name,
392-
index: indices,
393-
value,
394-
}
429+
self.parse_array_assignment(name)
430+
} else if let Some(Token::Dot) = self.peek() {
431+
self.parse_obj_assignment(name)
395432
} else {
396433
self.parse_assignment(name)
397434
}
@@ -421,6 +458,60 @@ impl Parser {
421458
Expression::Call { name, args }
422459
}
423460

461+
fn parse_object(&mut self) -> Expression {
462+
let mut properties = Vec::new();
463+
while let Some(token) = self.peek() {
464+
if *token == Token::RightBrace {
465+
break;
466+
}
467+
let key = match self.advance() {
468+
Some(Token::Identifier(name)) => name.to_owned(),
469+
_ => panic!("Expected identifier"),
470+
};
471+
match self.advance() {
472+
Some(Token::Colon) => {}
473+
_ => panic!("Expected ':'"),
474+
}
475+
let value = self.parse_first();
476+
properties.push((key, value));
477+
if let Some(Token::Comma) = self.peek() {
478+
self.advance();
479+
}
480+
}
481+
482+
match self.advance() {
483+
Some(Token::RightBrace) => {}
484+
_ => panic!("Expected '}}'"),
485+
}
486+
487+
Expression::ObjectLiteral(properties)
488+
}
489+
490+
fn parse_index_access(&mut self, array: Expression) -> Expression {
491+
self.advance(); // consume '['
492+
let index_expr = self.parse_first();
493+
match self.advance() {
494+
Some(Token::SquareRight) => {}
495+
_ => panic!("Expected ']' after array index"),
496+
}
497+
Expression::Index {
498+
array: Box::new(array),
499+
index: Box::new(index_expr),
500+
}
501+
}
502+
503+
fn parse_property_access(&mut self, object: Expression) -> Expression {
504+
self.advance(); // consume '.'
505+
let prop_name = match self.advance() {
506+
Some(Token::Identifier(name)) => name.to_owned(),
507+
_ => panic!("Expected identifier after '.'"),
508+
};
509+
Expression::PropertyAccess {
510+
object: Box::new(object),
511+
property: prop_name,
512+
}
513+
}
514+
424515
fn parse_primary(&mut self) -> Expression {
425516
match self.advance() {
426517
Some(Token::LeftParentheses) => {
@@ -433,39 +524,34 @@ impl Parser {
433524
}
434525
Some(Token::Number(n)) => Expression::Number(*n),
435526
Some(Token::Identifier(name)) => {
436-
let name = name.clone();
437-
// If followed by '(', it's a function call expression
438-
if let Some(Token::LeftParentheses) = self.peek() {
439-
self.parse_call_args(name)
440-
} else if let Some(Token::SquareLeft) = self.peek() {
441-
self.parse_index(name)
442-
} else {
443-
Expression::Identifier(name)
527+
let mut expr = Expression::Identifier(name.clone());
528+
loop {
529+
match self.peek() {
530+
Some(Token::LeftParentheses) => {
531+
let name = match expr {
532+
Expression::Identifier(ref n) => n.clone(),
533+
_ => panic!("Expected function name before '('"),
534+
};
535+
expr = self.parse_call_args(name);
536+
}
537+
Some(Token::SquareLeft) => {
538+
expr = self.parse_index_access(expr);
539+
}
540+
Some(Token::Dot) => {
541+
expr = self.parse_property_access(expr);
542+
}
543+
_ => break,
544+
}
444545
}
546+
expr
445547
}
446548
Some(Token::SquareLeft) => self.parse_array_literal(),
549+
Some(Token::LeftBrace) => self.parse_object(),
447550
Some(Token::String(s)) => Expression::String(s.clone()),
448551
_ => panic!("Invalid expression"),
449552
}
450553
}
451554

452-
fn parse_index(&mut self, array_name: String) -> Expression {
453-
let mut result = Expression::Identifier(array_name.clone());
454-
while Some(&Token::SquareLeft) == self.peek() {
455-
self.advance(); // consume '['
456-
let index_expr = self.parse_first();
457-
match self.advance() {
458-
Some(Token::SquareRight) => {}
459-
_ => panic!("Expected ']' after array index"),
460-
}
461-
result = Expression::Index {
462-
array: Box::new(result),
463-
index: Box::new(index_expr),
464-
};
465-
}
466-
result
467-
}
468-
469555
fn parse_array_literal(&mut self) -> Expression {
470556
let mut elements = Vec::new();
471557
while let Some(token) = self.peek() {

0 commit comments

Comments
 (0)