Compare commits

...

8 Commits

Author SHA1 Message Date
0x4261756D 3d2cd64c6d Implement enough asm generation to compile rule110 2023-01-03 21:52:48 +01:00
0x4261756D 870dcb105d Readd queue runaway optimizations in comparisons 2023-01-03 20:55:05 +01:00
0x4261756D a754602bde Readd queue runaway optimization in intToStr 2023-01-03 20:44:07 +01:00
0x4261756D 84442e5eb9 Finish enough asm generation to compile tests/while.qbl 2023-01-03 20:39:08 +01:00
0x4261756D 5bae80e9aa Assembly generation, WIP 2023-01-03 17:07:57 +01:00
0x4261756D fbccc9dd15 Remove any from syntax highlighting 2022-12-31 16:14:52 +01:00
0x4261756D cdfa5b1310 Fix tests failing on Windows due to EOL shenanigans 2022-12-31 16:12:53 +01:00
0x4261756D 41667ff4a3 Remove the "any" type 2022-12-31 16:09:55 +01:00
14 changed files with 454 additions and 73 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target
out*

View File

@ -21,7 +21,7 @@ rules:
- symbol.brackets: "[{}]"
# function
- identifier: "function"
- type: "\\b(bool|int|str|any)\\b"
- type: "\\b(bool|int|str)\\b"
- symbol.operator: "([-+<>]|==|!=|=>|print(ln)?)"
- identifier: "\\b(if|else|while)\\b"
- special: "\\b(deq|swp|dup|req|depth|decrease)\\b"

View File

