From e4a7bcccc0172f8703c11bd7063063d965bd53b2 Mon Sep 17 00:00:00 2001 From: 0x4261756D <–38735823+0x4261756D@users.noreply.github.com> Date: Wed, 21 Dec 2022 21:48:52 +0100 Subject: [PATCH] Add arrays, making the language turing complete See tests/rule110.qbl for turing completeness --- src/main.rs | 296 +++++++++++++++++++++++++++++++++++++++------- tests/rule110.qbl | 121 ++++++++++++------- 2 files changed, 334 insertions(+), 83 deletions(-) diff --git a/src/main.rs b/src/main.rs index e6a7ee0..06255b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ enum Token IntLit(String, i32, i32), BoolLit(String, i32, i32), Keyword(String, i32, i32), + Apply(String, String, i32, i32), } enum TokenizerState @@ -51,6 +52,15 @@ struct Function content: Vec, } +#[derive(Debug)] +struct Arr +{ + name: String, + datatype: Datatype, + length: i64, + data: Vec, +} + #[derive(Debug)] enum Operation { @@ -64,6 +74,7 @@ enum Operation FunctionCall(String, i32, i32), If(Vec, Option>, i32, i32), While(Vec, i32, i32), + Apply(String, String, i32, i32), Depth(i32, i32), QueueDiagnostic(i32, i32), } @@ -123,7 +134,7 @@ fn main() println!("===PASSED==="); count += 1; } - else if let Some(index) = file_content.find("//:END:") + else if let Some(index) = file_content.find(":END:") { let expected_output = file_content[8..index].replace("\n//", "\n"); println!("\n===FAILED===\nExpected the output to be\n'{}'\n({})", expected_output, expected); @@ -141,14 +152,21 @@ fn main() println!("===PASSED==="); count += 1; } - else if let Some(index) = file_content.find("//:END:") + else if file_content.starts_with("//invalid,") { - let expected_output = file_content[10..index].replace("\n//", "\n"); - println!("\n\n===FAILED===\nExpected the output to be {}", expected_output); + if let Some(index) = file_content.find(":END:") + { + let expected_output = file_content[10..index].replace("\n//", "\n"); + println!("\n\n===FAILED===\nExpected the output to be {}", expected_output); + } + else + { + panic!("Could not find an ending marker (:END:) for the expected output in {:?}", f.file_name()); + } } else { - panic!("Could not find an ending marker (:END:) for the expected output in {:?}", f.file_name()); + println!("Unexpected error"); } } } @@ -181,16 +199,18 @@ fn compile(file_content: &String, intrinsics: &HashMap<&str, (Vec, Vec println!("---Done tokenizing, got {} tokens---", tokens.len()); let functions: Vec = extract_functions(&mut tokens, &intrinsics, debug)?; println!("---Done extracting functions, got {} functions and reduced the token count to {}---", functions.len(), tokens.len()); + let mut arrays: Vec = extract_arrays(&mut tokens, &intrinsics, &functions, debug)?; + println!("---Done extracting arrays, got {} arrays and reduced the token count to {}---", arrays.len(), tokens.len()); let operations = parse_until_delimiter(&mut tokens.iter().peekable(), &intrinsics, None, debug)?; println!("---Done parsing tokens into {} operations---", operations.len()); - validate_function_calls(&operations, &functions, debug)?; + validate_function_calls(&operations, &functions, &arrays, debug)?; println!("---Done validating function calls---"); - typecheck(&operations, &functions, &intrinsics, debug)?; + typecheck(&operations, &functions, &intrinsics, &arrays, debug)?; println!("---Done typechecking---"); let output = if interpret { println!("---Starting to interpret the program---"); - Some(interpret_program(&operations, &mut Vec::new(), &functions, &intrinsics, debug)) + Some(interpret_program(&operations, &mut Vec::new(), &functions, &mut arrays, &intrinsics, debug)) } else { @@ -199,7 +219,7 @@ fn compile(file_content: &String, intrinsics: &HashMap<&str, (Vec, Vec return Ok(output); } -fn interpret_program(operations: &Vec, queue: &mut Vec, functions: &Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, debug: bool) -> String +fn interpret_program(operations: &Vec, queue: &mut Vec, functions: &Vec, arrays: &mut Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, debug: bool) -> String { let mut output = String::new(); for operation in operations @@ -244,7 +264,7 @@ fn interpret_program(operations: &Vec, queue: &mut Vec, funct let val = queue.remove(0); function_context.push(val); } - output += interpret_program(&function.content, function_context, functions, intrinsics, debug).as_str(); + output += interpret_program(&function.content, function_context, functions, arrays, intrinsics, debug).as_str(); for val in function_context { queue.push(val.to_string()); @@ -255,11 +275,11 @@ fn interpret_program(operations: &Vec, queue: &mut Vec, funct let val = queue.remove(0); if val == "true" { - output += interpret_program(if_block, queue, functions, intrinsics, debug).as_str(); + output += interpret_program(if_block, queue, functions, arrays, intrinsics, debug).as_str(); } else if let Some(else_block) = maybe_else_block { - output += interpret_program(else_block, queue, functions, intrinsics, debug).as_str(); + output += interpret_program(else_block, queue, functions, arrays, intrinsics, debug).as_str(); } } Operation::Intrinsic(intrinsic_name, line, col) => @@ -321,6 +341,36 @@ fn interpret_program(operations: &Vec, queue: &mut Vec, funct } } } + Operation::Apply(name, word, line, col) => + { + let arr: &mut Arr = arrays.iter_mut().find(|x| &x.name == name).unwrap(); + match word.as_str() + { + "write" => + { + let position: i64 = queue.remove(0).parse::().unwrap(); + if position >= arr.length + { + panic!("Attempted an out of bounds write for array {} ({} >= {}) at {}:{}", arr.name, position, arr.length, line, col); + } + arr.data[position as usize] = queue.remove(0); + } + "read" => + { + let position: i64 = queue.remove(0).parse::().unwrap(); + if position >= arr.length + { + panic!("Attempted an out of bounds read for array {} ({} >= {}) at {}:{}", arr.name, position, arr.length, line, col); + } + queue.push(arr.data[position as usize].clone()); + } + "length" => + { + queue.push(arr.length.to_string()); + } + _ => panic!("Unexpected application '{}' at {}:{}", word, line, col) + } + } Operation::While(while_block, _, _) => { loop @@ -330,7 +380,7 @@ fn interpret_program(operations: &Vec, queue: &mut Vec, funct { break; } - output += interpret_program(while_block, queue, functions, intrinsics, debug).as_str(); + output += interpret_program(while_block, queue, functions, arrays, intrinsics, debug).as_str(); } } Operation::Depth(_, _) => @@ -351,7 +401,7 @@ fn interpret_program(operations: &Vec, queue: &mut Vec, funct return output; } -fn typecheck(operations: &Vec, functions: &Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, debug: bool) -> Result<(), String> +fn typecheck(operations: &Vec, functions: &Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, arrays: &Vec, debug: bool) -> Result<(), String> { for function in functions { @@ -359,7 +409,7 @@ fn typecheck(operations: &Vec, functions: &Vec, intrinsics: { println!("Now typechecking function '{}'", function.name); } - typecheck_block(&function.content, &function.ins, &function.outs, functions, intrinsics, debug)?; + typecheck_block(&function.content, &function.ins, &function.outs, functions, intrinsics, arrays, debug)?; if debug { println!("Successfully typechecked function '{}'", function.name); @@ -369,7 +419,7 @@ fn typecheck(operations: &Vec, functions: &Vec, intrinsics: { println!("Now typechecking main operations"); } - typecheck_block(operations, &Vec::new(), &Vec::new(), functions, intrinsics, debug)?; + typecheck_block(operations, &Vec::new(), &Vec::new(), functions, intrinsics, arrays, debug)?; if debug { println!("Successfully typechecked main operations"); @@ -377,9 +427,9 @@ fn typecheck(operations: &Vec, functions: &Vec, intrinsics: return Ok(()); } -fn typecheck_block(operations: &Vec, ins: &Vec, outs: &Vec, functions: &Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, debug: bool) -> Result<(), String> +fn typecheck_block(operations: &Vec, ins: &Vec, outs: &Vec, functions: &Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, arrays: &Vec, debug: bool) -> Result<(), String> { - let actual_outs = get_return_type(operations, ins, functions, intrinsics, debug)?; + let actual_outs = get_return_type(operations, ins, functions, intrinsics, arrays, debug)?; if &actual_outs != outs { let (line, col) = match operations.last() @@ -398,6 +448,7 @@ fn typecheck_block(operations: &Vec, ins: &Vec, outs: &Vec< Operation::Intrinsic(_, line, col) | Operation::While(_, line, col) | Operation::QueueDiagnostic(line, col) | + Operation::Apply(_, _, line, col) | Operation::Depth(line, col) => (*line, *col), } } @@ -408,7 +459,7 @@ fn typecheck_block(operations: &Vec, ins: &Vec, outs: &Vec< return Ok(()); } -fn get_return_type(operations: &Vec, ins: &Vec, functions: &Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, debug: bool) -> Result, String> +fn get_return_type(operations: &Vec, ins: &Vec, functions: &Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, arrays: &Vec, debug: bool) -> Result, String> { let type_queue: &mut Vec = &mut Vec::new(); type_queue.extend_from_slice(ins); @@ -457,12 +508,12 @@ fn get_return_type(operations: &Vec, ins: &Vec, functions: { if type_queue.is_empty() { - panic!("Attempted to get the first element for a swap while the queue was empty at {}:{}", line, col); + return Err(format!("Attempted to get the first element for a swap while the queue was empty at {}:{}", line, col)); } let first_typ = type_queue.remove(0); if type_queue.is_empty() { - panic!("Attempted to get the second element for a swap while the queue was empty at {}:{}", line, col); + return Err(format!("Attempted to get the second element for a swap while the queue was empty at {}:{}", line, col)); } let second_typ = type_queue.remove(0); type_queue.push(second_typ); @@ -500,7 +551,7 @@ fn get_return_type(operations: &Vec, ins: &Vec, functions: { println!("Starting to typecheck if block"); } - let if_ret = get_return_type(if_block, &type_queue, functions, intrinsics, debug)?; + let if_ret = get_return_type(if_block, &type_queue, functions, intrinsics, arrays, debug)?; let else_ret = if let Some(else_block) = maybe_else_block { @@ -508,7 +559,7 @@ fn get_return_type(operations: &Vec, ins: &Vec, functions: { println!("Starting to typecheck else block"); } - get_return_type(else_block, &type_queue, functions, intrinsics, debug)? + get_return_type(else_block, &type_queue, functions, intrinsics, arrays, debug)? } else { @@ -547,7 +598,7 @@ fn get_return_type(operations: &Vec, ins: &Vec, functions: let comparison_type = type_queue.remove(0); if comparison_type != Datatype::Bool { - panic!("Expected a Bool as a while condition but got {:?} instead at {}:{}", comparison_type, line, col); + return Err(format!("Expected a Bool as a while condition but got {:?} instead at {}:{}", comparison_type, line, col)); } if debug { @@ -555,7 +606,7 @@ fn get_return_type(operations: &Vec, ins: &Vec, functions: } let mut outs = type_queue.clone(); outs.insert(0, Datatype::Bool); - typecheck_block(while_block, type_queue, &outs, functions, intrinsics, debug)?; + typecheck_block(while_block, type_queue, &outs, functions, intrinsics, arrays, debug)?; } Operation::Depth(_, _) => { @@ -565,6 +616,39 @@ fn get_return_type(operations: &Vec, ins: &Vec, functions: { println!("---Type queue state at {}:{}---\nlength: {}\n{:?}\n------------------------------", line, col, type_queue.len(), type_queue); } + Operation::Apply(name, word, line, col) => + { + match word.as_str() + { + "write" => + { + if type_queue.is_empty() || type_queue.remove(0) != Datatype::Int + { + return Err(format!("Expected a position for a write application at {}:{}", line, col)); + } + let expected_type = arrays.iter().find(|x| &x.name == name).unwrap().datatype; + let actual_type = type_queue.remove(0); + if actual_type != expected_type + { + return Err(format!("Expected a {:?} value but got a {:?} value at {}:{}", expected_type, actual_type, line, col)); + } + } + "read" => + { + if type_queue.is_empty() || type_queue.remove(0) != Datatype::Int + { + return Err(format!("Expected a position for a read application at {}:{}", line, col)); + } + let typ = arrays.iter().find(|x| &x.name == name).unwrap().datatype; + type_queue.push(typ); + } + "length" => + { + type_queue.push(Datatype::Int); + } + _ => return Err(format!("Encountered unknown application '{}' at {}:{}", word, line, col)) + } + } } if debug { @@ -574,17 +658,17 @@ fn get_return_type(operations: &Vec, ins: &Vec, functions: return Ok(type_queue.clone()); } -fn validate_function_calls(operations: &Vec, functions: &Vec, debug: bool) -> Result<(), String> +fn validate_function_calls(operations: &Vec, functions: &Vec, arrays: &Vec, debug: bool) -> Result<(), String> { for function in functions { - validate_function_calls_in_block(&function.content, functions, debug)?; + validate_function_calls_in_block(&function.content, functions, arrays, debug)?; if debug { println!("Successfully validated function calls in function '{}'", function.name); } } - validate_function_calls_in_block(operations, functions, debug)?; + validate_function_calls_in_block(operations, functions, arrays, debug)?; if debug { println!("Successfully validated function calls in main operations"); @@ -592,14 +676,14 @@ fn validate_function_calls(operations: &Vec, functions: &Vec, functions: &Vec, debug: bool) -> Result<(), String> +fn validate_function_calls_in_block(block: &Vec, functions: &Vec, arrays: &Vec, debug: bool) -> Result<(), String> { for operation in block { match operation { Operation::Depth(_, _) | Operation::QueueDiagnostic(_, _) | Operation::Intrinsic(_, _, _) | Operation::Enqueue(_, _, _, _) | Operation::Dequeue(_, _) | - Operation::Requeue(_, _) | Operation::Dup(_, _) | Operation::Swap(_, _) => {}, + Operation::Requeue(_, _) | Operation::Dup(_, _) | Operation::Swap(_, _) | Operation::Apply(_, _, _, _) => {}, Operation::FunctionCall(function_name, line, col) => { if !functions.iter().any(|x| &x.name == function_name) @@ -609,21 +693,129 @@ fn validate_function_calls_in_block(block: &Vec, functions: &Vec { - validate_function_calls_in_block(if_block, functions, debug)?; + validate_function_calls_in_block(if_block, functions, arrays, debug)?; if let Some(else_block) = maybe_else_block { - validate_function_calls_in_block(else_block, functions, debug)?; + validate_function_calls_in_block(else_block, functions, arrays, debug)?; } } Operation::While(while_block, _, _) => { - validate_function_calls_in_block(while_block, functions, debug)?; + validate_function_calls_in_block(while_block, functions, arrays, debug)?; } } } return Ok(()); } +fn extract_arrays(tokens: &mut Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, functions: &Vec, debug: bool) -> Result, String> +{ + let mut tokens_iter = tokens.iter().peekable(); + let mut arrays: Vec = Vec::new(); + let mut new_tokens: Vec = Vec::new(); + while let Some(token) = tokens_iter.next() + { + if let Token::Keyword(word, line, col) = token + { + if word == "arr" + { + if debug + { + println!("Found an array at {}:{}", line, col); + } + if let Some(Token::Keyword(name, _, _)) = tokens_iter.next() + { + if functions.iter().any(|x| &x.name == name) + { + return Err(format!("Cannot redeclare an array with the same name as a function {}:{}", line, col)); + } + if arrays.iter().any(|x| &x.name == name) + { + return Err(format!("Cannot redeclare an array with the same name as an array {}:{}", line, col)); + } + if intrinsics.contains_key(name.as_str()) + { + return Err(format!("An array cannot have the same name as an intrinsic ({}) at {}:{}", name, line, col)); + } + if let Some(Token::Keyword(open_curly, _, _)) = tokens_iter.next() + { + if open_curly != "{" + { + return Err(format!("Expected '{{' in array declaration at {}:{}", line, col)); + } + } + else + { + return Err(format!("Reached the end of the file while parsing an array at {}:{}", line, col)); + } + if let Some(Token::Keyword(typ, _, _)) = tokens_iter.next() + { + let datatype = str_to_datatype(typ, *line, *col)?; + if let Some(Token::IntLit(size_str, _, _)) = tokens_iter.next() + { + let size = size_str.parse::().unwrap(); + if let Some(Token::Keyword(close_curly, _, _)) = tokens_iter.next() + { + if close_curly != "}" + { + return Err(format!("Expected '}}' in array declaration at {}:{}", line, col)); + } + } + else + { + return Err(format!("Reached the end of the file while parsing an array at {}:{}", line, col)); + } + let mut data: Vec = Vec::new(); + let default_val = match datatype + { + Datatype::Any | Datatype::String => String::new(), + Datatype::Bool => String::from("false"), + Datatype::Int => String::from("0"), + }; + for _ in 0..size + { + data.push(default_val.clone()); + } + arrays.push(Arr { name: name.clone(), datatype, length: size , data }); + } + } + else + { + return Err(format!("Reached the end of the file while parsing an array at {}:{}", line, col)) + } + } + else + { + return Err(format!("Expected array name, at {}:{}", line, col)); + } + } + else + { + new_tokens.push(token.clone()); + } + } + else + { + new_tokens.push(token.clone()); + } + } + tokens.clear(); + tokens.extend_from_slice(&new_tokens); + return Ok(arrays); +} + +fn str_to_datatype(s: &str, line: i32, col: i32) -> Result +{ + match s + { + "any" => Ok(Datatype::Any), + "bool" => Ok(Datatype::Bool), + "int" => Ok(Datatype::Int), + "str" => Ok(Datatype::String), + _ => return Err(format!("Expected a datatype for the array, got {} instead at {}:{}", s, line, col)) + } +} + fn extract_functions(tokens: &mut Vec, intrinsics: &HashMap<&str, (Vec, Vec)>, debug: bool) -> Result, String> { let mut tokens_iter = tokens.iter().peekable(); @@ -649,7 +841,8 @@ fn extract_functions(tokens: &mut Vec, intrinsics: &HashMap<&str, (Vec + Token::IntLit(_, line, col) | Token::StringLit(_, line, col) | Token::BoolLit(_, line, col) | + Token::Apply(_, _, line, col) => { return Err(format!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col)); } @@ -687,7 +880,8 @@ fn extract_functions(tokens: &mut Vec, intrinsics: &HashMap<&str, (Vec + Token::IntLit(_, line, col) | Token::StringLit(_, line, col) | Token::BoolLit(_, line, col) | + Token::Apply(_, _, line, col) => { return Err(format!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col)); } @@ -781,6 +975,10 @@ fn parse_until_delimiter(tokens_iter: &mut Peekable>, in { operations.push(Operation::Enqueue(Datatype::Bool, value.clone(), *line, *col)); } + Token::Apply(name, word, line, col) => + { + operations.push(Operation::Apply(name.clone(), word.clone(), *line, *col)); + } Token::Keyword(word, line, col) => { if intrinsics.contains_key(word.as_str()) @@ -881,6 +1079,7 @@ fn tokenize(text: &str) -> Result, String> let mut state = TokenizerState::Whitespace; let mut word = String::new(); let mut iter = text.chars().peekable(); + let mut application_name = String::new(); while let Some(ch) = iter.next() { if ch == '/' && iter.peek() == Some(&'/') @@ -933,17 +1132,25 @@ fn tokenize(text: &str) -> Result, String> if ch.is_whitespace() { state = TokenizerState::Whitespace; - if let Ok(_) = word.parse::() + if application_name.is_empty() { - tokens.push(Token::IntLit(word.clone(), line, col)); - } - else if word == "true" || word == "false" - { - tokens.push(Token::BoolLit(word.clone(), line, col)); + if let Ok(_) = word.parse::() + { + tokens.push(Token::IntLit(word.clone(), line, col)); + } + else if word == "true" || word == "false" + { + tokens.push(Token::BoolLit(word.clone(), line, col)); + } + else + { + tokens.push(Token::Keyword(word.clone(), line, col)); + } } else { - tokens.push(Token::Keyword(word.clone(), line, col)); + tokens.push(Token::Apply(application_name.clone(), word.clone(), line, col)); + application_name.clear(); } word.clear(); } @@ -952,6 +1159,11 @@ fn tokenize(text: &str) -> Result, String> match ch { '"' => return Err(format!("Having '\"' in the middle of a word is not allowed")), + '.' => + { + application_name = word.clone(); + word.clear(); + } _ => { word.push(ch); diff --git a/tests/rule110.qbl b/tests/rule110.qbl index af87104..590098c 100644 --- a/tests/rule110.qbl +++ b/tests/rule110.qbl @@ -1,4 +1,15 @@ -//valid,:END: +//valid,00000000000000110000000000000000 +//00000000000001110000000000000000 +//00000000000011010000000000000000 +//00000000000111110000000000000000 +//00000000001100010000000000000000 +//00000000011100110000000000000000 +//00000000110101110000000000000000 +//00000001111111010000000000000000 +//00000011000001110000000000000000 +//00000111000011010000000000000000 +//00001101000111110000000000000000 +//:END: function bool bool bool => bool rule110 { if @@ -24,9 +35,62 @@ function bool bool bool => bool rule110 } } -function int => bool intToBool +arr val { bool 32 } + +15 true val.write + +function int => bool int check { - 0 != + 0 dup 1 > - +} + +function => fullApply +{ + 0 false val2.write + true 1 + while + { + // i-1 i + 1 dup - req + // i a | i a i | a i b | i b a i 1 + val.read dup val.read req dup 1 + // i a b i+1 | i+1 i a b | i a b c + req swp + req req req val.read + // i d + req rule110 + // i d i | i + dup val2.write + checkUp-1 + } + 1 - false val2.write +} + +function int => bool int checkUp +{ + // i+1 l i+1 + 1 + val.length dup < req +} + +function int => bool int checkUp-1 +{ + // i+1 l 1 | l 1 i+1 | i+1 l-1 i+1 + 1 + val.length 1 req - dup + // i+1 b + < req +} + +arr val2 { bool 32 } + +function => copyArrays +{ + true 0 + while + { + dup val2.read + dup val.write + checkUp + } + deq } function bool => int boolToInt @@ -41,49 +105,24 @@ function bool => int boolToInt } } -true 10 - -true 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - -while +function => printArrays { - check + true 0 while { - // 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 - checkAndApply - // 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 true 31 0 0 0 - shift-5 + dup val.read req boolToInt req print + checkUp } + deq + "" println } -function any any any any any any any any any any any any any any any any any any any any any any any any any any any => any any any any any any any any any any any any any any any any any any any any any any any any any any any shift-5 -{ } - -function int int int int => bool int int int int checkAndApply +true 10 +while { - check apply -} - -function int => bool int -{ - 0 dup 1 > req - -} - -function int int int => int int int apply -{ - // a b c - intToBool - // b c d - dup intToBool - // c d b e - dup intToBool - // d b e c f - req swp swp - // d e b f c - req req swp - // c d e f b - req rule110 - // b c g - req req boolToInt + fullApply + copyArrays + printArrays + check } +deq \ No newline at end of file