Compare commits

..

36 Commits

Author SHA1 Message Date
8f8f8ffe80 Update README.md 2025-04-07 17:50:32 +02:00
6ed7838a53 [Compiler-Rust]: Vec.get(0) -> Vec.first() 2024-01-02 17:22:36 +01:00
6085eb0d6e Add solution to project euler's problem 3 2023-12-23 22:10:07 +01:00
01c062ce8f Add increment function to the standard library 2023-12-23 22:05:32 +01:00
90d67867cc Fix fasm error when enqueueing ints > 2^32
On x64 the only instruction supporting imm64 is mov rXX,
imm64 so there has to be a special case for those numbers
2023-12-23 22:01:06 +01:00
1f78ad0a7f Progress on Project Euler problems 2023-12-22 22:57:55 +01:00
3440226658 Add newline 2023-12-22 22:19:52 +01:00
1beec6190e Add decrement to the standard library 2023-12-22 22:19:52 +01:00
8478b10210 Simplifications curtesy of clippy 2023-12-22 22:19:52 +01:00
dcfb3147ba Add first project euler problem 2023-07-28 07:05:09 +02:00
b1cb4a0a0e Optimize swp and req with two-item queue 2023-07-28 07:04:39 +02:00
92f7ec405d Add queue overflow check 2023-07-28 04:55:08 +02:00
09ac457b9d Fix names clobbering fasm's "type" keyword 2023-07-28 03:10:57 +02:00
abcbe9d68b Add divmod 2023-07-28 03:01:13 +02:00
95133360ee Make imports work on all OSes 2023-02-06 16:03:48 +01:00
5aa365c681 Improve importing 2023-02-06 15:00:37 +01:00
131386632f Fix tests 2023-01-23 06:08:26 +01:00
476aa8bfb2 Implement importing 2023-01-23 05:28:13 +01:00
4286fb4424 Start working on importing 2023-01-23 03:58:53 +01:00
4064309b26 Make oob check write to stderr instead of stdout 2023-01-11 19:45:38 +01:00
296ee6c5c8 Add icons for VSCode 2023-01-11 19:43:03 +01:00
d7c6cada32 remove decrease intrinsic+update editor support 2023-01-11 03:02:10 +01:00
34f52ae864 Add array oob checks + improve test handling 2023-01-11 02:33:30 +01:00
0448b56adf Improve diagnostics 2023-01-09 12:35:48 +01:00
83537737f5 Merge branch 'main' of https://gittea.dev/0x4261756D/kurz 2023-01-09 12:23:43 +01:00
df3e1b027f Time interpretation 2023-01-09 12:23:01 +01:00
2ce863c899 Add asm compilation and running 2023-01-05 00:19:29 +01:00
ffdc54947b Improve queue runaway problems
By changing the order of "pointer" increases it is possible to check and move back the queue if it is empty before function calls and req.
With the function call change in particular it is now possible to run conway_diehard.qbl a queue of length 1024 (down from ~16384)
2023-01-04 00:45:34 +01:00
3d2cd64c6d Implement enough asm generation to compile rule110 2023-01-03 21:52:48 +01:00
870dcb105d Readd queue runaway optimizations in comparisons 2023-01-03 20:55:05 +01:00
a754602bde Readd queue runaway optimization in intToStr 2023-01-03 20:44:07 +01:00
84442e5eb9 Finish enough asm generation to compile tests/while.qbl 2023-01-03 20:39:08 +01:00
5bae80e9aa Assembly generation, WIP 2023-01-03 17:07:57 +01:00
fbccc9dd15 Remove any from syntax highlighting 2022-12-31 16:14:52 +01:00
cdfa5b1310 Fix tests failing on Windows due to EOL shenanigans 2022-12-31 16:12:53 +01:00
41667ff4a3 Remove the "any" type 2022-12-31 16:09:55 +01:00
33 changed files with 1012 additions and 165 deletions