@ -33,7 +33,7 @@
},
{
"name": "support.type.kurz",
"match": "\\b(bool|int|str|any)\\b"
"match": "\\b(bool|int|str)\\b"
},
{
"name": "constant.numeric.kurz",

View File

@ -1,7 +1,6 @@
use core::panic;
use std::collections::HashMap;
use std::env;
use std::fmt::format;
use std::fs;
use std::iter::Peekable;
use std::process::exit;
@ -24,25 +23,25 @@ enum TokenizerState
Comment,
}
#[derive(Debug,Clone,Copy)]
#[derive(Debug,Clone,Copy, PartialEq)]
enum Datatype
{
Int,
String,
Bool,
//Pointer,
Any,
// Any,
}
impl PartialEq for Datatype
{
fn eq(&self, other: &Self) -> bool
{
core::mem::discriminant(self) == core::mem::discriminant(&Datatype::Any) ||
core::mem::discriminant(other) == core::mem::discriminant(&Datatype::Any) ||
core::mem::discriminant(self) == core::mem::discriminant(other)
}
}
// impl PartialEq for Datatype
// {
// fn eq(&self, other: &Self) -> bool
// {
// core::mem::discriminant(self) == core::mem::discriminant(&Datatype::Any) ||
// core::mem::discriminant(other) == core::mem::discriminant(&Datatype::Any) ||
// core::mem::discriminant(self) == core::mem::discriminant(other)
// }
// }
#[derive(Debug)]
struct Function
@ -78,20 +77,23 @@ enum Operation
Apply(String, String, i32, i32),
Depth(i32, i32),
QueueDiagnostic(i32, i32),
Interrupt(i32, i32),
}
fn main()
{
let intrinsics: HashMap<&str, (Vec<Datatype>, Vec<Datatype>)> = HashMap::from(
[
("print", (Vec::from([Datatype::Any]), Vec::new())),
("println", (Vec::from([Datatype::Any]), Vec::new())),
("print", (Vec::from([Datatype::String]), Vec::new())),
("println", (Vec::from([Datatype::String]), Vec::new())),
("intToStr", (Vec::from([Datatype::Int]), Vec::from([Datatype::String]))),
("-", (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]))),
("==", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))),
("!=", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Bool]))),
("&&", (Vec::from([Datatype::Bool, Datatype::Bool]), Vec::from([Datatype::Bool]))),
@ -121,7 +123,7 @@ fn main()
for f in fs::read_dir(&args[2]).unwrap()
{
let f = f.unwrap();
let file_content = fs::read_to_string(f.path()).unwrap();
let file_content = fs::read_to_string(f.path()).unwrap().replace("\r\n", "\n");
println!("========NOW TESTING {:?}========", f.path());
match compile(&file_content, &intrinsics, interpret, debug)
{
@ -246,34 +248,70 @@ fn merge_assemblies(data: &mut AssemblyData, data2: AssemblyData)
const ASSEMBLY_LINUX_X64_QUEUE_LENGTH: u32 = 1024;
const ASSEMBLY_LINUX_X64_HEADER: &str = "format ELF64 executable 3\n";
const ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE: &str = "\tcmp r8, r9\n\tcmove r8, r10\n\tcmove r9, r10\n";
const ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE: &str = "\tcmp r12, r13\n\tcmove r12, r14\n\tcmove r13, r14\n";
const ASSEMBLY_LINUX_X64_EXIT: &str = "\tmov rax, 60\n\tmov rdi, 0\n\tsyscall\n";
const ASSEMBLY_LINUX_X64_DYNAMIC_DATA_LENGTH: u32 = 16384;
fn generate_assembly_linux_x64(operations: &Vec<Operation>, functions: &Vec<Function>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, arrays: &Vec<Arr>, debug: bool) -> Result<(), std::io::Error>
{
let mut data = AssemblyData
{
arrays: format!("segment readable writeable\n\tqueue: rq {}\n", ASSEMBLY_LINUX_X64_QUEUE_LENGTH),
strings: String::from("segment readable\n"),
arrays: format!("segment readable writeable\n\tqueue: rq {}\n\tdynamic: rb {}\n", ASSEMBLY_LINUX_X64_QUEUE_LENGTH, ASSEMBLY_LINUX_X64_DYNAMIC_DATA_LENGTH),
strings: String::from("segment readable\n\tnewline: db 10\n"),
code: String::from("segment executable\n"),
};
for array in arrays
{
data.arrays += format!("\tarr_{}: rq {}\n", array.name, array.length).as_str();
}
data.code += "_start:\n";
merge_assemblies(&mut data, generate_assembly_linux_x64_block(operations, functions, intrinsics, arrays, debug));
data.code += ASSEMBLY_LINUX_X64_EXIT;
for function in functions
{
merge_assemblies(&mut data, generate_assembly_linux_x64_function(function.name.as_str(), &function.content, functions, intrinsics, arrays, debug));
}
merge_assemblies(&mut data, generate_assembly_linux_x64_function("_start", operations, functions, intrinsics, arrays, debug));
data.code += ASSEMBLY_LINUX_X64_EXIT;
if data.code.contains("call intToStr")
{
data.code += "intToStr:\n";
data.code += "\tmov rax, rdi\n";
data.code += "\tmov rsi, 10\n";
data.code += "\txor rdi, rdi\n";
data.code += "\txor rdx, rdx\n";
data.code += "\tintToStringLoop:\n";
data.code += "\t\tdiv rsi\n";
data.code += "\t\tadd rdx, 48\n";
data.code += "\t\tpush rdx\n";
data.code += "\t\txor rdx, rdx\n";
data.code += "\t\tinc rdi\n";
data.code += "\t\tcmp rax, 0\n";
data.code += "\t\tjne intToStringLoop\n";
data.code += "\tmov rsi, r15\n";
data.code += "\tmov qword [dynamic+r15], rdi\n";
data.code += "\tadd r15, 8\n";
data.code += "\tintToStringBuildLoop:\n";
data.code += "\t\tcmp rdi, 0\n";
data.code += "\t\tje intToStringBuildLoopEnd\n";
data.code += "\t\tpop rax\n";
data.code += "\t\tmov byte [dynamic+r15], byte al\n";
data.code += "\t\tinc r15\n";
data.code += "\t\tdec rdi\n";
data.code += "\t\tjmp intToStringBuildLoop\n";
data.code += "\tintToStringBuildLoopEnd:\n";
data.code += "\tmov byte [dynamic+r15], 0\n";
data.code += "\tinc r15\n";
data.code += "\tlea rax, [dynamic+rsi]\n";
data.code += "\tret\n";
}
return fs::write("out.asm", format!("{}{}{}{}", ASSEMBLY_LINUX_X64_HEADER, data.code, data.arrays, data.strings));
}
// r8: head
// r9: tail
// r10: base
// r12: head
// r13: tail
// r14: base
// r15: dynamic end
fn generate_assembly_linux_x64_block(operations: &Vec<Operation>, functions: &Vec<Function>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, arrays: &Vec<Arr>, debug: bool) -> AssemblyData
{
@ -290,7 +328,7 @@ fn generate_assembly_linux_x64_block(operations: &Vec<Operation>, functions: &Ve
Operation::Dequeue(line, col) =>
{
data.code += format!("\t;;deq {}:{}\n", line, col).as_str();
data.code += "\tinc r8\n";
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
}
Operation::Enqueue(datatype, value, line, col) =>
@ -298,50 +336,321 @@ fn generate_assembly_linux_x64_block(operations: &Vec<Operation>, functions: &Ve
data.code += format!("\t;;enq {:?} {} {}:{}\n", datatype, value, line, col).as_str();
match datatype
{
Datatype::Int | Datatype::Bool =>
Datatype::Int =>
{
data.code += format!("\tmov qword [queue+r9], {}\n", value).as_str();
data.code += format!("\tmov qword [queue+8*r13], {}\n", value).as_str();
}
Datatype::Bool =>
{
data.code += format!("\tmov qword [queue+8*r13], {}\n", if value == "true" { 1 } else { 0 }).as_str();
}
Datatype::String =>
{
data.strings += format!("\tstr_{}_{}: dq {}, \"{}\", 0\n", line, col, value.len(), value).as_str();
data.code += format!("\tlea rax, [str_{}_{}]\n", line, col).as_str();
data.code += "\tmov [queue+8*r13], rax\n";
}
_ => todo!("enq {:?}", datatype)
}
data.code += "\tinc r9\n";
data.code += "\tinc r13\n";
}
Operation::Requeue(line, col) =>
{
data.code += format!("\t;;req {}:{}\n", line, col).as_str();
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r12\n";
data.code += "\tinc r13\n";
}
Operation::Swap(line, col) =>
{
data.code += format!("\t;;swp {}:{}\n", line, col).as_str();
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tmov rbx, [queue+8*r12+8]\n";
data.code += "\tadd r12, 2\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tmov [queue+8*r13], rbx\n";
data.code += "\tmov [queue+8*r13+8], rax\n";
data.code += "\tadd r13, 2\n";
}
Operation::While(while_operations, line, col) =>
{
data.code += format!("\t;;while {}:{}\n", line, col).as_str();
data.code += format!("while_{}_{}:\n", line, col).as_str();
data.code += "\tcmp qword [queue+r8], 0\n";
data.code += "\tcmp qword [queue+8*r12], 0\n";
data.code += format!("\tje while_{}_{}_end\n", line, col).as_str();
data.code += "\tinc r8\n";
data.code += format!("while_{}_{}:\n", line, col).as_str();
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
merge_assemblies(&mut data, generate_assembly_linux_x64_block(while_operations, functions, intrinsics, arrays, debug));
data.code += format!("\tjmp while_{}_{}\n", line, col).as_str();
data.code += "\tcmp qword [queue+8*r12], 0\n";
data.code += format!("\tjne while_{}_{}\n", line, col).as_str();
data.code += format!("while_{}_{}_end:\n", line, col).as_str();
data.code += "\tinc r8\n";
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
}
Operation::If(if_operations, maybe_else_operations, line, col) =>
{
data.code += format!("\t;;if {}:{}\n", line, col).as_str();
data.code += "\tcmp qword [queue+8*r12], 0\n";
data.code += format!("\tje else_{}_{}\n", line, col).as_str();
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
merge_assemblies(&mut data, generate_assembly_linux_x64_block(if_operations, functions, intrinsics, arrays, debug));
data.code += format!("\tjmp if_{}_{}_end\n", line, col).as_str();
data.code += format!("else_{}_{}:\n", line, col).as_str();
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
if let Some(else_operations) = maybe_else_operations
{
merge_assemblies(&mut data, generate_assembly_linux_x64_block(else_operations, functions, intrinsics, arrays, debug));
}
data.code += format!("if_{}_{}_end:\n", line, col).as_str();
}
Operation::Dup(line, col) =>
{
data.code += format!("\t;;dup {}:{}\n", line, col).as_str();
data.code += "\tmov qword [queue+r9], [queue+r8]\n";
data.code += "\tinc r9\n";
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r13\n";
}
Operation::Intrinsic(name, line, col) =>
{
data.code += format!("\t;;intrinsic {} {}:{}", name, line, col).as_str();
data.code += format!("\t;;intrinsic {} {}:{}\n", name, line, col).as_str();
match name.as_str()
{
"print" =>
{
// For now printing numbers directly is unsupported
data.code += "\trax, 1\n";
data.code += "\trdi, 1\n";
data.code += "\tmov rsi, [queue+r8]\n";
data.code += "\tmov "
data.code += "\tmov rax, 1\n";
data.code += "\tmov rdi, 1\n";
// load address
data.code += "\tmov rsi, [queue+8*r12]\n";
// size
data.code += "\tmov rdx, [rsi]\n";
// data
data.code += "\tlea rsi, [rsi+8]\n";
// incorporate the null byte
data.code += "\tinc rdx\n";
data.code += "\tsyscall\n";
// TODO: factor this out
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
}
"println" =>
{
// For now printing numbers directly is unsupported
data.code += "\tmov rax, 1\n";
data.code += "\tmov rdi, 1\n";
// load address
data.code += "\tmov rsi, [queue+8*r12]\n";
// size
data.code += "\tmov rdx, [rsi]\n";
// data
data.code += "\tlea rsi, [rsi+8]\n";
// incorporate the null byte
data.code += "\tinc rdx\n";
data.code += "\tsyscall\n";
// TODO: factor this out
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
// TODO: Don't syscall twice
data.code += "\tmov rax, 1\n";
data.code += "\tlea rsi, [newline]\n";
data.code += "\tmov rdx, 1\n";
data.code += "\tsyscall\n";
}
"intToStr" =>
{
data.code += "\tmov qword rdi, [queue+8*r12]\n";
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tcall intToStr\n";
data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r13\n";
}
"-" =>
{
data.code += "\tmov qword rax, [queue+8*r12]\n";
data.code += "\tinc r12\n";
data.code += "\tmov qword rbx, [queue+8*r12]\n";
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tsub rax, rbx\n";
data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r13\n";
}
"+" =>
{
data.code += "\tmov qword rax, [queue+8*r12]\n";
data.code += "\tinc r12\n";
data.code += "\tmov qword rbx, [queue+8*r12]\n";
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tadd rax, rbx\n";
data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r13\n";
}
"*" =>
{
data.code += "\tmov qword rax, [queue+8*r12]\n";
data.code += "\tinc r12\n";
data.code += "\tmov qword rbx, [queue+8*r12]\n";
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tmul rbx\n";
data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r13\n";
}
">" =>
{
data.code += "\tmov rbx, 0\n";
data.code += "\tmov rcx, 1\n";
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tcmp qword rax, [queue+8*r12+8]\n";
data.code += "\tcmovg rbx, rcx\n";
data.code += "\tadd r12, 2\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tmov qword [queue+8*r13], rbx\n";
data.code += "\tinc r13\n";
}
"<" =>
{
data.code += "\tmov rbx, 0\n";
data.code += "\tmov rcx, 1\n";
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tcmp qword rax, [queue+8*r12+8]\n";
data.code += "\tcmovl rbx, rcx\n";
data.code += "\tadd r12, 2\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tmov qword [queue+8*r13], rbx\n";
data.code += "\tinc r13\n";
}
">=" =>
{
data.code += "\tmov rbx, 0\n";
data.code += "\tmov rcx, 1\n";
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tcmp qword rax, [queue+8*r12+8]\n";
data.code += "\tcmovge rbx, rcx\n";
data.code += "\tadd r12, 2\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tmov qword [queue+8*r13], rbx\n";
data.code += "\tinc r13\n";
}
"<=" =>
{
data.code += "\tmov rbx, 0\n";
data.code += "\tmov rcx, 1\n";
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tcmp qword rax, [queue+8*r12+8]\n";
data.code += "\tcmovle rbx, rcx\n";
data.code += "\tadd r12, 2\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tmov qword [queue+8*r13], rbx\n";
data.code += "\tinc r13\n";
}
"==" =>
{
data.code += "\tmov rbx, 0\n";
data.code += "\tmov rcx, 1\n";
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tcmp qword rax, [queue+8*r12+8]\n";
data.code += "\tcmove rbx, rcx\n";
data.code += "\tadd r12, 2\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tmov qword [queue+8*r13], rbx\n";
data.code += "\tinc r13\n";
}
"&&" =>
{
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tmov rbx, [queue+8*r12+8]\n";
data.code += "\tand rax, rbx\n";
data.code += "\tadd r12, 2\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r13\n";
}
_ => todo!("intrinsic {} {}:{}", name, line, col)
}
}
Operation::Apply(name, word, line, col) =>
{
data.code += format!("\t;;apply {}.{} {}:{}\n", name, word, line, col).as_str();
match word.as_str()
{
"read" =>
{
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += format!("\tmov qword rbx, [arr_{}+8*rax]\n", name).as_str();
data.code += "\tmov qword [queue+8*r13], rbx\n";
data.code += "\tinc r13\n";
}
"write" =>
{
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tmov qword rbx, [queue+8*r12]\n";
data.code += format!("\tmov qword [arr_{}+8*rax], rbx\n", name).as_str();
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
}
"length" =>
{
let array = arrays.iter().find(|x| &x.name == name).unwrap();
data.code += format!("\tmov qword [queue+8*r13], {}\n", array.length).as_str();
data.code += "\tinc r13\n";
}
_ => todo!("apply {}", word)
}
}
Operation::FunctionCall(name, line, col) =>
{
data.code += format!("\t;;func call {} {}:{}\n", name, line, col).as_str();
let function = functions.iter().find(|x| &x.name == name).unwrap();
for _ in 0..function.ins.len()
{
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r12\n";
data.code += "\tinc r13\n";
}
data.code += "\t;; move pointers\n";
// save the current base
data.code += "\tpush r14\n";
// save the current head
data.code += "\tpush r12\n";
// prepare the layout
data.code += "\tmov r14, r13\n";
data.code += format!("\tsub r14, {}\n", function.ins.len()).as_str();
data.code += "\tmov r12, r14\n";
// call
data.code += format!("\tcall {}\n", name).as_str();
// move the sub-queue back to the base
for _ in 0..function.outs.len()
{
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tmov [queue+8*r14], rax\n";
data.code += "\tinc r12\n";
data.code += "\tinc r14\n";
}
// restore the tail
data.code += "\tmov r13, r14\n";
// restore the head
data.code += "\tpop r12\n";
// restore the base
data.code += "\tpop r14\n";
}
Operation::Interrupt(line, col) =>
{
data.code += format!("\t;;interrupt {}:{}\n", line, col).as_str();
data.code += "lea r8, [queue]\n";
data.code += format!("mov r9, {}\n", 1000*line + col).as_str();
data.code += "int3\n";
}
_ => todo!("{:?}", operation)
}
}
@ -357,6 +666,7 @@ fn generate_assembly_linux_x64_function(name: &str, operations: &Vec<Operation>,
strings: String::new(),
};
merge_assemblies(&mut data, generate_assembly_linux_x64_block(operations, functions, intrinsics, arrays, debug));
data.code += "\tret\n";
return data;
}
@ -455,17 +765,23 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
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());
queue.push((first <= second).to_string());
}
"==" =>
{
@ -494,6 +810,11 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
{
output += format!("{}\n", queue.remove(0)).as_str();
}
"intToStr" =>
{
let val = queue.remove(0).clone();
queue.push(val);
}
_ =>
{
return Err(format!("Unexpected intrinsic '{}' at {}:{}", intrinsic_name, line, col));
@ -568,6 +889,7 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
{
println!("---Queue state at {}:{}---\nlength: {}\n{:?}\n------------------------------", line, col, queue.len(), queue);
}
Operation::Interrupt(_, _) => {}
}
if debug
{
@ -614,6 +936,7 @@ fn typecheck_block(operations: &Vec<Operation>, ins: &Vec<Datatype>, outs: &Vec<
{
match operation
{
Operation::Interrupt(line, col) |
Operation::Enqueue(_, _, line, col) |
Operation::Dequeue(line, col) |
Operation::Requeue(line, col) |
@ -792,6 +1115,7 @@ fn get_return_type(operations: &Vec<Operation>, ins: &Vec<Datatype>, functions:
{
println!("---Type queue state at {}:{}---\nlength: {}\n{:?}\n------------------------------", line, col, type_queue.len(), type_queue);
}
Operation::Interrupt(_, _) => {}
Operation::Apply(name, word, line, col) =>
{
match word.as_str()
@ -863,7 +1187,7 @@ fn validate_function_calls_in_block(block: &Vec<Operation>, functions: &Vec<Func
match operation
{
Operation::Depth(_, _) | Operation::QueueDiagnostic(_, _) | Operation::Intrinsic(_, _, _) | Operation::Enqueue(_, _, _, _) | Operation::Dequeue(_, _) |
Operation::Requeue(_, _) | Operation::Dup(_, _) | Operation::Swap(_, _) | Operation::Apply(_, _, _, _) => {},
Operation::Requeue(_, _) | Operation::Dup(_, _) | Operation::Swap(_, _) | Operation::Apply(_, _, _, _) | Operation::Interrupt(_, _) => {},
Operation::FunctionCall(function_name, line, col) =>
{
if !functions.iter().any(|x| &x.name == function_name)
@ -948,7 +1272,7 @@ fn extract_arrays(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Datat
let mut data: Vec<String> = Vec::new();
let default_val = match datatype
{
Datatype::Any | Datatype::String => String::new(),
Datatype::String => String::new(),
Datatype::Bool => String::from("false"),
Datatype::Int => String::from("0"),
};
@ -956,7 +1280,7 @@ fn extract_arrays(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Datat
{
data.push(default_val.clone());
}
arrays.push(Arr { name: name.clone(), datatype, length: size , data });
arrays.push(Arr { name: sanitize_name(name.clone()), datatype, length: size , data });
}
}
else
@ -984,11 +1308,16 @@ fn extract_arrays(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Datat
return Ok(arrays);
}
fn sanitize_name(name: String) -> String
{
return name.replace("-", "_").replace("+", "_");
}
fn str_to_datatype(s: &str, line: i32, col: i32) -> Result<Datatype, String>
{
match s
{
"any" => Ok(Datatype::Any),
//"any" => Ok(Datatype::Any),
"bool" => Ok(Datatype::Bool),
"int" => Ok(Datatype::Int),
"str" => Ok(Datatype::String),
@ -1034,7 +1363,7 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
}
match word.as_str()
{
"any" => ins.push(Datatype::Any),
//"any" => ins.push(Datatype::Any),
"str" => ins.push(Datatype::String),
"int" => ins.push(Datatype::Int),
"bool" => ins.push(Datatype::Bool),
@ -1069,7 +1398,7 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
{
match word.as_str()
{
"any" => outs.push(Datatype::Any),
//"any" => outs.push(Datatype::Any),
"str" => outs.push(Datatype::String),
"int" => outs.push(Datatype::Int),
"bool" => outs.push(Datatype::Bool),
@ -1087,9 +1416,9 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
if debug
{
println!("outs: {:?}", outs);
}
}
let block = parse_block(&mut tokens_iter, intrinsics, debug)?;
functions.push(Function {name: word.clone(), ins, outs, content: block});
functions.push(Function {name: sanitize_name(word.clone()), ins, outs, content: block});
break;
}
}
@ -1157,7 +1486,7 @@ fn parse_until_delimiter(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, in
}
Token::Apply(name, word, line, col) =>
{
operations.push(Operation::Apply(name.clone(), word.clone(), *line, *col));
operations.push(Operation::Apply(sanitize_name(name.clone()), word.clone(), *line, *col));
}
Token::Keyword(word, line, col) =>
{
@ -1215,6 +1544,10 @@ fn parse_until_delimiter(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, in
{
operations.push(Operation::QueueDiagnostic(*line, *col));
}
else if word == "interrupt"
{
operations.push(Operation::Interrupt(*line, *col));
}
else if Some(word.as_str()) == delimiter
{
return Ok(operations);
@ -1225,7 +1558,7 @@ fn parse_until_delimiter(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, in
}
else
{
operations.push(Operation::FunctionCall(word.clone(), *line, *col));
operations.push(Operation::FunctionCall(sanitize_name(word.clone()), *line, *col));
}
}
}
@ -1329,7 +1662,7 @@ fn tokenize(text: &str) -> Result<Vec<Token>, String>
}
else
{
tokens.push(Token::Apply(application_name.clone(), word.clone(), line, col));
tokens.push(Token::Apply(sanitize_name(application_name.clone()), word.clone(), line, col));
application_name.clear();
}
word.clear();
@ -1374,7 +1707,7 @@ fn tokenize(text: &str) -> Result<Vec<Token>, String>
}
else
{
tokens.push(Token::Apply(application_name.clone(), word.clone(), line, col));
tokens.push(Token::Apply(sanitize_name(application_name.clone()), word.clone(), line, col));
}
}
}

View File

@ -10,7 +10,8 @@ function => dump
true 0
while
{
dup test.read req print
// i
dup test.read req intToStr req print
1 + test.length dup < req
}
deq "" println

33
tests/comparisons.qbl Normal file
View File

@ -0,0 +1,33 @@
//valid,true
//false
//true
//false
//false
//false
//true
//true
//:END:
function bool => str boolToStr
{
if
{
"true"
}
else
{
"false"
}
}
1 0 > boolToStr println
1 0 < boolToStr println
1 0 >= boolToStr println
1 0 <= boolToStr println
1 0 == boolToStr println
"" println
1 1 > boolToStr println
1 1 < boolToStr println
1 1 >= boolToStr println
1 1 <= boolToStr println
1 1 == boolToStr println

View File

@ -1,8 +1,8 @@
//invalid,Function name print at 3:22 is already an intrinsic:END:
function int => print
function str => print
{
deq
}
42 print
"42" print

View File

@ -1,8 +1,8 @@
//invalid,Expected function name but got deq at 3:20:END:
function int => deq
function str => deq
{
deq
}
42 print
"42" print

View File

@ -1,8 +1,8 @@
//invalid,Expected function name but got { at 4:2:END:
function int =>
function str =>
{
deq
}
42 print
"42" print

View File

@ -14,4 +14,4 @@ function int int int int => int fibonacci
}
}
20 0 1 0 fibonacci println
20 0 1 0 fibonacci intToStr println

View File

@ -4,5 +4,5 @@
function int => int req_impl { }
1 2 3 req_impl print print println
1 2 3 req print print println
1 2 3 req_impl intToStr intToStr intToStr print print println
1 2 3 req intToStr intToStr intToStr print print println

View File

@ -5,9 +5,9 @@
// Dequeues, enqueues 42 and 17, prints the head
function any => int foo
function int => int foo
{
deq 42 17 print
deq 42 17 intToStr req print
}
"test2" print false

View File

@ -1,9 +1,9 @@
//valid,42footesttest2stuff
//:END:
function int str any => str str str foo
function int str str => str str str foo
{
print req deq "test" "test2" "stuff" print
intToStr req req print req deq "test" "test2" "stuff" print
}
42 "foo" "bar" foo print print println

View File

@ -7,16 +7,29 @@ true while
}
10 0 dup > req
// true 10
while
{
dup print
// i
dup intToStr req print
1 - 0 dup > req
}
deq
function bool => str boolToStr
{
if
{
"true"
}
else
{
"false"
}
}
true true true while
{
false
}
print println
boolToStr boolToStr print println