diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..15b9ac0 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,45 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'ferrite-lua'", + "cargo": { + "args": [ + "build", + "--bin=ferrite-lua", + "--package=ferrite-lua" + ], + "filter": { + "name": "ferrite-lua", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'ferrite-lua'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=ferrite-lua", + "--package=ferrite-lua" + ], + "filter": { + "name": "ferrite-lua", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/examples/test.lua b/examples/test.lua index c9904ca..3f16c49 100644 --- a/examples/test.lua +++ b/examples/test.lua @@ -17,6 +17,7 @@ function min(x, y) end global sometable = {} +sometable["hello"] = "there" print(max(11, 9)) print(add(10)(15)) diff --git a/src/ast.rs b/src/ast.rs index 0b8a650..2e3be82 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -206,7 +206,11 @@ impl Parse for Block { #[derive(Debug, Clone)] pub enum Statement { - Assignment(Option, Vec>, ExpressionList), + Assignment( + Option, + Vec<(Node, Vec>)>, + ExpressionList, + ), Return(ExpressionList), If(Node, Block), Expression(Node), @@ -220,7 +224,7 @@ impl Parse for Statement { if let Some(name) = function.kind.name { Ok(Self::Assignment( None, - vec![name], + vec![(name, Vec::new())], ExpressionList(vec![Node { kind: Expression::FunctionDefinition( function.kind.params, @@ -252,23 +256,23 @@ impl Parse for Statement { _ => panic!(), }; let mut names = Vec::new(); - names.push(stream.parse()?); + names.push((stream.parse()?, Vec::new())); while stream.peek() == Some(Token::Symbol(',')) { stream.next(); - names.push(stream.parse()?); + names.push((stream.parse()?, Vec::new())); } stream.expect(Token::Symbol('='))?; let expr = stream.parse()?; Ok(Statement::Assignment(Some(access_modifier), names, expr)) - } else if let Some(Token::Word(_)) = peeked - && stream.peek2() == Some(Token::Symbol('=')) - { - let name = stream.parse()?; - stream.expect(Token::Symbol('='))?; + } else if stream.parse_peek::().is_ok() { + let access = stream.parse::().unwrap(); + + let expression = stream.parse()?; + Ok(Self::Assignment( None, - vec![name], - ExpressionList(vec![stream.parse()?]), + vec![(access.0.0, access.0.1)], + ExpressionList(vec![expression]), )) } else if let Ok(expr) = stream.parse() { Ok(Self::Expression(expr)) @@ -284,6 +288,48 @@ pub enum AccessModifier { Global, } +#[derive(Debug, Clone)] +pub struct IndexedAccess(Node, Vec>); + +impl Parse for IndexedAccess { + fn parse(mut stream: TokenStream) -> Result { + let name = stream.parse()?; + + let mut expressions = Vec::new(); + while let Some(Token::Symbol('[') | Token::Symbol('.')) = stream.peek() { + match stream.next().unwrap() { + Token::Symbol('[') => { + let expression = stream.parse()?; + stream.expect_symbol(']')?; + expressions.push(expression); + } + Token::Symbol('.') => { + let word = stream.parse::>()?; + stream.expect_symbol(']')?; + expressions.push(Node { + kind: Expression::Literal(Literal::String(word.kind)), + meta: word.meta, + }); + } + _ => panic!(), + } + } + + Ok(IndexedAccess(name, expressions)) + } +} + +#[derive(Debug, Clone)] +pub struct IndexedAssignment(IndexedAccess); + +impl Parse for IndexedAssignment { + fn parse(mut stream: TokenStream) -> Result { + let access = stream.parse()?; + stream.expect_symbol('=')?; + Ok(IndexedAssignment(access)) + } +} + #[derive(Debug, Clone)] pub enum Expression { ValueRef(String), @@ -433,17 +479,22 @@ impl Parse for PrimaryExpression { Expression::ValueRef(stream.parse()?) }; - while let Some(Token::Symbol('(')) = stream.peek() { - stream.next(); - let expression_list = stream.parse::>()?; - stream.expect(Token::Symbol(')'))?; - expression = Expression::FunctionCall( - Box::new(Node { - kind: expression, - meta: Metadata::produce(&mut stream, pre.clone()), - }), - expression_list, - ); + while let Some(Token::Symbol('(') | Token::Symbol('[')) = stream.peek() { + match stream.next().unwrap() { + Token::Symbol('(') => { + let expression_list = stream.parse::>()?; + stream.expect(Token::Symbol(')'))?; + expression = Expression::FunctionCall( + Box::new(Node { + kind: expression, + meta: Metadata::produce(&mut stream, pre.clone()), + }), + expression_list, + ); + } + Token::Symbol('[') => todo!(), + _ => panic!(), + } } for unop in unary_operators.into_iter().rev() { diff --git a/src/compile.rs b/src/compile.rs index 1dd2227..9765389 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -103,18 +103,27 @@ impl Statement { Statement::Assignment(access, names, expr_list) => { let mut constants = HashSet::new(); if *access == Some(AccessModifier::Global) { - for name in names { + for (name, indexes) in names { constants.insert(Constant::String(name.kind.clone())); + for index in indexes { + constants.extend(index.kind.find_constants(scope)); + } } } else if *access == None { - for name in names { + for (name, indexes) in names { if !scope.locals.contains_key(&name.kind) { constants.insert(Constant::String(name.kind.clone())); } + for index in indexes { + constants.extend(index.kind.find_constants(scope)); + } } } else { - for name in names { + for (name, indexes) in names { scope.locals.insert(name.kind.clone(), 0); + for index in indexes { + constants.extend(index.kind.find_constants(scope)); + } } } for expr in &expr_list.0 { @@ -176,18 +185,24 @@ impl Statement { match access_modifier { Some(AccessModifier::Local) => { - for (i, name) in names.iter().enumerate() { + for (i, (name, indexes)) in names.iter().enumerate() { instructions.push(Instruction::Move( *new_registers.get(i).unwrap(), expr_regs.get(i).cloned().unwrap(), )); + if indexes.len() > 0 { + todo!() + } scope .locals .insert(name.kind.clone(), *new_registers.get(i).unwrap()); } } Some(AccessModifier::Global) => { - for (i, name) in names.iter().enumerate() { + for (i, (name, indexes)) in names.iter().enumerate() { + if indexes.len() > 0 { + todo!() + } let global = state.get_constant(&Constant::String(name.kind.clone())); instructions.push(Instruction::SetGlobal( expr_regs.get(i).cloned().unwrap(), @@ -196,24 +211,53 @@ impl Statement { } } None => { - for (i, name) in names.iter().enumerate() { - if let Some(reg) = scope.locals.get(&name.kind) { - instructions.push(Instruction::Move( - *reg, - expr_regs.get(i).cloned().unwrap(), - )); - } else if let Some(upval_reg) = scope.upvalues.get(&name.kind) { - instructions.push(Instruction::SetUpVal( - *upval_reg, + for (i, (name, indexes)) in names.iter().enumerate() { + if indexes.len() > 0 { + let table_reg = if let Some(reg) = scope.locals.get(&name.kind) { + *reg + } else if let Some(upval_reg) = scope.upvalues.get(&name.kind) { + let local = scope.register_counter.next(); + instructions.push(Instruction::GetUpVal(local, *upval_reg)); + local + } else { + let global = + state.get_constant(&Constant::String(name.kind.clone())); + let local = scope.register_counter.next(); + instructions.push(Instruction::GetGlobal(local, global)); + local + }; + + if indexes.len() > 1 { + todo!() + } + + let (instr, index_reg) = + indexes.last().unwrap().kind.compile(state, scope, Some(1)); + instructions.extend(instr); + instructions.push(Instruction::SetTable( + table_reg, + *index_reg.first().unwrap(), expr_regs.get(i).cloned().unwrap(), )); } else { - let global = - state.get_constant(&Constant::String(name.kind.clone())); - instructions.push(Instruction::SetGlobal( - expr_regs.get(i).cloned().unwrap(), - global, - )); + if let Some(reg) = scope.locals.get(&name.kind) { + instructions.push(Instruction::Move( + *reg, + expr_regs.get(i).cloned().unwrap(), + )); + } else if let Some(upval_reg) = scope.upvalues.get(&name.kind) { + instructions.push(Instruction::SetUpVal( + *upval_reg, + expr_regs.get(i).cloned().unwrap(), + )); + } else { + let global = + state.get_constant(&Constant::String(name.kind.clone())); + instructions.push(Instruction::SetGlobal( + expr_regs.get(i).cloned().unwrap(), + global, + )); + } } } } diff --git a/src/main.rs b/src/main.rs index 2a0abd2..f64060f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,12 +57,12 @@ fn compile( let tokens = tokenize(text).unwrap(); let mut stream = TokenStream::from(&file_path, &tokens); - // dbg!(&tokens); + dbg!(&tokens); let chunk = stream.parse::().unwrap(); stream.expect(Token::Eof).unwrap(); - // dbg!(&chunk); + dbg!(&chunk); let constants = chunk .find_constants(&mut Default::default(), constants)