Compare commits

...

3 Commits

Author SHA1 Message Date
0x4261756D
d1883ff3ab Introduce boolean types 2022-12-14 20:39:51 +01:00
0x4261756D
2a560cfcef Add and update tests 2022-12-14 11:46:47 +01:00
0x4261756D
c2800dfcc7 Make while swallow the input 2022-12-14 11:46:39 +01:00
5 changed files with 165 additions and 21 deletions

View File

@ -10,6 +10,7 @@ enum Token
{ {
StringLit(String, i32, i32), StringLit(String, i32, i32),
IntLit(String, i32, i32), IntLit(String, i32, i32),
BoolLit(String, i32, i32),
Keyword(String, i32, i32), Keyword(String, i32, i32),
} }
@ -26,6 +27,7 @@ enum Datatype
{ {
Int, Int,
String, String,
Bool,
//Pointer, //Pointer,
Any, Any,
} }
@ -54,7 +56,10 @@ enum Operation
{ {
Enqueue(Datatype, String, i32, i32), Enqueue(Datatype, String, i32, i32),
Dequeue(i32, i32), Dequeue(i32, i32),
// TODO: req can be implemented in terms of dup and dequeue
Requeue(i32, i32), Requeue(i32, i32),
Swap(i32, i32),
Dup(i32, i32),
Intrinsic(String, i32, i32), Intrinsic(String, i32, i32),
FunctionCall(String, i32, i32), FunctionCall(String, i32, i32),
If(Vec<Operation>, Option<Vec<Operation>>, i32, i32), If(Vec<Operation>, Option<Vec<Operation>>, i32, i32),
@ -67,6 +72,11 @@ fn main()
[ [
("print", (Vec::from([Datatype::Any]), Vec::new())), ("print", (Vec::from([Datatype::Any]), Vec::new())),
("-", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Int]))), ("-", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Int]))),
("+", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Int]))),
("<", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))),
(">", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))),
("==", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))),
("!=", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))),
]); ]);
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if args.len() < 2 if args.len() < 2
@ -132,6 +142,18 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
let val = queue.remove(0); let val = queue.remove(0);
queue.push(val); queue.push(val);
} }
Operation::Dup(_, _) =>
{
let val = queue.get(0).unwrap();
queue.push(val.clone());
}
Operation::Swap(_, _) =>
{
let first = queue.remove(0);
let second = queue.remove(0);
queue.push(second);
queue.push(first);
}
Operation::FunctionCall(function_name, _, _) => Operation::FunctionCall(function_name, _, _) =>
{ {
interpret_program(&functions.iter().find(|x| &x.name == function_name).unwrap().content, queue, functions, intrinsics, debug); interpret_program(&functions.iter().find(|x| &x.name == function_name).unwrap().content, queue, functions, intrinsics, debug);
@ -140,7 +162,7 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
{ {
let val = queue.remove(0); let val = queue.remove(0);
// TODO: Add bool type // TODO: Add bool type
if val == "0" if val == "true"
{ {
interpret_program(if_block, queue, functions, intrinsics, debug); interpret_program(if_block, queue, functions, intrinsics, debug);
} }
@ -163,6 +185,36 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
let subtrahend = queue.remove(0).parse::<i64>().unwrap(); let subtrahend = queue.remove(0).parse::<i64>().unwrap();
queue.push((minuend - subtrahend).to_string()); queue.push((minuend - subtrahend).to_string());
} }
"+" =>
{
let addend1 = queue.remove(0).parse::<i64>().unwrap();
let addend2 = queue.remove(0).parse::<i64>().unwrap();
queue.push((addend1 + addend2).to_string());
}
">" =>
{
let first = queue.remove(0).parse::<i64>().unwrap();
let second = queue.remove(0).parse::<i64>().unwrap();
queue.push((first > second).to_string());
}
"<" =>
{
let first = queue.remove(0).parse::<i64>().unwrap();
let second = queue.remove(0).parse::<i64>().unwrap();
queue.push((first < second).to_string());
}
"==" =>
{
let first = queue.remove(0).parse::<i64>().unwrap();
let second = queue.remove(0).parse::<i64>().unwrap();
queue.push((first == second).to_string());
}
"!=" =>
{
let first = queue.remove(0).parse::<i64>().unwrap();
let second = queue.remove(0).parse::<i64>().unwrap();
queue.push((first != second).to_string());
}
_ => _ =>
{ {
panic!("Unexpected intrinsic '{}' at {}:{}", intrinsic_name, line, col); panic!("Unexpected intrinsic '{}' at {}:{}", intrinsic_name, line, col);
@ -173,8 +225,8 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
{ {
loop loop
{ {
let val = queue.get(0).unwrap(); let val = queue.remove(0);
if val == "0" if val == "false"
{ {
break; break;
} }
@ -227,6 +279,8 @@ fn typecheck_block(operations: &Vec<Operation>, ins: &Vec<Datatype>, outs: &Vec<
{ {
Operation::Enqueue(_, _, line, col) | Operation::Enqueue(_, _, line, col) |
Operation::Requeue(line, col) | Operation::Requeue(line, col) |
Operation::Dup(line, col) |
Operation::Swap(line, col) |
Operation::FunctionCall(_, line, col) | Operation::FunctionCall(_, line, col) |
Operation::If(_, _, line, col) | Operation::If(_, _, line, col) |
Operation::Intrinsic(_, line, col) | Operation::Intrinsic(_, line, col) |
@ -265,6 +319,17 @@ fn get_return_type(operations: &Vec<Operation>, ins: &Vec<Datatype>, functions:
{ {
type_queue.push(*datatype); type_queue.push(*datatype);
} }
Operation::Dup(line, col) =>
{
if let Some(typ) = type_queue.get(0)
{
type_queue.push(typ.clone());
}
else
{
panic!("Attempted to dup an element while the queue was empty at {}:{}", line, col);
}
}
Operation::Requeue(line, col) => Operation::Requeue(line, col) =>
{ {
if type_queue.is_empty() if type_queue.is_empty()
@ -274,6 +339,21 @@ fn get_return_type(operations: &Vec<Operation>, ins: &Vec<Datatype>, functions:
let typ = type_queue.remove(0); let typ = type_queue.remove(0);
type_queue.push(typ); type_queue.push(typ);
} }
Operation::Swap(line, col) =>
{
if type_queue.is_empty()
{
panic!("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);
}
let second_typ = type_queue.remove(0);
type_queue.push(second_typ);
type_queue.push(first_typ);
}
Operation::FunctionCall(function_name, line, col) => Operation::FunctionCall(function_name, line, col) =>
{ {
let function = functions.iter().find(|x| &x.name == function_name).unwrap(); let function = functions.iter().find(|x| &x.name == function_name).unwrap();
@ -298,9 +378,9 @@ fn get_return_type(operations: &Vec<Operation>, ins: &Vec<Datatype>, functions:
panic!("Encountered if block with an empty queue at {}:{}", line, col); panic!("Encountered if block with an empty queue at {}:{}", line, col);
} }
let comparison_type = type_queue.remove(0); let comparison_type = type_queue.remove(0);
if comparison_type != Datatype::Int if comparison_type != Datatype::Bool
{ {
panic!("Expected an int as an if condition but got {:?} instead at {}:{}", comparison_type, line, col); panic!("Expected a Bool as an if condition but got {:?} instead at {}:{}", comparison_type, line, col);
} }
if debug if debug
{ {
@ -350,16 +430,18 @@ fn get_return_type(operations: &Vec<Operation>, ins: &Vec<Datatype>, functions:
{ {
panic!("Encountered while block with an empty queue at {}:{}", line, col); panic!("Encountered while block with an empty queue at {}:{}", line, col);
} }
let &comparison_type = type_queue.get(0).unwrap(); let comparison_type = type_queue.remove(0);
if comparison_type != Datatype::Int if comparison_type != Datatype::Bool
{ {
panic!("Expected an int as a while condition but got {:?} instead at {}:{}", comparison_type, line, col); panic!("Expected a Bool as a while condition but got {:?} instead at {}:{}", comparison_type, line, col);
} }
if debug if debug
{ {
println!("Starting to typecheck while block"); println!("Starting to typecheck while block");
} }
typecheck_block(while_block, type_queue, type_queue, functions, intrinsics, debug); let mut outs = type_queue.clone();
outs.insert(0, Datatype::Bool);
typecheck_block(while_block, type_queue, &outs, functions, intrinsics, debug);
} }
} }
if debug if debug
@ -393,12 +475,12 @@ fn validate_function_calls_in_block(block: &Vec<Operation>, functions: &Vec<Func
{ {
match operation match operation
{ {
Operation::Intrinsic(_, _, _) | Operation::Enqueue(_, _, _, _) | Operation::Dequeue(_, _) | Operation::Requeue(_, _) => {}, Operation::Intrinsic(_, _, _) | Operation::Enqueue(_, _, _, _) | Operation::Dequeue(_, _) | Operation::Requeue(_, _) | Operation::Dup(_, _) | Operation::Swap(_, _) => {},
Operation::FunctionCall(function_name, line, col) => Operation::FunctionCall(function_name, line, col) =>
{ {
if !functions.iter().any(|x| &x.name == function_name) if !functions.iter().any(|x| &x.name == function_name)
{ {
panic!("Call to unknown function {} at {}:{}", function_name, line, col); panic!("Call to unknown function '{}' at {}:{}", function_name, line, col);
} }
} }
Operation::If(if_block, maybe_else_block, _, _) => Operation::If(if_block, maybe_else_block, _, _) =>
@ -442,7 +524,7 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
{ {
match token match token
{ {
Token::IntLit(_, line, col) | Token::StringLit(_, line, col) => Token::IntLit(_, line, col) | Token::StringLit(_, line, col) | Token::BoolLit(_, line, col) =>
{ {
panic!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col); panic!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col);
} }
@ -457,6 +539,7 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
"any" => ins.push(Datatype::Any), "any" => ins.push(Datatype::Any),
"str" => ins.push(Datatype::String), "str" => ins.push(Datatype::String),
"int" => ins.push(Datatype::Int), "int" => ins.push(Datatype::Int),
"bool" => ins.push(Datatype::Bool),
_ => panic!("Expected input parameters for a function but got {} instead at {}:{}", word, line, col) _ => panic!("Expected input parameters for a function but got {} instead at {}:{}", word, line, col)
} }
} }
@ -479,7 +562,7 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
{ {
match token match token
{ {
Token::IntLit(_, line, col) | Token::StringLit(_, line, col) => Token::IntLit(_, line, col) | Token::StringLit(_, line, col) | Token::BoolLit(_, line, col) =>
{ {
panic!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col); panic!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col);
} }
@ -490,7 +573,8 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
"any" => outs.push(Datatype::Any), "any" => outs.push(Datatype::Any),
"str" => outs.push(Datatype::String), "str" => outs.push(Datatype::String),
"int" => outs.push(Datatype::Int), "int" => outs.push(Datatype::Int),
"{" | "}" | "deq" | "req" => panic!("Expected function name but got {} at {}:{}", word, line, col), "bool" => ins.push(Datatype::Bool),
"{" | "}" | "deq" | "req" | "dup" | "swp" | "true" | "false" => panic!("Expected function name but got {} at {}:{}", word, line, col),
_ => _ =>
{ {
if functions.iter().any(|x| &x.name == word) if functions.iter().any(|x| &x.name == word)
@ -568,6 +652,10 @@ fn parse_until_delimiter(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, in
{ {
operations.push(Operation::Enqueue(Datatype::String, value.clone(), *line, *col)); operations.push(Operation::Enqueue(Datatype::String, value.clone(), *line, *col));
} }
Token::BoolLit(value, line, col) =>
{
operations.push(Operation::Enqueue(Datatype::Bool, value.clone(), *line, *col));
}
Token::Keyword(word, line, col) => Token::Keyword(word, line, col) =>
{ {
if intrinsics.contains_key(word.as_str()) if intrinsics.contains_key(word.as_str())
@ -606,9 +694,16 @@ fn parse_until_delimiter(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, in
} }
else if word == "req" else if word == "req"
{ {
operations.push(Operation::Requeue(*line, *col)); operations.push(Operation::Requeue(*line, *col));
} }
else if word == "dup"
{
operations.push(Operation::Dup(*line, *col));
}
else if word == "swp"
{
operations.push(Operation::Swap(*line, *col));
}
else if Some(word.as_str()) == delimiter else if Some(word.as_str()) == delimiter
{ {
return operations; return operations;
@ -692,7 +787,7 @@ fn tokenize(text: &str) -> Vec<Token>
if ch == '"' if ch == '"'
{ {
state = TokenizerState::Whitespace; state = TokenizerState::Whitespace;
tokens.push(Token::StringLit(word.clone(), line, col)); tokens.push(Token::StringLit(word.clone().replace("\\n", "\n"), line, col));
word.clear(); word.clear();
} }
else else
@ -709,6 +804,10 @@ fn tokenize(text: &str) -> Vec<Token>
{ {
tokens.push(Token::IntLit(word.clone(), line, col)); tokens.push(Token::IntLit(word.clone(), line, col));
} }
else if word == "true" || word == "false"
{
tokens.push(Token::BoolLit(word.clone(), line, col));
}
else else
{ {
tokens.push(Token::Keyword(word.clone(), line, col)); tokens.push(Token::Keyword(word.clone(), line, col));

13
tests/recursion.qbl Normal file
View File

@ -0,0 +1,13 @@
function int int int => int fibonacci
{
dup if
{
req deq deq
}
else
{
1 dup + - swp fibonacci
}
}
20 1 0 fibonacci print

7
tests/req_impl.qbl Normal file
View File

@ -0,0 +1,7 @@
function int int => int int req_impl
{
dup deq
}
1 2 3 req_impl print print print
1 2 3 req print print print

View File

@ -1,5 +1,6 @@
//valid //valid
//output: Hello, World!\n4242test2Falsetesttesttest //output: Hello, World!
//4242test2Falsetesttesttest
"Hello, World!\n" print 43 foo foo deq "Hello, World!\n" print 43 foo foo deq
@ -10,12 +11,12 @@ function any => int foo
deq 42 17 print deq 42 17 print
} }
"test2" print 1 "test2" print false
check check
print print
function int => str check function bool => str check
{ {
if if
{ {
@ -29,9 +30,12 @@ function int => str check
function int => whileFunction function int => whileFunction
{ {
while dup
0 req >
req while
{ {
1 - "test" req print 1 - 0 dup >
"test" req req print req
} }
deq deq
} }

21
tests/while.qbl Normal file
View File

@ -0,0 +1,21 @@
//valid,output:10987654321
true while
{
false
}
10 0 dup > req
while
{
dup print
1 - 0 dup > req
}
deq
true true true while
{
false
}
print print