1
.gitignore vendored
View File

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

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2022 0x4261756D Copyright (c) 2022-2023 0x4261756D
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -1,3 +1,5 @@
**Moved to [Codeberg](https://codeberg.org/0x4261756D/kurz)**
# kurz # kurz
Queue based language Queue based language

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

View File

@ -14,7 +14,11 @@
"id": "kurz", "id": "kurz",
"aliases": ["kurz", "kurz"], "aliases": ["kurz", "kurz"],
"extensions": [".qbl"], "extensions": [".qbl"],
"configuration": "./language-configuration.json" "configuration": "./language-configuration.json",
"icon": {
"light": "./icons/kurz-light.png",
"dark": "./icons/kurz-dark.png"
}
}], }],
"grammars": [{ "grammars": [{
"language": "kurz", "language": "kurz",

View File

@ -33,7 +33,7 @@
}, },
{ {
"name": "support.type.kurz", "name": "support.type.kurz",
"match": "\\b(bool|int|str|any)\\b" "match": "\\b(bool|int|str)\\b"
}, },
{ {
"name": "constant.numeric.kurz", "name": "constant.numeric.kurz",
@ -45,7 +45,7 @@
}, },
{ {
"name": "support.function.kurz", "name": "support.function.kurz",
"match": "\\b(deq|swp|dup|req|depth|decrease)\\b" "match": "\\b(deq|swp|dup|req|depth)\\b"
}, },
{ {
"name": "keyword.control", "name": "keyword.control",

View File

@ -0,0 +1,55 @@
//valid,4613732
//:END:
import "../std.qbl"
true 1 2 0 0 0
while
{
// a b even_count 0 sum
fib
// even_count 0 sum b a+b
dup ==
// sum b a+b even_count even?
req req req req
if
// sum b a+b even_count
{
req dup
// b a+b even_count sum b
req req deq 3 +
// b a+b even_count sum+b
req req
}
else
{
req req req
}
// even_count sum+b b a+b
decrement 0 req req
// a+b even_count-1 0 sum+b b
check
// even_count-1 0 sum+b b a+b big?
req req req req swp
// even_count-1 0 sum+b b big? a+b
req req req swp
// a+b even_count-1 0 sum+b big? b
req req req req
// big? b a+b even_count-1 0 sum+b
}
deq deq deq deq intToStr println
function int int => int int fib
{
req dup +
}
function int => int nPrint
{
dup intToStr req print
}
function int => int bool check
{
4000000 dup <
}

View File

@ -0,0 +1,56 @@
//valid,6857
//:END:
import "../std.qbl"
600851475143 2 divHasLargerPF 3
while
{
// n p
req dup 2 req req +
// p n p+2
swp req
// n p p+2
divHasLargerPF
// p+2 hasLPF newN
req
}
deq 2 - intToStr println
function int int => bool int divHasLargerPF
{
req dup req divIfDivisible
// n newN
1 req dup
// newN 1 n newN
== req req
// is1 n newN
if
{
false req deq
}
else
{
true deq req
}
}
function int int => int divIfDivisible
{
// p n
dup req dup swp
// p n p n
divmod 0
// p n p/n p%n 0
req req req ==
// p n p/n isDiv
req req req
if
{
deq req divIfDivisible
}
else
{
req deq deq
}
}

View File

@ -0,0 +1,32 @@
//valid,233168
//:END:
import "../std.qbl"
true 3 0
while
{
// i sum
dup check req req +
incAndCheck req
}
deq intToStr println
function int => int check
{
3 dup % 0 req != req
// not_divisible start
if
{
5 dup % 0 req != req
if
{
deq 0
}
}
}
function int => bool int incAndCheck
{
1 + 1000 dup < req
}

View File

@ -1,10 +1,13 @@
use core::panic; use core::panic;
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use std::fmt::format;
use std::fs; use std::fs;
use std::iter::Peekable; use std::iter::Peekable;
use std::path::PathBuf;
use std::process::Command;
use std::process::Stdio;
use std::process::exit; use std::process::exit;
use std::time::Instant;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
enum Token enum Token
@ -14,6 +17,7 @@ enum Token
BoolLit(String, i32, i32), BoolLit(String, i32, i32),
Keyword(String, i32, i32), Keyword(String, i32, i32),
Apply(String, String, i32, i32), Apply(String, String, i32, i32),
Import(i32, i32),
} }
enum TokenizerState enum TokenizerState
@ -24,25 +28,25 @@ enum TokenizerState
Comment, Comment,
} }
#[derive(Debug,Clone,Copy)] #[derive(Debug,Clone,Copy, PartialEq)]
enum Datatype enum Datatype
{ {
Int, Int,
String, String,
Bool, Bool,
//Pointer, //Pointer,
Any, // Any,
} }
impl PartialEq for Datatype // impl PartialEq for Datatype
{ // {
fn eq(&self, other: &Self) -> bool // fn eq(&self, other: &Self) -> bool
{ // {
core::mem::discriminant(self) == core::mem::discriminant(&Datatype::Any) || // core::mem::discriminant(self) == core::mem::discriminant(&Datatype::Any) ||
core::mem::discriminant(other) == core::mem::discriminant(&Datatype::Any) || // core::mem::discriminant(other) == core::mem::discriminant(&Datatype::Any) ||
core::mem::discriminant(self) == core::mem::discriminant(other) // core::mem::discriminant(self) == core::mem::discriminant(other)
} // }
} // }
#[derive(Debug)] #[derive(Debug)]
struct Function struct Function
@ -78,24 +82,27 @@ enum Operation
Apply(String, String, i32, i32), Apply(String, String, i32, i32),
Depth(i32, i32), Depth(i32, i32),
QueueDiagnostic(i32, i32), QueueDiagnostic(i32, i32),
Interrupt(i32, i32),
} }
fn main() fn main()
{ {
let intrinsics: HashMap<&str, (Vec<Datatype>, Vec<Datatype>)> = HashMap::from( let intrinsics: HashMap<&str, (Vec<Datatype>, Vec<Datatype>)> = HashMap::from(
[ [
("print", (Vec::from([Datatype::Any]), Vec::new())), ("print", (Vec::from([Datatype::String]), Vec::new())),
("println", (Vec::from([Datatype::Any]), 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::Int]))),
("*", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Int]))), ("*", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Int]))),
("divmod", (Vec::from([Datatype::Int, Datatype::Int]), Vec::from([Datatype::Int, 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::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]))), ("&&", (Vec::from([Datatype::Bool, Datatype::Bool]), Vec::from([Datatype::Bool]))),
("decrease", (Vec::from([Datatype::Int]), Vec::from([Datatype::Int]))),
]); ]);
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if args.len() < 2 if args.len() < 2
@ -104,12 +111,14 @@ fn main()
} }
let mut debug = false; let mut debug = false;
let mut interpret = false; let mut interpret = false;
let mut run = false;
for arg in &args[3..] for arg in &args[3..]
{ {
match arg.as_str() match arg.as_str()
{ {
"-d" | "--debug" => debug = true, "-d" | "--debug" => debug = true,
"-i" | "--interpret" => interpret = true, "-i" | "--interpret" => interpret = true,
"-r" | "--run" => run = true,
_ => panic!("Unknown option {}", arg), _ => panic!("Unknown option {}", arg),
} }
} }
@ -121,9 +130,9 @@ fn main()
for f in fs::read_dir(&args[2]).unwrap() for f in fs::read_dir(&args[2]).unwrap()
{ {
let f = f.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()); println!("========NOW TESTING {:?}========", f.path());
match compile(&file_content, &intrinsics, interpret, debug) match compile(&file_content, f.path().to_str().unwrap(), &intrinsics, interpret, run, debug)
{ {
Ok(maybe_msg) => Ok(maybe_msg) =>
{ {
@ -132,7 +141,7 @@ fn main()
{ {
print!("---Output---\n'{}'\n", msg); print!("---Output---\n'{}'\n", msg);
} }
let expected = &format!("//valid,{}:END:", maybe_msg.unwrap_or(String::new()).replace("\n", "\n//")); let expected = &format!("//valid,{}:END:", maybe_msg.unwrap_or(String::new()).replace('\n', "\n//"));
if file_content.starts_with(expected) if file_content.starts_with(expected)
{ {
println!("===PASSED==="); println!("===PASSED===");
@ -151,7 +160,7 @@ fn main()
Err(msg) => Err(msg) =>
{ {
println!("ERROR: {}", msg); println!("ERROR: {}", msg);
if file_content.starts_with(&format!("//invalid,{}:END:", msg.replace("\n", "\n//"))) if file_content.starts_with(&format!("//invalid,{}:END:", msg.replace('\n', "\n//")))
{ {
println!("===PASSED==="); println!("===PASSED===");
count += 1; count += 1;
@ -160,8 +169,9 @@ fn main()
{ {
if let Some(index) = file_content.find(":END:") if let Some(index) = file_content.find(":END:")
{ {
let expected = &format!("//invalid,{}:END:", msg.replace('\n', "\n//"));
let expected_output = file_content[10..index].replace("\n//", "\n"); let expected_output = file_content[10..index].replace("\n//", "\n");
println!("\n\n===FAILED===\nExpected the output to be {}", expected_output); println!("\n===FAILED===\nExpected the output to be\n'{}'\n({})", expected_output, expected);
} }
else else
{ {
@ -180,7 +190,7 @@ fn main()
"-c" | "--compile" => "-c" | "--compile" =>
{ {
let file_content = fs::read_to_string(&args[2]).expect("Could not read the source file"); let file_content = fs::read_to_string(&args[2]).expect("Could not read the source file");
match compile(&file_content, &intrinsics, interpret, debug) match compile(&file_content, &args[2], &intrinsics, interpret, run, debug)
{ {
Ok(maybe_msg) => Ok(maybe_msg) =>
{ {
@ -196,24 +206,28 @@ fn main()
} }
} }
fn compile(file_content: &String, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, interpret: bool, debug: bool) -> Result<Option<String>, String> fn compile(file_content: &str, file_path: &str, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, interpret: bool, run: bool, debug: bool) -> Result<Option<String>, String>
{ {
let mut tokens: Vec<Token> = tokenize(&file_content)?; let mut tokens: Vec<Token> = tokenize(file_content)?;
println!("---Done tokenizing, got {} tokens---", tokens.len()); println!("---Done tokenizing, got {} tokens---", tokens.len());
let functions: Vec<Function> = extract_functions(&mut tokens, &intrinsics, debug)?; let mut functions: Vec<Function> = Vec::new();
extract_functions(&mut tokens, &mut functions, intrinsics, debug)?;
println!("---Done extracting functions, got {} functions and reduced the token count to {}---", functions.len(), tokens.len()); println!("---Done extracting functions, got {} functions and reduced the token count to {}---", functions.len(), tokens.len());
let mut arrays: Vec<Arr> = extract_arrays(&mut tokens, &intrinsics, &functions, debug)?; resolve_imports(&mut tokens, &mut functions, file_path, &mut Vec::from([fs::canonicalize(file_path).unwrap()]), intrinsics, debug)?;
println!("---Done importing files---");
let mut arrays: Vec<Arr> = extract_arrays(&mut tokens, intrinsics, &functions, debug)?;
println!("---Done extracting arrays, got {} arrays and reduced the token count to {}---", arrays.len(), tokens.len()); 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)?; let operations = parse_until_delimiter(&mut tokens.iter().peekable(), intrinsics, None, debug)?;
println!("---Done parsing tokens into {} operations---", operations.len()); println!("---Done parsing tokens into {} operations---", operations.len());
validate_function_calls(&operations, &functions, &arrays, debug)?; validate_function_calls(&operations, &functions, debug)?;
println!("---Done validating function calls---"); println!("---Done validating function calls---");
typecheck(&operations, &functions, &intrinsics, &arrays, debug)?; typecheck(&operations, &functions, intrinsics, &arrays, debug)?;
println!("---Done typechecking---"); println!("---Done typechecking---");
let start = Instant::now();
let output = if interpret let output = if interpret
{ {
println!("---Starting to interpret the program---"); println!("---Starting to interpret the program---");
Some(interpret_program(&operations, &mut Vec::new(), &functions, &mut arrays, &intrinsics, debug)?) Some(interpret_program(&operations, &mut Vec::new(), &functions, &mut arrays, debug)?)
} }
else else
{ {
@ -221,13 +235,113 @@ fn compile(file_content: &String, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec
}; };
if !interpret if !interpret
{ {
match generate_assembly_linux_x64(&operations, &functions, &intrinsics, &arrays, debug) if let Err(err) = generate_assembly_linux_x64(&operations, &functions, &arrays)
{ {
Ok(()) => return Ok(None), return Err(err.to_string());
Err(error) => return Err(error.to_string()), }
let mut fasm_process = match Command::new("fasm").arg("out.asm").spawn()
{
Ok(process) => process,
Err(err) => return Err(format!("Fasm process error: {}", err)),
};
match fasm_process.wait()
{
Ok(status) =>
{
if !status.success()
{
return Err(format!("fasm exited with an error: {}", status));
}
}
Err(err) => return Err(err.to_string()),
} }
} }
return Ok(output); if run
{
let process = match Command::new("./out").stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()
{
Ok(process) => process,
Err(err) => return Err(err.to_string()),
};
return match process.wait_with_output()
{
Ok(output) =>
{
match String::from_utf8(output.stdout)
{
Ok(stdout) =>
{
match String::from_utf8(output.stderr)
{
Ok(stderr) =>
{
let text = format!("{}{}", stdout, stderr);
match output.status.code()
{
Some(0) => Ok(Some(text)),
_ => Err(text),
}
}
Err(err) => Err(err.to_string()),
}
}
Err(err) => Err(err.to_string()),
}
}
Err(err) => Err(err.to_string()),
};
}
println!("---Done after {:?}---", start.elapsed());
Ok(output)
}
fn resolve_imports(tokens: &mut Vec<Token>, functions: &mut Vec<Function>, file_path: &str, visited_paths: &mut Vec<PathBuf>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, debug: bool) -> Result<(), String>
{
let mut tokens_iter = tokens.iter();
let mut new_tokens: Vec<Token> = Vec::new();
while let Some(token) = tokens_iter.next()
{
if let Token::Import(line, col) = token
{
if let Some(Token::StringLit(import_path, _, _)) = tokens_iter.next()
{
println!("Resolving {}", import_path);
let full_import_path = fs::canonicalize(file_path).map_err(|e| e.to_string())?.parent().unwrap().join(import_path);
if visited_paths.contains(&full_import_path)
{
println!("--Already visited {}--", full_import_path.display());
}
else
{
visited_paths.push(full_import_path.clone());
let maybe_file_content = fs::read_to_string(full_import_path);
match maybe_file_content
{
Ok(file_content) =>
{
let mut import_tokens: Vec<Token> = tokenize(&file_content)?;
println!("--Done tokenizing the imported file at {}:{}, got {} tokens--", line, col, tokens.len());
extract_functions(&mut import_tokens, functions, intrinsics, debug)?;
resolve_imports(&mut import_tokens, functions, file_path, visited_paths, intrinsics, debug)?;
println!("--Now totalling {} functions--", functions.len());
}
Err(e) => return Err(e.to_string()),
}
}
}
else
{
return Err(format!("Expected an import location at {}:{}", line, col));
}
}
else
{
new_tokens.push(token.clone());
}
}
tokens.clear();
tokens.extend_from_slice(&new_tokens);
Ok(())
} }
struct AssemblyData struct AssemblyData
@ -244,38 +358,125 @@ fn merge_assemblies(data: &mut AssemblyData, data2: AssemblyData)
data.strings += data2.strings.as_str(); data.strings += data2.strings.as_str();
} }
const ASSEMBLY_LINUX_X64_QUEUE_LENGTH: u32 = 1024; const ASSEMBLY_LINUX_X64_QUEUE_LENGTH: u32 = 8192;
const ASSEMBLY_LINUX_X64_HEADER: &str = "format ELF64 executable 3\n"; 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_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> // r12: head
// r13: tail
// r14: base
// r15: dynamic end
fn generate_assembly_linux_x64(operations: &Vec<Operation>, functions: &Vec<Function>, arrays: &Vec<Arr>) -> Result<(), std::io::Error>
{ {
let mut data = AssemblyData let mut data = AssemblyData
{ {
arrays: format!("segment readable writeable\n\tqueue: rq {}\n", ASSEMBLY_LINUX_X64_QUEUE_LENGTH), 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"), strings: String::from("segment readable\n\tnewline: db 10\n"),
code: String::from("segment executable\n"), code: String::from("segment executable\n"),
}; };
for array in arrays for array in arrays
{ {
data.arrays += format!("\tarr_{}: rq {}\n", array.name, array.length).as_str(); 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, arrays));
data.code += ASSEMBLY_LINUX_X64_EXIT;
for function in functions 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(function.name.as_str(), &function.content, functions, arrays));
} }
merge_assemblies(&mut data, generate_assembly_linux_x64_function("_start", operations, functions, intrinsics, arrays, debug));
data.code += ASSEMBLY_LINUX_X64_EXIT;
return fs::write("out.asm", format!("{}{}{}{}", ASSEMBLY_LINUX_X64_HEADER, data.code, data.arrays, data.strings)); 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";
}
if data.code.contains("exception_array_read_out_of_bounds")
{
data.strings += "\texception_array_oob_msg db \"Attempted array out-of-bounds access\", 10\n";
data.code += "exception_array_read_out_of_bounds:\n";
//TODO: report the passed sizes
data.code += "\tmov rax, 1\n";
data.code += "\tmov rdi, 2\n";
// size
data.code += "\tmov rdx, 37\n";
// data
data.code += "\tmov rsi, exception_array_oob_msg\n";
data.code += "\tsyscall\n";
data.code += "\tmov rax, 60\n";
data.code += "\tmov rdi, -1\n";
data.code += "\tsyscall\n";
}
if data.code.contains("exception_queue_read_out_of_bounds")
{
data.strings += "\texception_queue_oob_msg db \"Queue overflow\", 10\n";
data.code += "exception_queue_read_out_of_bounds:\n";
//TODO: report the passed sizes
data.code += "\tmov rax, 1\n";
data.code += "\tmov rdi, 2\n";
// size
data.code += "\tmov rdx, 37\n";
// data
data.code += "\tmov rsi, exception_queue_oob_msg\n";
data.code += "\tsyscall\n";
data.code += "\tmov rax, 60\n";
data.code += "\tmov rdi, -1\n";
data.code += "\tsyscall\n";
}
fs::write("out.asm", format!("{}{}{}{}", ASSEMBLY_LINUX_X64_HEADER, data.code, data.arrays, data.strings))
} }
// r8: head fn generate_assembly_linux_x64_array_oob_check(length: i64) -> String
// r9: tail {
// r10: base let mut data = String::new();
data += "\t\t;;Array bounds check\n";
data += format!("\tcmp qword rax, {}\n", length).as_str();
data += "\tjge exception_array_read_out_of_bounds\n";
data += "\tcmp qword rax, 0\n";
data += "\tjl exception_array_read_out_of_bounds\n";
data += "\t\t;;Array bounds check over\n";
data.clone()
}
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 fn generate_assembly_linux_x64_queue_oob_check() -> String
{
return "\t\t;;Queue bounds check\n".to_string() +
format!("\tcmp qword r13, {}\n", ASSEMBLY_LINUX_X64_QUEUE_LENGTH).as_str() +
"\tjge exception_queue_read_out_of_bounds\n\t\t;;Queue bounds over\n";
}
fn generate_assembly_linux_x64_block(operations: &Vec<Operation>, functions: &Vec<Function>, arrays: &Vec<Arr>) -> AssemblyData
{ {
let mut data = AssemblyData let mut data = AssemblyData
{ {
@ -290,7 +491,7 @@ fn generate_assembly_linux_x64_block(operations: &Vec<Operation>, functions: &Ve
Operation::Dequeue(line, col) => Operation::Dequeue(line, col) =>
{ {
data.code += format!("\t;;deq {}:{}\n", line, col).as_str(); 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; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
} }
Operation::Enqueue(datatype, value, line, col) => Operation::Enqueue(datatype, value, line, col) =>
@ -298,57 +499,423 @@ fn generate_assembly_linux_x64_block(operations: &Vec<Operation>, functions: &Ve
data.code += format!("\t;;enq {:?} {} {}:{}\n", datatype, value, line, col).as_str(); data.code += format!("\t;;enq {:?} {} {}:{}\n", datatype, value, line, col).as_str();
match datatype match datatype
{ {
Datatype::Int | Datatype::Bool => Datatype::Int =>
{ {
data.code += format!("\tmov qword [queue+r9], {}\n", value).as_str(); if value.parse::<i64>().unwrap() > u32::MAX as i64
{
data.code += format!("\tmov rax, {}\n", value).as_str();
data.code += "\tmov qword [queue+8*r13], rax\n";
}
else
{
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_{}_{}: db {}, {}, {}, {}, {}, {}, {}, {}, \"{}\", 0\n",
line, col,
value.len() % 256,
(value.len() >> 8) % 256,
(value.len() >> 16) % 256,
(value.len() >> 24) % 256,
(value.len() >> 32) % 256,
(value.len() >> 40) % 256,
(value.len() >> 48) % 256,
(value.len() >> 56) % 256,
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";
data.code += generate_assembly_linux_x64_queue_oob_check().as_str();
}
Operation::Requeue(line, col) =>
{
data.code += format!("\t;;req {}:{}\n", line, col).as_str();
data.code += "\tmov rax, r13\n";
data.code += "\tsub rax, r12\n";
data.code += "\tcmp rax, 2\n";
data.code += format!("\tje req_{line}_{col}_special\n").as_str();
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r13\n";
data.code += generate_assembly_linux_x64_queue_oob_check().as_str();
data.code += format!("\tjmp req_{line}_{col}_end\n").as_str();
data.code += format!("req_{line}_{col}_special:\n").as_str();
data.code += "\tmov qword rax, [queue+8*r12]\n";
data.code += "\tmov qword rdi, [queue+8*r12+8]\n";
data.code += "\tmov qword r12, r14\n";
data.code += "\tmov qword r13, r14\n";
data.code += "\tmov qword [queue+8*r12], rdi\n";
data.code += "\tmov qword [queue+8*r12+8], rax\n";
data.code += "\tadd r13, 2\n";
data.code += format!("req_{line}_{col}_end:\n").as_str();
}
Operation::Swap(line, col) =>
{
data.code += format!("\t;;swp {}:{}\n", line, col).as_str();
data.code += "\tmov rax, r13\n";
data.code += "\tsub rax, r12\n";
data.code += "\tcmp rax, 2\n";
data.code += format!("\tje swp_{line}_{col}_special\n").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";
data.code += generate_assembly_linux_x64_queue_oob_check().as_str();
data.code += format!("\tjmp swp_{line}_{col}_end\n").as_str();
data.code += format!("swp_{line}_{col}_special:\n").as_str();
data.code += "\tmov qword rax, [queue+8*r12]\n";
data.code += "\tmov qword rdi, [queue+8*r12+8]\n";
data.code += "\tmov qword r12, r14\n";
data.code += "\tmov qword r13, r14\n";
data.code += "\tmov qword [queue+8*r12], rdi\n";
data.code += "\tmov qword [queue+8*r12+8], rax\n";
data.code += "\tadd r13, 2\n";
data.code += format!("swp_{line}_{col}_end:\n").as_str();
} }
Operation::While(while_operations, line, col) => Operation::While(while_operations, line, col) =>
{ {
data.code += format!("\t;;while {}:{}\n", line, col).as_str(); data.code += format!("\t;;while {}:{}\n", line, col).as_str();
data.code += format!("while_{}_{}:\n", line, col).as_str(); data.code += "\tcmp qword [queue+8*r12], 0\n";
data.code += "\tcmp qword [queue+r8], 0\n";
data.code += format!("\tje while_{}_{}_end\n", line, col).as_str(); 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; data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
merge_assemblies(&mut data, generate_assembly_linux_x64_block(while_operations, functions, intrinsics, arrays, debug)); merge_assemblies(&mut data, generate_assembly_linux_x64_block(while_operations, functions, arrays));
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 += 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; 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, arrays));
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, arrays));
}
data.code += format!("if_{}_{}_end:\n", line, col).as_str();
}
Operation::Dup(line, col) => Operation::Dup(line, col) =>
{ {
data.code += format!("\t;;dup {}:{}\n", line, col).as_str(); data.code += format!("\t;;dup {}:{}\n", line, col).as_str();
data.code += "\tmov qword [queue+r9], [queue+r8]\n"; data.code += "\tmov rax, [queue+8*r12]\n";
data.code += "\tinc r9\n"; data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r13\n";
data.code += generate_assembly_linux_x64_queue_oob_check().as_str();
} }
Operation::Intrinsic(name, line, col) => 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() match name.as_str()
{ {
"print" => "print" =>
{ {
// For now printing numbers directly is unsupported // For now printing numbers directly is unsupported
data.code += "\trax, 1\n"; data.code += "\tmov rax, 1\n";
data.code += "\trdi, 1\n"; data.code += "\tmov rdi, 1\n";
data.code += "\tmov rsi, [queue+r8]\n"; // load address
data.code += "\tmov " 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 += generate_assembly_linux_x64_queue_oob_check().as_str();
}
"-" =>
{
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 += generate_assembly_linux_x64_queue_oob_check().as_str();
}
"+" =>
{
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 += generate_assembly_linux_x64_queue_oob_check().as_str();
}
"*" =>
{
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 += generate_assembly_linux_x64_queue_oob_check().as_str();
}
"divmod" =>
{
data.code += "\tmov qword rax, [queue+8*r12]\n";
data.code += "\tmov qword rdx, 0\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 += "\tidiv rbx\n";
data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r13\n";
data.code += generate_assembly_linux_x64_queue_oob_check().as_str();
data.code += "\tmov [queue+8*r13], rdx\n";
data.code += "\tinc r13\n";
data.code += generate_assembly_linux_x64_queue_oob_check().as_str();
}
">" =>
{
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 += generate_assembly_linux_x64_queue_oob_check().as_str();
}
"<" =>
{
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 += generate_assembly_linux_x64_queue_oob_check().as_str();
}
">=" =>
{
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 += generate_assembly_linux_x64_queue_oob_check().as_str();
}
"<=" =>
{
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 += generate_assembly_linux_x64_queue_oob_check().as_str();
}
"==" =>
{
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 += generate_assembly_linux_x64_queue_oob_check().as_str();
}
"!=" =>
{
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 += "\tcmovne 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 += generate_assembly_linux_x64_queue_oob_check().as_str();
}
"&&" =>
{
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";
data.code += generate_assembly_linux_x64_queue_oob_check().as_str();
} }
_ => todo!("intrinsic {} {}:{}", name, line, col) _ => todo!("intrinsic {} {}:{}", name, line, col)
} }
} }
Operation::Apply(name, word, line, col) =>
{
let array = arrays.iter().find(|x| &x.name == name).unwrap();
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 += generate_assembly_linux_x64_array_oob_check(array.length).as_str();
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";
data.code += generate_assembly_linux_x64_queue_oob_check().as_str();
}
"write" =>
{
data.code += "\tmov rax, [queue+8*r12]\n";
data.code += generate_assembly_linux_x64_array_oob_check(array.length).as_str();
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" =>
{
data.code += format!("\tmov qword [queue+8*r13], {}\n", array.length).as_str();
data.code += "\tinc r13\n";
data.code += generate_assembly_linux_x64_queue_oob_check().as_str();
}
_ => 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 += "\tinc r12\n";
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
data.code += "\tmov [queue+8*r13], rax\n";
data.code += "\tinc r13\n";
data.code += generate_assembly_linux_x64_queue_oob_check().as_str();
}
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) _ => todo!("{:?}", operation)
} }
} }
return data; data
} }
fn generate_assembly_linux_x64_function(name: &str, operations: &Vec<Operation>, functions: &Vec<Function>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, arrays: &Vec<Arr>, debug: bool) -> AssemblyData fn generate_assembly_linux_x64_function(name: &str, operations: &Vec<Operation>, functions: &Vec<Function>, arrays: &Vec<Arr>) -> AssemblyData
{ {
let mut data = AssemblyData let mut data = AssemblyData
{ {
@ -356,11 +923,12 @@ fn generate_assembly_linux_x64_function(name: &str, operations: &Vec<Operation>,
code: format!("{}:\n", name), code: format!("{}:\n", name),
strings: String::new(), strings: String::new(),
}; };
merge_assemblies(&mut data, generate_assembly_linux_x64_block(operations, functions, intrinsics, arrays, debug)); merge_assemblies(&mut data, generate_assembly_linux_x64_block(operations, functions, arrays));
return data; data.code += "\tret\n";
data
} }
fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, functions: &Vec<Function>, arrays: &mut Vec<Arr>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, debug: bool) -> Result<String,String> fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, functions: &Vec<Function>, arrays: &mut Vec<Arr>, debug: bool) -> Result<String,String>
{ {
let mut output = String::new(); let mut output = String::new();
for operation in operations for operation in operations
@ -386,7 +954,7 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
} }
Operation::Dup(_, _) => Operation::Dup(_, _) =>
{ {
let val = queue.get(0).unwrap(); let val = queue.first().unwrap();
queue.push(val.clone()); queue.push(val.clone());
} }
Operation::Swap(_, _) => Operation::Swap(_, _) =>
@ -405,7 +973,7 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
let val = queue.remove(0); let val = queue.remove(0);
function_context.push(val); function_context.push(val);
} }
output += interpret_program(&function.content, function_context, functions, arrays, intrinsics, debug)?.as_str(); output += interpret_program(&function.content, function_context, functions, arrays, debug)?.as_str();
for val in function_context for val in function_context
{ {
queue.push(val.to_string()); queue.push(val.to_string());
@ -416,11 +984,11 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
let val = queue.remove(0); let val = queue.remove(0);
if val == "true" if val == "true"
{ {
output += interpret_program(if_block, queue, functions, arrays, intrinsics, debug)?.as_str(); output += interpret_program(if_block, queue, functions, arrays, debug)?.as_str();
} }
else if let Some(else_block) = maybe_else_block else if let Some(else_block) = maybe_else_block
{ {
output += interpret_program(else_block, queue, functions, arrays, intrinsics, debug)?.as_str(); output += interpret_program(else_block, queue, functions, arrays, debug)?.as_str();
} }
} }
Operation::Intrinsic(intrinsic_name, line, col) => Operation::Intrinsic(intrinsic_name, line, col) =>
@ -429,7 +997,7 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
{ {
"print" => "print" =>
{ {
output += format!("{}", queue.remove(0)).as_str(); output += queue.remove(0).to_string().as_str();
} }
"-" => "-" =>
{ {
@ -449,23 +1017,36 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
let multiplicant2 = queue.remove(0).parse::<i64>().unwrap(); let multiplicant2 = queue.remove(0).parse::<i64>().unwrap();
queue.push((multiplicant1 * multiplicant2).to_string()); queue.push((multiplicant1 * multiplicant2).to_string());
} }
"divmod" =>
{
let dividend = queue.remove(0).parse::<i64>().unwrap();
let divisor = queue.remove(0).parse::<i64>().unwrap();
queue.push((dividend / divisor).to_string());
queue.push((dividend % divisor).to_string());
}
">" => ">" =>
{ {
let first = queue.remove(0).parse::<i64>().unwrap(); let first = queue.remove(0).parse::<i64>().unwrap();
let second = 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());
} }
"<" =>
{
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 first = queue.remove(0).parse::<i64>().unwrap();
let second = 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());
} }
"<" => "<=" =>
{ {
let first = queue.remove(0).parse::<i64>().unwrap(); let first = queue.remove(0).parse::<i64>().unwrap();
let second = 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());
} }
"==" => "==" =>
{ {
@ -485,15 +1066,15 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
let second = queue.remove(0).parse::<bool>().unwrap(); let second = queue.remove(0).parse::<bool>().unwrap();
queue.push((first && second).to_string()); queue.push((first && second).to_string());
} }
"decrease" =>
{
let val = queue.remove(0).parse::<i64>().unwrap();
queue.push((val - 1).to_string());
}
"println" => "println" =>
{ {
output += format!("{}\n", queue.remove(0)).as_str(); 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)); return Err(format!("Unexpected intrinsic '{}' at {}:{}", intrinsic_name, line, col));
@ -510,11 +1091,13 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
let position: i64 = queue.remove(0).parse::<i64>().unwrap(); let position: i64 = queue.remove(0).parse::<i64>().unwrap();
if position >= arr.length if position >= arr.length
{ {
return Err(format!("Attempted an out of bounds write for array {} ({} >= {}) at {}:{}", arr.name, position, arr.length, line, col)); //return Err(format!("Attempted an out of bounds write for array {} ({} >= {}) at {}:{}", arr.name, position, arr.length, line, col));
return Err(String::from("Attempted array out-of-bounds access\n"));
} }
if position < 0 if position < 0
{ {
return Err(format!("Attempted an out of bounds write for array {} ({} < 0) at {}:{}", arr.name, position, line, col)); //return Err(format!("Attempted an out of bounds write for array {} ({} < 0) at {}:{}", arr.name, position, line, col));
return Err(String::from("Attempted array out-of-bounds access\n"));
} }
let data = queue.remove(0); let data = queue.remove(0);
if debug if debug
@ -532,11 +1115,13 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
let position: i64 = queue.remove(0).parse::<i64>().unwrap(); let position: i64 = queue.remove(0).parse::<i64>().unwrap();
if position >= arr.length if position >= arr.length
{ {
return Err(format!("Attempted an out of bounds read for array {} ({} >= {}) at {}:{}", arr.name, position, arr.length, line, col)); //return Err(format!("Attempted an out of bounds read for array {} ({} >= {}) at {}:{}", arr.name, position, arr.length, line, col));
return Err(String::from("Attempted array out-of-bounds access\n"));
} }
if position < 0 if position < 0
{ {
return Err(format!("Attempted an out of bounds read for array {} ({} < 0) at {}:{}", arr.name, position, line, col)); //return Err(format!("Attempted an out of bounds read for array {} ({} < 0) at {}:{}", arr.name, position, line, col));
return Err(String::from("Attempted array out-of-bounds access\n"));
} }
queue.push(arr.data[position as usize].clone()); queue.push(arr.data[position as usize].clone());
} }
@ -556,7 +1141,7 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
{ {
break; break;
} }
output += interpret_program(while_block, queue, functions, arrays, intrinsics, debug)?.as_str(); output += interpret_program(while_block, queue, functions, arrays, debug)?.as_str();
} }
} }
Operation::Depth(_, _) => Operation::Depth(_, _) =>
@ -568,13 +1153,14 @@ fn interpret_program(operations: &Vec<Operation>, queue: &mut Vec<String>, funct
{ {
println!("---Queue state at {}:{}---\nlength: {}\n{:?}\n------------------------------", line, col, queue.len(), queue); println!("---Queue state at {}:{}---\nlength: {}\n{:?}\n------------------------------", line, col, queue.len(), queue);
} }
Operation::Interrupt(_, _) => {}
} }
if debug if debug
{ {
println!("after: {:?}: {:?}, '{}'", operation, queue, output); println!("after: {:?}: {:?}, '{}'", operation, queue, output);
} }
} }
return Ok(output); Ok(output)
} }
fn typecheck(operations: &Vec<Operation>, functions: &Vec<Function>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, arrays: &Vec<Arr>, debug: bool) -> Result<(), String> fn typecheck(operations: &Vec<Operation>, functions: &Vec<Function>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, arrays: &Vec<Arr>, debug: bool) -> Result<(), String>
@ -600,10 +1186,10 @@ fn typecheck(operations: &Vec<Operation>, functions: &Vec<Function>, intrinsics:
{ {
println!("Successfully typechecked main operations"); println!("Successfully typechecked main operations");
} }
return Ok(()); Ok(())
} }
fn typecheck_block(operations: &Vec<Operation>, ins: &Vec<Datatype>, outs: &Vec<Datatype>, functions: &Vec<Function>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, arrays: &Vec<Arr>, debug: bool) -> Result<(), String> fn typecheck_block(operations: &Vec<Operation>, ins: &[Datatype], outs: &Vec<Datatype>, functions: &Vec<Function>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, arrays: &Vec<Arr>, debug: bool) -> Result<(), String>
{ {
let actual_outs = get_return_type(operations, ins, functions, intrinsics, arrays, debug)?; let actual_outs = get_return_type(operations, ins, functions, intrinsics, arrays, debug)?;
if &actual_outs != outs if &actual_outs != outs
@ -614,6 +1200,7 @@ fn typecheck_block(operations: &Vec<Operation>, ins: &Vec<Datatype>, outs: &Vec<
{ {
match operation match operation
{ {
Operation::Interrupt(line, col) |
Operation::Enqueue(_, _, line, col) | Operation::Enqueue(_, _, line, col) |
Operation::Dequeue(line, col) | Operation::Dequeue(line, col) |
Operation::Requeue(line, col) | Operation::Requeue(line, col) |
@ -632,10 +1219,10 @@ fn typecheck_block(operations: &Vec<Operation>, ins: &Vec<Datatype>, outs: &Vec<
}; };
return Err(format!("Wrong queue state at the end of a block, expected {:?} but got {:?} at {}:{}", outs, actual_outs, line, col)); return Err(format!("Wrong queue state at the end of a block, expected {:?} but got {:?} at {}:{}", outs, actual_outs, line, col));
} }
return Ok(()); Ok(())
} }
fn get_return_type(operations: &Vec<Operation>, ins: &Vec<Datatype>, functions: &Vec<Function>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, arrays: &Vec<Arr>, debug: bool) -> Result<Vec<Datatype>, String> fn get_return_type(operations: &Vec<Operation>, ins: &[Datatype], functions: &Vec<Function>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, arrays: &Vec<Arr>, debug: bool) -> Result<Vec<Datatype>, String>
{ {
let type_queue: &mut Vec<Datatype> = &mut Vec::new(); let type_queue: &mut Vec<Datatype> = &mut Vec::new();
type_queue.extend_from_slice(ins); type_queue.extend_from_slice(ins);
@ -662,9 +1249,9 @@ fn get_return_type(operations: &Vec<Operation>, ins: &Vec<Datatype>, functions:
} }
Operation::Dup(line, col) => Operation::Dup(line, col) =>
{ {
if let Some(typ) = type_queue.get(0) if let Some(typ) = type_queue.first()
{ {
type_queue.push(typ.clone()); type_queue.push(*typ);
} }
else else
{ {
@ -727,7 +1314,7 @@ fn get_return_type(operations: &Vec<Operation>, ins: &Vec<Datatype>, functions:
{ {
println!("Starting to typecheck if block"); println!("Starting to typecheck if block");
} }
let if_ret = get_return_type(if_block, &type_queue, functions, intrinsics, arrays, debug)?; let if_ret = get_return_type(if_block, type_queue, functions, intrinsics, arrays, debug)?;
let else_ret = let else_ret =
if let Some(else_block) = maybe_else_block if let Some(else_block) = maybe_else_block
{ {
@ -735,7 +1322,7 @@ fn get_return_type(operations: &Vec<Operation>, ins: &Vec<Datatype>, functions:
{ {
println!("Starting to typecheck else block"); println!("Starting to typecheck else block");
} }
get_return_type(else_block, &type_queue, functions, intrinsics, arrays, debug)? get_return_type(else_block, type_queue, functions, intrinsics, arrays, debug)?
} }
else else
{ {
@ -792,6 +1379,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); println!("---Type queue state at {}:{}---\nlength: {}\n{:?}\n------------------------------", line, col, type_queue.len(), type_queue);
} }
Operation::Interrupt(_, _) => {}
Operation::Apply(name, word, line, col) => Operation::Apply(name, word, line, col) =>
{ {
match word.as_str() match word.as_str()
@ -835,35 +1423,35 @@ fn get_return_type(operations: &Vec<Operation>, ins: &Vec<Datatype>, functions:
println!("{} => {:?}", debug_string, type_queue); println!("{} => {:?}", debug_string, type_queue);
} }
} }
return Ok(type_queue.clone()); Ok(type_queue.clone())
} }
fn validate_function_calls(operations: &Vec<Operation>, functions: &Vec<Function>, arrays: &Vec<Arr>, debug: bool) -> Result<(), String> fn validate_function_calls(operations: &Vec<Operation>, functions: &Vec<Function>, debug: bool) -> Result<(), String>
{ {
for function in functions for function in functions
{ {
validate_function_calls_in_block(&function.content, functions, arrays, debug)?; validate_function_calls_in_block(&function.content, functions)?;
if debug if debug
{ {
println!("Successfully validated function calls in function '{}'", function.name); println!("Successfully validated function calls in function '{}'", function.name);
} }
} }
validate_function_calls_in_block(operations, functions, arrays, debug)?; validate_function_calls_in_block(operations, functions)?;
if debug if debug
{ {
println!("Successfully validated function calls in main operations"); println!("Successfully validated function calls in main operations");
} }
return Ok(()); Ok(())
} }
fn validate_function_calls_in_block(block: &Vec<Operation>, functions: &Vec<Function>, arrays: &Vec<Arr>, debug: bool) -> Result<(), String> fn validate_function_calls_in_block(block: &Vec<Operation>, functions: &Vec<Function>) -> Result<(), String>
{ {
for operation in block for operation in block
{ {
match operation match operation
{ {
Operation::Depth(_, _) | Operation::QueueDiagnostic(_, _) | Operation::Intrinsic(_, _, _) | Operation::Enqueue(_, _, _, _) | Operation::Dequeue(_, _) | 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) => Operation::FunctionCall(function_name, line, col) =>
{ {
if !functions.iter().any(|x| &x.name == function_name) if !functions.iter().any(|x| &x.name == function_name)
@ -873,22 +1461,22 @@ fn validate_function_calls_in_block(block: &Vec<Operation>, functions: &Vec<Func
} }
Operation::If(if_block, maybe_else_block, _, _) => Operation::If(if_block, maybe_else_block, _, _) =>
{ {
validate_function_calls_in_block(if_block, functions, arrays, debug)?; validate_function_calls_in_block(if_block, functions)?;
if let Some(else_block) = maybe_else_block if let Some(else_block) = maybe_else_block
{ {
validate_function_calls_in_block(else_block, functions, arrays, debug)?; validate_function_calls_in_block(else_block, functions)?;
} }
} }
Operation::While(while_block, _, _) => Operation::While(while_block, _, _) =>
{ {
validate_function_calls_in_block(while_block, functions, arrays, debug)?; validate_function_calls_in_block(while_block, functions)?;
} }
} }
} }
return Ok(()); Ok(())
} }
fn extract_arrays(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, functions: &Vec<Function>, debug: bool) -> Result<Vec<Arr>, String> fn extract_arrays(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, functions: &[Function], debug: bool) -> Result<Vec<Arr>, String>
{ {
let mut tokens_iter = tokens.iter().peekable(); let mut tokens_iter = tokens.iter().peekable();
let mut arrays: Vec<Arr> = Vec::new(); let mut arrays: Vec<Arr> = Vec::new();
@ -948,7 +1536,7 @@ fn extract_arrays(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Datat
let mut data: Vec<String> = Vec::new(); let mut data: Vec<String> = Vec::new();
let default_val = match datatype let default_val = match datatype
{ {
Datatype::Any | Datatype::String => String::new(), Datatype::String => String::new(),
Datatype::Bool => String::from("false"), Datatype::Bool => String::from("false"),
Datatype::Int => String::from("0"), Datatype::Int => String::from("0"),
}; };
@ -956,7 +1544,7 @@ fn extract_arrays(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Datat
{ {
data.push(default_val.clone()); 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 else
@ -981,25 +1569,33 @@ fn extract_arrays(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Datat
} }
tokens.clear(); tokens.clear();
tokens.extend_from_slice(&new_tokens); tokens.extend_from_slice(&new_tokens);
return Ok(arrays); Ok(arrays)
}
fn sanitize_name(name: String) -> String
{
if name == "test"
{
return "test_".to_string();
}
name.replace(['-', '+'], "_").replace('%', "percent").replace('/', "slash")
} }
fn str_to_datatype(s: &str, line: i32, col: i32) -> Result<Datatype, String> fn str_to_datatype(s: &str, line: i32, col: i32) -> Result<Datatype, String>
{ {
match s match s
{ {
"any" => Ok(Datatype::Any), //"any" => Ok(Datatype::Any),
"bool" => Ok(Datatype::Bool), "bool" => Ok(Datatype::Bool),
"int" => Ok(Datatype::Int), "int" => Ok(Datatype::Int),
"str" => Ok(Datatype::String), "str" => Ok(Datatype::String),
_ => return Err(format!("Expected a datatype for the array, got {} instead at {}:{}", s, line, col)) _ => Err(format!("Expected a datatype for the array, got {} instead at {}:{}", s, line, col))
} }
} }
fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, debug: bool) -> Result<Vec<Function>, String> fn extract_functions(tokens: &mut Vec<Token>, functions: &mut Vec<Function>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, debug: bool) -> Result<(), String>
{ {
let mut tokens_iter = tokens.iter().peekable(); let mut tokens_iter = tokens.iter().peekable();
let mut functions: Vec<Function> = Vec::new();
let mut new_tokens: Vec<Token> = Vec::new(); let mut new_tokens: Vec<Token> = Vec::new();
while let Some(token) = tokens_iter.next() while let Some(token) = tokens_iter.next()
{ {
@ -1021,7 +1617,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::BoolLit(_, line, col) | Token::IntLit(_, line, col) | Token::StringLit(_, line, col) | Token::BoolLit(_, line, col) | Token::Import(line, col) |
Token::Apply(_, _, line, col) => Token::Apply(_, _, line, col) =>
{ {
return Err(format!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col)); return Err(format!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col));
@ -1034,7 +1630,7 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
} }
match word.as_str() match word.as_str()
{ {
"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), "bool" => ins.push(Datatype::Bool),
@ -1043,7 +1639,7 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
} }
} }
} }
None => return Err(format!("Unexpected end of file while extracting a function")) None => return Err("Unexpected end of file while extracting a function".to_string())
} }
} }
if debug if debug
@ -1060,7 +1656,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::BoolLit(_, line, col) | Token::IntLit(_, line, col) | Token::StringLit(_, line, col) | Token::BoolLit(_, line, col) | Token::Import(line, col) |
Token::Apply(_, _, line, col) => Token::Apply(_, _, line, col) =>
{ {
return Err(format!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col)); return Err(format!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col));
@ -1069,11 +1665,11 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
{ {
match word.as_str() match word.as_str()
{ {
"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),
"bool" => outs.push(Datatype::Bool), "bool" => outs.push(Datatype::Bool),
"{" | "}" | "deq" | "req" | "dup" | "swp" | "true" | "false" | "depth" | "???" => return Err(format!("Expected function name but got {} at {}:{}", word, line, col)), "{" | "}" | "deq" | "req" | "dup" | "swp" | "true" | "false" | "depth" | "???" | "import" => return Err(format!("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)
@ -1087,16 +1683,16 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
if debug if debug
{ {
println!("outs: {:?}", outs); println!("outs: {:?}", outs);
} }
let block = parse_block(&mut tokens_iter, intrinsics, debug)?; 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; break;
} }
} }
} }
} }
} }
None => return Err(format!("Unexpected end of file while extracting a function")) None => return Err("Unexpected end of file while extracting a function".to_string())
} }
} }
} }
@ -1112,7 +1708,7 @@ fn extract_functions(tokens: &mut Vec<Token>, intrinsics: &HashMap<&str, (Vec<Da
} }
tokens.clear(); tokens.clear();
tokens.extend_from_slice(&new_tokens); tokens.extend_from_slice(&new_tokens);
return Ok(functions); Ok(())
} }
fn parse_block(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, debug: bool) -> Result<Vec<Operation>, String> fn parse_block(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, debug: bool) -> Result<Vec<Operation>, String>
@ -1126,9 +1722,9 @@ fn parse_block(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, intrinsics:
} }
else else
{ {
return Err(format!("Expected '{{' to open a block")); return Err("Expected '{' to open a block".to_string());
} }
return parse_until_delimiter(tokens_iter, intrinsics, Some("}"), debug); parse_until_delimiter(tokens_iter, intrinsics, Some("}"), debug)
} }
fn parse_until_delimiter(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, delimiter: Option<&str>, debug: bool) -> Result<Vec<Operation>, String> fn parse_until_delimiter(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, intrinsics: &HashMap<&str, (Vec<Datatype>, Vec<Datatype>)>, delimiter: Option<&str>, debug: bool) -> Result<Vec<Operation>, String>
@ -1157,7 +1753,11 @@ fn parse_until_delimiter(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, in
} }
Token::Apply(name, word, line, col) => 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::Import(line, col) =>
{
return Err(format!("Unexpected import token at {}:{}, should have been resolved before, probably a compiler bug", line, col));
} }
Token::Keyword(word, line, col) => Token::Keyword(word, line, col) =>
{ {
@ -1215,6 +1815,10 @@ fn parse_until_delimiter(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, in
{ {
operations.push(Operation::QueueDiagnostic(*line, *col)); operations.push(Operation::QueueDiagnostic(*line, *col));
} }
else if word == "interrupt"
{
operations.push(Operation::Interrupt(*line, *col));
}
else if Some(word.as_str()) == delimiter else if Some(word.as_str()) == delimiter
{ {
return Ok(operations); return Ok(operations);
@ -1225,7 +1829,7 @@ fn parse_until_delimiter(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, in
} }
else else
{ {
operations.push(Operation::FunctionCall(word.clone(), *line, *col)); operations.push(Operation::FunctionCall(sanitize_name(word.clone()), *line, *col));
} }
} }
} }
@ -1234,7 +1838,7 @@ fn parse_until_delimiter(tokens_iter: &mut Peekable<std::slice::Iter<Token>>, in
{ {
if delimiter.is_some() if delimiter.is_some()
{ {
return Err(format!("Reached the end of the file while parsing a block")); return Err("Reached the end of the file while parsing a block".to_string());
} }
else else
{ {
@ -1314,7 +1918,7 @@ fn tokenize(text: &str) -> Result<Vec<Token>, String>
state = TokenizerState::Whitespace; state = TokenizerState::Whitespace;
if application_name.is_empty() if application_name.is_empty()
{ {
if let Ok(_) = word.parse::<i64>() if word.parse::<i64>().is_ok()
{ {
tokens.push(Token::IntLit(word.clone(), line, col)); tokens.push(Token::IntLit(word.clone(), line, col));
} }
@ -1322,6 +1926,10 @@ fn tokenize(text: &str) -> Result<Vec<Token>, String>
{ {
tokens.push(Token::BoolLit(word.clone(), line, col)); tokens.push(Token::BoolLit(word.clone(), line, col));
} }
else if word == "import"
{
tokens.push(Token::Import(line, col));
}
else else
{ {
tokens.push(Token::Keyword(word.clone(), line, col)); tokens.push(Token::Keyword(word.clone(), line, col));
@ -1329,7 +1937,7 @@ fn tokenize(text: &str) -> Result<Vec<Token>, String>
} }
else 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(); application_name.clear();
} }
word.clear(); word.clear();
@ -1338,7 +1946,7 @@ fn tokenize(text: &str) -> Result<Vec<Token>, String>
{ {
match ch match ch
{ {
'"' => return Err(format!("Having '\"' in the middle of a word is not allowed")), '"' => return Err("Having '\"' in the middle of a word is not allowed".to_string()),
'.' => '.' =>
{ {
application_name = word.clone(); application_name = word.clone();
@ -1363,7 +1971,7 @@ fn tokenize(text: &str) -> Result<Vec<Token>, String>
{ {
TokenizerState::Quote => TokenizerState::Quote =>
{ {
return Err(format!("Encountered EOF before closing string")); return Err("Encountered EOF before closing string".to_string());
} }
TokenizerState::Whitespace | TokenizerState::Comment => {}, TokenizerState::Whitespace | TokenizerState::Comment => {},
TokenizerState::Keyword => TokenizerState::Keyword =>
@ -1374,7 +1982,7 @@ fn tokenize(text: &str) -> Result<Vec<Token>, String>
} }
else 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));
} }
} }
} }

30
std.qbl Normal file
View File

@ -0,0 +1,30 @@
function bool => str boolToStr
{
if
{
"true"
}
else
{
"false"
}
}
function int int => int %
{
divmod deq
}
function int int => int /
{
divmod req deq
}
function int => int decrement
{
1 -
}
function int => int increment
{
1 +
}

View File

@ -1,4 +1,5 @@
//invalid,Attempted an out of bounds read for array test (5 >= 4) at 5:12:END: //invalid,Attempted array out-of-bounds access
//:END:
arr test { bool 4 } arr test { bool 4 }

View File

@ -1,4 +1,5 @@
//invalid,Attempted an out of bounds write for array test (5 >= 4) at 5:19:END: //invalid,Attempted array out-of-bounds access
//:END:
arr test { bool 4 } arr test { bool 4 }

View File

@ -1,4 +1,5 @@
//invalid,Attempted an out of bounds read for array test (-1 < 0) at 5:13:END: //invalid,Attempted array out-of-bounds access
//:END:
arr test { bool 4 } arr test { bool 4 }

View File

@ -1,4 +1,5 @@
//invalid,Attempted an out of bounds write for array test (-1 < 0) at 5:19:END: //invalid,Attempted array out-of-bounds access
//:END:
arr test { bool 4 } arr test { bool 4 }

View File

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

7
tests/basic_import.qbl Normal file
View File

@ -0,0 +1,7 @@
//valid,true
//:END:
import "../std.qbl"
true boolToStr println

26
tests/comparisons.qbl Normal file
View File

@ -0,0 +1,26 @@
//valid,true
//false
//true
//false
//false
//
//false
//false
//true
//true
//true
//:END:
import "../std.qbl"
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

@ -371,4 +371,4 @@ function int int => int coordToIndex
{ {
// y x // y x
req 29 req * + req 29 req * +
} }

View File

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

View File

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

View File

@ -1,8 +1,8 @@
//invalid,Expected function name but got { at 4:2:END: //invalid,Expected function name but got { at 4:2:END:
function int => function str =>
{ {
deq 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

@ -0,0 +1,8 @@
//valid,:END:
import "recursive_import_2.qbl"
import "recursive_import.qbl"
function => test
{
"test" println
}

View File

@ -0,0 +1,5 @@
//valid,test
//:END:
import "recursive_import.qbl"
test

View File

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

View File

@ -1,13 +1,13 @@
//valid,Hello, World! //valid,Hello, World!
//4242test2Falsetesttesttest:END: //4242test2Falsetesttesttest:END:
"Hello, World!\n" print 43 foo foo deq "Hello, World!" println 43 foo foo deq
// Dequeues, enqueues 42 and 17, prints the head // 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 "test2" print false
@ -39,4 +39,4 @@ function int => whileFunction
deq deq
} }
3 whileFunction 3 whileFunction

View File

@ -0,0 +1,5 @@
//valid,false
//:END:
import "basic_import.qbl"
false boolToStr println

View File

@ -1,9 +1,9 @@
//valid,42footesttest2stuff //valid,42footesttest2stuff
//:END: //: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 42 "foo" "bar" foo print print println

View File

@ -1,16 +1,19 @@
//valid,10987654321falsefalse //valid,10987654321falsefalse
//:END: //:END:
import "../std.qbl"
true while true while
{ {
false false
} }
10 0 dup > req 10 0 dup > req
// true 10
while while
{ {
dup print // i
dup intToStr req print
1 - 0 dup > req 1 - 0 dup > req
} }
deq deq
@ -19,4 +22,4 @@ true true true while
{ {
false false
} }
print println boolToStr boolToStr print println