Compare commits
60 Commits
1e8322cc7c
...
main
Author | SHA1 | Date | |
---|---|---|---|
8f8f8ffe80 | |||
6ed7838a53 | |||
6085eb0d6e | |||
01c062ce8f | |||
90d67867cc | |||
1f78ad0a7f | |||
3440226658 | |||
1beec6190e | |||
8478b10210 | |||
dcfb3147ba | |||
b1cb4a0a0e | |||
92f7ec405d | |||
09ac457b9d | |||
abcbe9d68b | |||
95133360ee | |||
5aa365c681 | |||
131386632f | |||
476aa8bfb2 | |||
4286fb4424 | |||
4064309b26 | |||
296ee6c5c8 | |||
d7c6cada32 | |||
34f52ae864 | |||
0448b56adf | |||
83537737f5 | |||
df3e1b027f | |||
2ce863c899 | |||
ffdc54947b | |||
3d2cd64c6d | |||
870dcb105d | |||
a754602bde | |||
84442e5eb9 | |||
5bae80e9aa | |||
fbccc9dd15 | |||
cdfa5b1310 | |||
41667ff4a3 | |||
e3a1223ac9 | |||
97bd50fa55 | |||
fb37c4f58b | |||
6d31d5da98 | |||
23c6ec8dad | |||
5682ee708c | |||
53cfd52dcd | |||
8397d32756 | |||
bf4a08e484 | |||
0abc74f043 | |||
dedb601a9b | |||
e4a7bcccc0 | |||
2255aaff8b | |||
f60441ef9d | |||
35f65101ad | |||
0de0838284 | |||
d1883ff3ab | |||
2a560cfcef | |||
c2800dfcc7 | |||
89a7780d10 | |||
48cb618d5a | |||
abfb3d2f3d | |||
d9e7f18049 | |||
ac6a835a64 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
/target
|
||||
out*
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
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:
|
||||
|
||||
|
10
README.md
10
README.md
@ -1,3 +1,11 @@
|
||||
**Moved to [Codeberg](https://codeberg.org/0x4261756D/kurz)**
|
||||
|
||||
# kurz
|
||||
|
||||
Queue based language
|
||||
Queue based language
|
||||
|
||||
## Roadmap
|
||||
- [x] turing completeness
|
||||
- [ ] compiled
|
||||
- [ ] cross-plattform
|
||||
- [ ] self-hosted
|
||||
|
29
editor/micro/kurz.yaml
Normal file
29
editor/micro/kurz.yaml
Normal file
@ -0,0 +1,29 @@
|
||||
filetype: kurz
|
||||
|
||||
detect:
|
||||
filename: "\\.qbl$"
|
||||
|
||||
rules:
|
||||
- comment:
|
||||
start: "//"
|
||||
end: "$"
|
||||
rules:
|
||||
- todo: "(TODO|FIXME):?"
|
||||
- constant.string:
|
||||
start: "\""
|
||||
end: "\""
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "\\\\."
|
||||
- constant.bool: "\\b(true|false)\\b"
|
||||
# ints
|
||||
- constant.number: "\\b([0-9]*)\\b"
|
||||
- symbol.brackets: "[{}]"
|
||||
# function
|
||||
- identifier: "function"
|
||||
- type: "\\b(bool|int|str)\\b"
|
||||
- symbol.operator: "([-+<>]|==|!=|=>|print(ln)?)"
|
||||
- identifier: "\\b(if|else|while)\\b"
|
||||
- special: "\\b(deq|swp|dup|req|depth)\\b"
|
||||
- special: "\\?\\?\\?"
|
||||
- statement: "arr"
|
17
editor/vscode/kurz-lang/.vscode/launch.json
vendored
Normal file
17
editor/vscode/kurz-lang/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// A launch configuration that launches the extension inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
4
editor/vscode/kurz-lang/.vscodeignore
Normal file
4
editor/vscode/kurz-lang/.vscodeignore
Normal file
@ -0,0 +1,4 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
.gitignore
|
||||
vsc-extension-quickstart.md
|
9
editor/vscode/kurz-lang/CHANGELOG.md
Normal file
9
editor/vscode/kurz-lang/CHANGELOG.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to the "kurz-lang" extension will be documented in this file.
|
||||
|
||||
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Initial release
|
5
editor/vscode/kurz-lang/README.md
Normal file
5
editor/vscode/kurz-lang/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# kurz-lang README
|
||||
|
||||
This is a syntax highlighting extension for the lanuage '[kurz](https://gittea.dev/0x4261756D/kurz)'.
|
||||
|
||||
**Enjoy!**
|
BIN
editor/vscode/kurz-lang/icons/kurz-dark.png
Normal file
BIN
editor/vscode/kurz-lang/icons/kurz-dark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 612 B |
BIN
editor/vscode/kurz-lang/icons/kurz-light.png
Normal file
BIN
editor/vscode/kurz-lang/icons/kurz-light.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 612 B |
BIN
editor/vscode/kurz-lang/kurz-lang-0.0.1.vsix
Normal file
BIN
editor/vscode/kurz-lang/kurz-lang-0.0.1.vsix
Normal file
Binary file not shown.
20
editor/vscode/kurz-lang/language-configuration.json
Normal file
20
editor/vscode/kurz-lang/language-configuration.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"comments": {
|
||||
// symbol used for single line comment. Remove this entry if your language does not support line comments
|
||||
"lineComment": "//",
|
||||
},
|
||||
// symbols used as brackets
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
],
|
||||
// symbols that are auto closed when typing
|
||||
"autoClosingPairs": [
|
||||
["{", "}"],
|
||||
["\"", "\""],
|
||||
],
|
||||
// symbols that can be used to surround a selection
|
||||
"surroundingPairs": [
|
||||
["{", "}"],
|
||||
["\"", "\""],
|
||||
]
|
||||
}
|
29
editor/vscode/kurz-lang/package.json
Normal file
29
editor/vscode/kurz-lang/package.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "kurz-lang",
|
||||
"displayName": "kurz",
|
||||
"description": "Syntax highlighting for the kurz language",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"vscode": "^1.74.0"
|
||||
},
|
||||
"categories": [
|
||||
"Programming Languages"
|
||||
],
|
||||
"contributes": {
|
||||
"languages": [{
|
||||
"id": "kurz",
|
||||
"aliases": ["kurz", "kurz"],
|
||||
"extensions": [".qbl"],
|
||||
"configuration": "./language-configuration.json",
|
||||
"icon": {
|
||||
"light": "./icons/kurz-light.png",
|
||||
"dark": "./icons/kurz-dark.png"
|
||||
}
|
||||
}],
|
||||
"grammars": [{
|
||||
"language": "kurz",
|
||||
"scopeName": "source.kurz",
|
||||
"path": "./syntaxes/kurz.tmLanguage.json"
|
||||
}]
|
||||
}
|
||||
}
|
89
editor/vscode/kurz-lang/syntaxes/kurz.tmLanguage.json
Normal file
89
editor/vscode/kurz-lang/syntaxes/kurz.tmLanguage.json
Normal file
@ -0,0 +1,89 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
|
||||
"name": "kurz",
|
||||
"patterns":
|
||||
[
|
||||
{
|
||||
"include": "#keywords"
|
||||
},
|
||||
{
|
||||
"include": "#strings"
|
||||
},
|
||||
{
|
||||
"include": "#comment"
|
||||
}
|
||||
],
|
||||
"repository":
|
||||
{
|
||||
"keywords":
|
||||
{
|
||||
"patterns":
|
||||
[
|
||||
{
|
||||
"name": "keyword.control.kurz",
|
||||
"match": "\\b(if|else|while)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.operator",
|
||||
"match": "[-+<>]|==|!=|=>|print(ln)?"
|
||||
},
|
||||
{
|
||||
"name": "support.function.kurz",
|
||||
"match": "function"
|
||||
},
|
||||
{
|
||||
"name": "support.type.kurz",
|
||||
"match": "\\b(bool|int|str)\\b"
|
||||
},
|
||||
{
|
||||
"name": "constant.numeric.kurz",
|
||||
"match": "[0-9]"
|
||||
},
|
||||
{
|
||||
"name": "constant.language.kurz",
|
||||
"match": "true|false"
|
||||
},
|
||||
{
|
||||
"name": "support.function.kurz",
|
||||
"match": "\\b(deq|swp|dup|req|depth)\\b"
|
||||
},
|
||||
{
|
||||
"name": "keyword.control",
|
||||
"match": "arr"
|
||||
}
|
||||
]
|
||||
},
|
||||
"comment":
|
||||
{
|
||||
"patterns":
|
||||
[
|
||||
{
|
||||
"name": "comment.line.kurz",
|
||||
"begin": "//",
|
||||
"end": "$",
|
||||
"patterns":
|
||||
[
|
||||
{
|
||||
"name": "markup.bold.kurz",
|
||||
"match": "(TODO|FIXME):?"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"strings":
|
||||
{
|
||||
"name": "string.quoted.double.kurz",
|
||||
"begin": "\"",
|
||||
"end": "\"",
|
||||
"patterns":
|
||||
[
|
||||
{
|
||||
"name": "constant.character.escape.kurz",
|
||||
"match": "\\\\."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"scopeName": "source.kurz"
|
||||
}
|
29
editor/vscode/kurz-lang/vsc-extension-quickstart.md
Normal file
29
editor/vscode/kurz-lang/vsc-extension-quickstart.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Welcome to your VS Code Extension
|
||||
|
||||
## What's in the folder
|
||||
|
||||
* This folder contains all of the files necessary for your extension.
|
||||
* `package.json` - this is the manifest file in which you declare your language support and define the location of the grammar file that has been copied into your extension.
|
||||
* `syntaxes/kurz.tmLanguage.json` - this is the Text mate grammar file that is used for tokenization.
|
||||
* `language-configuration.json` - this is the language configuration, defining the tokens that are used for comments and brackets.
|
||||
|
||||
## Get up and running straight away
|
||||
|
||||
* Make sure the language configuration settings in `language-configuration.json` are accurate.
|
||||
* Press `F5` to open a new window with your extension loaded.
|
||||
* Create a new file with a file name suffix matching your language.
|
||||
* Verify that syntax highlighting works and that the language configuration settings are working.
|
||||
|
||||
## Make changes
|
||||
|
||||
* You can relaunch the extension from the debug toolbar after making changes to the files listed above.
|
||||
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
|
||||
|
||||
## Add more language features
|
||||
|
||||
* To add features such as IntelliSense, hovers and validators check out the VS Code extenders documentation at https://code.visualstudio.com/docs
|
||||
|
||||
## Install your extension
|
||||
|
||||
* To start using your extension with Visual Studio Code copy it into the `<user home>/.vscode/extensions` folder and restart Code.
|
||||
* To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension.
|
55
project_euler/problem2.qbl
Normal file
55
project_euler/problem2.qbl
Normal 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 <
|
||||
}
|
56
project_euler/problem3.qbl
Normal file
56
project_euler/problem3.qbl
Normal 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
|
||||
}
|
||||
}
|
||||
|
32
project_euler/problem_1.qbl
Normal file
32
project_euler/problem_1.qbl
Normal 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
|
||||
}
|
1904
src/main.rs
1904
src/main.rs
@ -1,15 +1,25 @@
|
||||
use core::panic;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::iter::Peekable;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::process::Stdio;
|
||||
use std::process::exit;
|
||||
use std::time::Instant;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum Token
|
||||
{
|
||||
StringLit(String, i32, i32),
|
||||
IntLit(i64, i32, i32),
|
||||
IntLit(String, i32, i32),
|
||||
BoolLit(String, i32, i32),
|
||||
Keyword(String, i32, i32),
|
||||
Apply(String, String, i32, i32),
|
||||
Import(i32, i32),
|
||||
}
|
||||
|
||||
enum TokenizerState
|
||||
{
|
||||
Whitespace,
|
||||
@ -18,180 +28,1825 @@ enum TokenizerState
|
||||
Comment,
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
#[derive(Debug,Clone,Copy, PartialEq)]
|
||||
enum Datatype
|
||||
{
|
||||
Int,
|
||||
String,
|
||||
Pointer,
|
||||
Any,
|
||||
Bool,
|
||||
//Pointer,
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Function
|
||||
{
|
||||
name: String,
|
||||
ins: Vec<Datatype>,
|
||||
outs: Vec<Datatype>,
|
||||
content: Vec<Token>
|
||||
content: Vec<Operation>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Arr
|
||||
{
|
||||
name: String,
|
||||
datatype: Datatype,
|
||||
length: i64,
|
||||
data: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Operation
|
||||
{
|
||||
Enqueue(Datatype, String, i32, i32),
|
||||
Dequeue(i32, i32),
|
||||
// TODO: req can be implemented in terms of dup and dequeue
|
||||
Requeue(i32, i32),
|
||||
Swap(i32, i32),
|
||||
Dup(i32, i32),
|
||||
Intrinsic(String, i32, i32),
|
||||
FunctionCall(String, i32, i32),
|
||||
If(Vec<Operation>, Option<Vec<Operation>>, i32, i32),
|
||||
While(Vec<Operation>, i32, i32),
|
||||
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::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]))),
|
||||
("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::Bool, Datatype::Bool]), Vec::from([Datatype::Bool]))),
|
||||
]);
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 2
|
||||
{
|
||||
usage()
|
||||
}
|
||||
let mut debug = false;
|
||||
let mut interpret = false;
|
||||
let mut run = false;
|
||||
for arg in &args[3..]
|
||||
{
|
||||
match arg.as_str()
|
||||
{
|
||||
"-d" | "--debug" => debug = true,
|
||||
"-i" | "--interpret" => interpret = true,
|
||||
"-r" | "--run" => run = true,
|
||||
_ => panic!("Unknown option {}", arg),
|
||||
}
|
||||
}
|
||||
match args[1].as_str()
|
||||
{
|
||||
"-t" | "--test" =>
|
||||
{
|
||||
let mut count = 0;
|
||||
for f in fs::read_dir(&args[2]).unwrap()
|
||||
{
|
||||
let f = f.unwrap();
|
||||
let file_content = fs::read_to_string(f.path()).unwrap().replace("\r\n", "\n");
|
||||
println!("========NOW TESTING {:?}========", f.path());
|
||||
match compile(&file_content, f.path().to_str().unwrap(), &intrinsics, interpret, run, debug)
|
||||
{
|
||||
Ok(maybe_msg) =>
|
||||
{
|
||||
println!("---Successfully parsed {:?}---", f.path());
|
||||
if let Some(msg) = &maybe_msg
|
||||
{
|
||||
print!("---Output---\n'{}'\n", msg);
|
||||
}
|
||||
let expected = &format!("//valid,{}:END:", maybe_msg.unwrap_or(String::new()).replace('\n', "\n//"));
|
||||
if file_content.starts_with(expected)
|
||||
{
|
||||
println!("===PASSED===");
|
||||
count += 1;
|
||||
}
|
||||
else if let Some(index) = file_content.find(":END:")
|
||||
{
|
||||
let expected_output = file_content[8..index].replace("\n//", "\n");
|
||||
println!("\n===FAILED===\nExpected the output to be\n'{}'\n({})", expected_output, expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
panic!("Could not find an ending marker (:END:) for the expected output in {:?}", f.file_name());
|
||||
}
|
||||
}
|
||||
Err(msg) =>
|
||||
{
|
||||
println!("ERROR: {}", msg);
|
||||
if file_content.starts_with(&format!("//invalid,{}:END:", msg.replace('\n', "\n//")))
|
||||
{
|
||||
println!("===PASSED===");
|
||||
count += 1;
|
||||
}
|
||||
else if file_content.starts_with("//invalid,")
|
||||
{
|
||||
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");
|
||||
println!("\n===FAILED===\nExpected the output to be\n'{}'\n({})", expected_output, expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
panic!("Could not find an ending marker (:END:) for the expected output in {:?}", f.file_name());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
println!("Unexpected error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("\n\n=========RESULT=========\n{}/{}", count, fs::read_dir(&args[2]).unwrap().count());
|
||||
}
|
||||
"-c" | "--compile" =>
|
||||
{
|
||||
let file_content = fs::read_to_string(&args[2]).expect("Could not read the source file");
|
||||
let mut tokens: Vec<Token> = tokenize(&file_content);
|
||||
println!("{:?}", tokens);
|
||||
let functions: Vec<Function> = extract_functions(&mut tokens);
|
||||
println!("{:?}", tokens);
|
||||
println!("{:?}", functions);
|
||||
match compile(&file_content, &args[2], &intrinsics, interpret, run, debug)
|
||||
{
|
||||
Ok(maybe_msg) =>
|
||||
{
|
||||
if let Some(msg) = maybe_msg
|
||||
{
|
||||
print!("---Output---\n\n{}", msg);
|
||||
}
|
||||
}
|
||||
Err(msg) => println!("ERROR: {}", msg),
|
||||
}
|
||||
}
|
||||
_ => panic!("Unknown option {}", args[1])
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_functions(tokens: &mut Vec<Token>) -> Vec<Function>
|
||||
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)?;
|
||||
println!("---Done tokenizing, got {} tokens---", tokens.len());
|
||||
let mut functions: Vec<Function> = Vec::new();
|
||||
let mut state = FunctionExtractionState::Outside;
|
||||
let mut ins: Vec<Datatype> = Vec::new();
|
||||
let mut outs: Vec<Datatype> = Vec::new();
|
||||
let mut function_name = String::from("");
|
||||
let mut content: Vec<Token> = Vec::new();
|
||||
let mut indices_to_remove: Vec<usize> = Vec::new();
|
||||
for (i, token) in tokens.iter().enumerate()
|
||||
extract_functions(&mut tokens, &mut functions, intrinsics, debug)?;
|
||||
println!("---Done extracting functions, got {} functions and reduced the token count to {}---", functions.len(), tokens.len());
|
||||
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());
|
||||
let operations = parse_until_delimiter(&mut tokens.iter().peekable(), intrinsics, None, debug)?;
|
||||
println!("---Done parsing tokens into {} operations---", operations.len());
|
||||
validate_function_calls(&operations, &functions, debug)?;
|
||||
println!("---Done validating function calls---");
|
||||
typecheck(&operations, &functions, intrinsics, &arrays, debug)?;
|
||||
println!("---Done typechecking---");
|
||||
let start = Instant::now();
|
||||
let output = if interpret
|
||||
{
|
||||
match state
|
||||
println!("---Starting to interpret the program---");
|
||||
Some(interpret_program(&operations, &mut Vec::new(), &functions, &mut arrays, debug)?)
|
||||
}
|
||||
else
|
||||
{
|
||||
None
|
||||
};
|
||||
if !interpret
|
||||
{
|
||||
if let Err(err) = generate_assembly_linux_x64(&operations, &functions, &arrays)
|
||||
{
|
||||
FunctionExtractionState::Outside =>
|
||||
return Err(err.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 let Token::Keyword(name, _, _) = token
|
||||
if !status.success()
|
||||
{
|
||||
if name == &String::from("function")
|
||||
return Err(format!("fasm exited with an error: {}", status));
|
||||
}
|
||||
}
|
||||
Err(err) => return Err(err.to_string()),
|
||||
}
|
||||
}
|
||||
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) =>
|
||||
{
|
||||
state = FunctionExtractionState::Ins;
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
FunctionExtractionState::Ins =>
|
||||
else
|
||||
{
|
||||
match token
|
||||
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
|
||||
{
|
||||
strings: String,
|
||||
code: String,
|
||||
arrays: String,
|
||||
}
|
||||
|
||||
fn merge_assemblies(data: &mut AssemblyData, data2: AssemblyData)
|
||||
{
|
||||
data.arrays += data2.arrays.as_str();
|
||||
data.code += data2.code.as_str();
|
||||
data.strings += data2.strings.as_str();
|
||||
}
|
||||
|
||||
const ASSEMBLY_LINUX_X64_QUEUE_LENGTH: u32 = 8192;
|
||||
const ASSEMBLY_LINUX_X64_HEADER: &str = "format ELF64 executable 3\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;
|
||||
|
||||
// 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
|
||||
{
|
||||
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, arrays));
|
||||
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, arrays));
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
fn generate_assembly_linux_x64_array_oob_check(length: i64) -> String
|
||||
{
|
||||
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_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
|
||||
{
|
||||
arrays: String::new(),
|
||||
code: String::new(),
|
||||
strings: String::new(),
|
||||
};
|
||||
for operation in operations
|
||||
{
|
||||
match operation
|
||||
{
|
||||
Operation::Dequeue(line, col) =>
|
||||
{
|
||||
data.code += format!("\t;;deq {}:{}\n", line, col).as_str();
|
||||
data.code += "\tinc r12\n";
|
||||
data.code += ASSEMBLY_LINUX_X64_TRY_RESET_QUEUE;
|
||||
}
|
||||
Operation::Enqueue(datatype, value, line, col) =>
|
||||
{
|
||||
data.code += format!("\t;;enq {:?} {} {}:{}\n", datatype, value, line, col).as_str();
|
||||
match datatype
|
||||
{
|
||||
Token::Keyword(name, line, col) =>
|
||||
Datatype::Int =>
|
||||
{
|
||||
match name.as_str()
|
||||
if value.parse::<i64>().unwrap() > u32::MAX as i64
|
||||
{
|
||||
"int" => ins.push(Datatype::Int),
|
||||
"str" => ins.push(Datatype::String),
|
||||
"ptr" => ins.push(Datatype::Pointer),
|
||||
"any" => ins.push(Datatype::Any),
|
||||
"=>" => state = FunctionExtractionState::Outs,
|
||||
_ => panic!("Unknown datatype '{}' at {}:{}", name, line, col)
|
||||
data.code += format!("\tmov rax, {}\n", value).as_str();
|
||||
data.code += "\tmov qword [queue+8*r13], rax\n";
|
||||
}
|
||||
},
|
||||
Token::StringLit(_, line, col) | Token::IntLit(_, line, col) => panic!("Expected datatype for function declaration at {}:{}", line, col),
|
||||
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";
|
||||
}
|
||||
}
|
||||
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) =>
|
||||
{
|
||||
data.code += format!("\t;;while {}:{}\n", line, col).as_str();
|
||||
data.code += "\tcmp qword [queue+8*r12], 0\n";
|
||||
data.code += format!("\tje while_{}_{}_end\n", line, col).as_str();
|
||||
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, arrays));
|
||||
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 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, 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) =>
|
||||
{
|
||||
data.code += format!("\t;;dup {}:{}\n", line, col).as_str();
|
||||
data.code += "\tmov rax, [queue+8*r12]\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) =>
|
||||
{
|
||||
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 += "\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 += 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)
|
||||
}
|
||||
}
|
||||
FunctionExtractionState::Outs =>
|
||||
Operation::Apply(name, word, line, col) =>
|
||||
{
|
||||
match token
|
||||
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()
|
||||
{
|
||||
Token::Keyword(name, _, _) =>
|
||||
"read" =>
|
||||
{
|
||||
match name.as_str()
|
||||
{
|
||||
"int" => outs.push(Datatype::Int),
|
||||
"str" => outs.push(Datatype::String),
|
||||
"ptr" => outs.push(Datatype::Pointer),
|
||||
"any" => outs.push(Datatype::Any),
|
||||
_ =>
|
||||
{
|
||||
if let Token::Keyword(name, _, _) = token
|
||||
{
|
||||
if functions.iter().any(|x| &x.name == name)
|
||||
{
|
||||
panic!("A function with name {} already exists", name);
|
||||
}
|
||||
function_name = name.clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
panic!("Expected a function name") // TODO: Add location
|
||||
}
|
||||
state =FunctionExtractionState::OpenCurly;
|
||||
}
|
||||
}
|
||||
},
|
||||
Token::StringLit(_, line, col) | Token::IntLit(_, line, col) => panic!("Expected datatype for function declaration at {}:{}", line, col),
|
||||
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)
|
||||
}
|
||||
}
|
||||
FunctionExtractionState::OpenCurly =>
|
||||
Operation::FunctionCall(name, line, col) =>
|
||||
{
|
||||
if let Token::Keyword(name, line, col) = token
|
||||
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()
|
||||
{
|
||||
if name == "{"
|
||||
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)
|
||||
}
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
fn generate_assembly_linux_x64_function(name: &str, operations: &Vec<Operation>, functions: &Vec<Function>, arrays: &Vec<Arr>) -> AssemblyData
|
||||
{
|
||||
let mut data = AssemblyData
|
||||
{
|
||||
arrays: String::new(),
|
||||
code: format!("{}:\n", name),
|
||||
strings: String::new(),
|
||||
};
|
||||
merge_assemblies(&mut data, generate_assembly_linux_x64_block(operations, functions, arrays));
|
||||
data.code += "\tret\n";
|
||||
data
|
||||
}
|
||||
|
||||
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();
|
||||
for operation in operations
|
||||
{
|
||||
if debug
|
||||
{
|
||||
println!("before: {:?}: {:?}, '{}'", operation, queue, output);
|
||||
}
|
||||
match operation
|
||||
{
|
||||
Operation::Dequeue(_, _) =>
|
||||
{
|
||||
queue.remove(0);
|
||||
}
|
||||
Operation::Enqueue(_, value, _, _) =>
|
||||
{
|
||||
queue.push(value.clone());
|
||||
}
|
||||
Operation::Requeue(_, _) =>
|
||||
{
|
||||
let val = queue.remove(0);
|
||||
queue.push(val);
|
||||
}
|
||||
Operation::Dup(_, _) =>
|
||||
{
|
||||
let val = queue.first().unwrap();
|
||||
queue.push(val.clone());
|
||||
}
|
||||
Operation::Swap(_, _) =>
|
||||
{
|
||||
let first = queue.remove(0);
|
||||
let second = queue.remove(0);
|
||||
queue.push(second);
|
||||
queue.push(first);
|
||||
}
|
||||
Operation::FunctionCall(function_name, _, _) =>
|
||||
{
|
||||
let function = functions.iter().find(|x| &x.name == function_name).unwrap();
|
||||
let function_context: &mut Vec<String> = &mut Vec::new();
|
||||
for _ in 0..function.ins.len()
|
||||
{
|
||||
let val = queue.remove(0);
|
||||
function_context.push(val);
|
||||
}
|
||||
output += interpret_program(&function.content, function_context, functions, arrays, debug)?.as_str();
|
||||
for val in function_context
|
||||
{
|
||||
queue.push(val.to_string());
|
||||
}
|
||||
}
|
||||
Operation::If(if_block, maybe_else_block, _, _) =>
|
||||
{
|
||||
let val = queue.remove(0);
|
||||
if val == "true"
|
||||
{
|
||||
output += interpret_program(if_block, queue, functions, arrays, debug)?.as_str();
|
||||
}
|
||||
else if let Some(else_block) = maybe_else_block
|
||||
{
|
||||
output += interpret_program(else_block, queue, functions, arrays, debug)?.as_str();
|
||||
}
|
||||
}
|
||||
Operation::Intrinsic(intrinsic_name, line, col) =>
|
||||
{
|
||||
match intrinsic_name.as_str()
|
||||
{
|
||||
"print" =>
|
||||
{
|
||||
state = FunctionExtractionState::Body
|
||||
output += queue.remove(0).to_string().as_str();
|
||||
}
|
||||
"-" =>
|
||||
{
|
||||
let minuend = queue.remove(0).parse::<i64>().unwrap();
|
||||
let subtrahend = queue.remove(0).parse::<i64>().unwrap();
|
||||
queue.push((minuend - subtrahend).to_string());
|
||||
}
|
||||
"+" =>
|
||||
{
|
||||
let addend1 = queue.remove(0).parse::<i64>().unwrap();
|
||||
let addend2 = queue.remove(0).parse::<i64>().unwrap();
|
||||
queue.push((addend1 + addend2).to_string());
|
||||
}
|
||||
"*" =>
|
||||
{
|
||||
let multiplicant1 = queue.remove(0).parse::<i64>().unwrap();
|
||||
let multiplicant2 = queue.remove(0).parse::<i64>().unwrap();
|
||||
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 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());
|
||||
}
|
||||
"==" =>
|
||||
{
|
||||
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::<bool>().unwrap();
|
||||
let second = queue.remove(0).parse::<bool>().unwrap();
|
||||
queue.push((first && second).to_string());
|
||||
}
|
||||
"println" =>
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Operation::Apply(name, word, line, col) =>
|
||||
{
|
||||
let arr: &mut Arr = arrays.iter_mut().find(|x| &x.name == name).unwrap();
|
||||
match word.as_str()
|
||||
{
|
||||
"write" =>
|
||||
{
|
||||
let position: i64 = queue.remove(0).parse::<i64>().unwrap();
|
||||
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(String::from("Attempted array out-of-bounds access\n"));
|
||||
}
|
||||
if position < 0
|
||||
{
|
||||
//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);
|
||||
if debug
|
||||
{
|
||||
println!("write before: {} {} {:?}", position, data, arr);
|
||||
}
|
||||
arr.data[position as usize] = data;
|
||||
if debug
|
||||
{
|
||||
println!("write after: {:?}", arr);
|
||||
}
|
||||
}
|
||||
"read" =>
|
||||
{
|
||||
let position: i64 = queue.remove(0).parse::<i64>().unwrap();
|
||||
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(String::from("Attempted array out-of-bounds access\n"));
|
||||
}
|
||||
if position < 0
|
||||
{
|
||||
//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());
|
||||
}
|
||||
"length" =>
|
||||
{
|
||||
queue.push(arr.length.to_string());
|
||||
}
|
||||
_ => return Err(format!("Unexpected application '{}' at {}:{}", word, line, col))
|
||||
}
|
||||
}
|
||||
Operation::While(while_block, _, _) =>
|
||||
{
|
||||
loop
|
||||
{
|
||||
let val = queue.remove(0);
|
||||
if val == "false"
|
||||
{
|
||||
break;
|
||||
}
|
||||
output += interpret_program(while_block, queue, functions, arrays, debug)?.as_str();
|
||||
}
|
||||
}
|
||||
Operation::Depth(_, _) =>
|
||||
{
|
||||
let depth = queue.len();
|
||||
queue.push(depth.to_string());
|
||||
}
|
||||
Operation::QueueDiagnostic(line, col) =>
|
||||
{
|
||||
println!("---Queue state at {}:{}---\nlength: {}\n{:?}\n------------------------------", line, col, queue.len(), queue);
|
||||
}
|
||||
Operation::Interrupt(_, _) => {}
|
||||
}
|
||||
if debug
|
||||
{
|
||||
println!("after: {:?}: {:?}, '{}'", operation, queue, 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>
|
||||
{
|
||||
for function in functions
|
||||
{
|
||||
if debug
|
||||
{
|
||||
println!("Now typechecking function '{}'", function.name);
|
||||
}
|
||||
typecheck_block(&function.content, &function.ins, &function.outs, functions, intrinsics, arrays, debug)?;
|
||||
if debug
|
||||
{
|
||||
println!("Successfully typechecked function '{}'", function.name);
|
||||
}
|
||||
}
|
||||
if debug
|
||||
{
|
||||
println!("Now typechecking main operations");
|
||||
}
|
||||
typecheck_block(operations, &Vec::new(), &Vec::new(), functions, intrinsics, arrays, debug)?;
|
||||
if debug
|
||||
{
|
||||
println!("Successfully typechecked main operations");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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)?;
|
||||
if &actual_outs != outs
|
||||
{
|
||||
let (line, col) = match operations.last()
|
||||
{
|
||||
Some(operation) =>
|
||||
{
|
||||
match operation
|
||||
{
|
||||
Operation::Interrupt(line, col) |
|
||||
Operation::Enqueue(_, _, line, col) |
|
||||
Operation::Dequeue(line, col) |
|
||||
Operation::Requeue(line, col) |
|
||||
Operation::Dup(line, col) |
|
||||
Operation::Swap(line, col) |
|
||||
Operation::FunctionCall(_, line, col) |
|
||||
Operation::If(_, _, line, col) |
|
||||
Operation::Intrinsic(_, line, col) |
|
||||
Operation::While(_, line, col) |
|
||||
Operation::QueueDiagnostic(line, col) |
|
||||
Operation::Apply(_, _, line, col) |
|
||||
Operation::Depth(line, col) => (*line, *col),
|
||||
}
|
||||
}
|
||||
None => (-1, -1)
|
||||
};
|
||||
return Err(format!("Wrong queue state at the end of a block, expected {:?} but got {:?} at {}:{}", outs, actual_outs, line, col));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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();
|
||||
type_queue.extend_from_slice(ins);
|
||||
let mut debug_string = String::from("");
|
||||
for operation in operations
|
||||
{
|
||||
if debug
|
||||
{
|
||||
debug_string = format!("operation: {:?}: {:?}", operation, type_queue);
|
||||
}
|
||||
match operation
|
||||
{
|
||||
Operation::Dequeue(line, col) =>
|
||||
{
|
||||
if type_queue.is_empty()
|
||||
{
|
||||
return Err(format!("Attempted to dequeue an element while the queue was empty at {}:{}", line, col));
|
||||
}
|
||||
type_queue.remove(0);
|
||||
}
|
||||
Operation::Enqueue(datatype, _, _, _) =>
|
||||
{
|
||||
type_queue.push(*datatype);
|
||||
}
|
||||
Operation::Dup(line, col) =>
|
||||
{
|
||||
if let Some(typ) = type_queue.first()
|
||||
{
|
||||
type_queue.push(*typ);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Err(format!("Attempted to dup an element while the queue was empty at {}:{}", line, col));
|
||||
}
|
||||
}
|
||||
Operation::Requeue(line, col) =>
|
||||
{
|
||||
if type_queue.is_empty()
|
||||
{
|
||||
return Err(format!("Attempted to requeue an element while the queue was empty at {}:{}", line, col));
|
||||
}
|
||||
let typ = type_queue.remove(0);
|
||||
type_queue.push(typ);
|
||||
}
|
||||
Operation::Swap(line, col) =>
|
||||
{
|
||||
if type_queue.is_empty()
|
||||
{
|
||||
return Err(format!("Attempted to get the first element for a swap while the queue was empty at {}:{}", line, col));
|
||||
}
|
||||
let first_typ = type_queue.remove(0);
|
||||
if type_queue.is_empty()
|
||||
{
|
||||
return Err(format!("Attempted to get the second element for a swap while the queue was empty at {}:{}", line, col));
|
||||
}
|
||||
let second_typ = type_queue.remove(0);
|
||||
type_queue.push(second_typ);
|
||||
type_queue.push(first_typ);
|
||||
}
|
||||
Operation::FunctionCall(function_name, line, col) =>
|
||||
{
|
||||
let function = functions.iter().find(|x| &x.name == function_name).unwrap();
|
||||
if function.ins.len() > type_queue.len()
|
||||
{
|
||||
return Err(format!("Attempted to call function '{}' at {}:{}, with insufficient elements in the queue, expected {:?} but got {:?}", function.name, line, col, function.ins, type_queue));
|
||||
}
|
||||
for in_type in &function.ins
|
||||
{
|
||||
let actual_type = type_queue.remove(0);
|
||||
if in_type != &actual_type
|
||||
{
|
||||
return Err(format!("Attempted to call function '{}' at {}:{} with a wrong parameter, expected {:?} but got {:?}", function.name, line, col, in_type, actual_type));
|
||||
}
|
||||
}
|
||||
type_queue.extend_from_slice(&function.outs);
|
||||
}
|
||||
Operation::If(if_block, maybe_else_block, line, col) =>
|
||||
{
|
||||
if type_queue.is_empty()
|
||||
{
|
||||
return Err(format!("Encountered if block with an empty queue at {}:{}", line, col));
|
||||
}
|
||||
let comparison_type = type_queue.remove(0);
|
||||
if comparison_type != Datatype::Bool
|
||||
{
|
||||
return Err(format!("Expected a Bool as an if condition but got {:?} instead at {}:{}", comparison_type, line, col));
|
||||
}
|
||||
if debug
|
||||
{
|
||||
println!("Starting to typecheck if block");
|
||||
}
|
||||
let if_ret = get_return_type(if_block, type_queue, functions, intrinsics, arrays, debug)?;
|
||||
let else_ret =
|
||||
if let Some(else_block) = maybe_else_block
|
||||
{
|
||||
if debug
|
||||
{
|
||||
println!("Starting to typecheck else block");
|
||||
}
|
||||
get_return_type(else_block, type_queue, functions, intrinsics, arrays, debug)?
|
||||
}
|
||||
else
|
||||
{
|
||||
type_queue.clone()
|
||||
};
|
||||
if if_ret != else_ret
|
||||
{
|
||||
return Err(format!("Incompatible queue states after if/else construction, expected {:?} but got {:?}", if_ret, else_ret));
|
||||
}
|
||||
type_queue.clear();
|
||||
type_queue.extend_from_slice(&if_ret);
|
||||
}
|
||||
Operation::Intrinsic(intrinsic_name, line, col) =>
|
||||
{
|
||||
let io = intrinsics.get(intrinsic_name.as_str()).unwrap();
|
||||
if io.0.len() > type_queue.len()
|
||||
{
|
||||
return Err(format!("Attempted to call intrinsic '{}' at {}:{}, with insufficient elements in the queue, expected {:?} but got {:?}", intrinsic_name, line, col, io.0, type_queue));
|
||||
}
|
||||
for in_type in &io.0
|
||||
{
|
||||
let actual_type = type_queue.remove(0);
|
||||
if in_type != &actual_type
|
||||
{
|
||||
return Err(format!("Attempted to call intrinsic '{}' at {}:{} with a wrong parameter, expected {:?} but got {:?}", intrinsic_name, line, col, in_type, actual_type));
|
||||
}
|
||||
}
|
||||
type_queue.extend_from_slice(&io.1);
|
||||
}
|
||||
Operation::While(while_block, line, col) =>
|
||||
{
|
||||
if type_queue.is_empty()
|
||||
{
|
||||
return Err(format!("Encountered while block with an empty queue at {}:{}", line, col));
|
||||
}
|
||||
let comparison_type = type_queue.remove(0);
|
||||
if comparison_type != Datatype::Bool
|
||||
{
|
||||
return Err(format!("Expected a Bool as a while condition but got {:?} instead at {}:{}", comparison_type, line, col));
|
||||
}
|
||||
if debug
|
||||
{
|
||||
println!("Starting to typecheck while block");
|
||||
}
|
||||
let mut outs = type_queue.clone();
|
||||
outs.insert(0, Datatype::Bool);
|
||||
typecheck_block(while_block, type_queue, &outs, functions, intrinsics, arrays, debug)?;
|
||||
}
|
||||
Operation::Depth(_, _) =>
|
||||
{
|
||||
type_queue.push(Datatype::Int);
|
||||
}
|
||||
Operation::QueueDiagnostic(line, col) =>
|
||||
{
|
||||
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()
|
||||
{
|
||||
"write" =>
|
||||
{
|
||||
if type_queue.is_empty() || type_queue.remove(0) != Datatype::Int
|
||||
{
|
||||
return Err(format!("Expected a position for a write application at {}:{}", line, col));
|
||||
}
|
||||
let expected_type = arrays.iter().find(|x| &x.name == name).unwrap().datatype;
|
||||
if type_queue.is_empty()
|
||||
{
|
||||
return Err(format!("Expected data for a write application at {}:{}", line, col));
|
||||
}
|
||||
let actual_type = type_queue.remove(0);
|
||||
if actual_type != expected_type
|
||||
{
|
||||
return Err(format!("Expected a {:?} value but got a {:?} value at {}:{}", expected_type, actual_type, line, col));
|
||||
}
|
||||
}
|
||||
"read" =>
|
||||
{
|
||||
if type_queue.is_empty() || type_queue.remove(0) != Datatype::Int
|
||||
{
|
||||
return Err(format!("Expected a position for a read application at {}:{}", line, col));
|
||||
}
|
||||
let typ = arrays.iter().find(|x| &x.name == name).unwrap().datatype;
|
||||
type_queue.push(typ);
|
||||
}
|
||||
"length" =>
|
||||
{
|
||||
type_queue.push(Datatype::Int);
|
||||
}
|
||||
_ => return Err(format!("Encountered unknown application '{}' at {}:{}", word, line, col))
|
||||
}
|
||||
}
|
||||
}
|
||||
if debug
|
||||
{
|
||||
println!("{} => {:?}", debug_string, type_queue);
|
||||
}
|
||||
}
|
||||
Ok(type_queue.clone())
|
||||
}
|
||||
|
||||
fn validate_function_calls(operations: &Vec<Operation>, functions: &Vec<Function>, debug: bool) -> Result<(), String>
|
||||
{
|
||||
for function in functions
|
||||
{
|
||||
validate_function_calls_in_block(&function.content, functions)?;
|
||||
if debug
|
||||
{
|
||||
println!("Successfully validated function calls in function '{}'", function.name);
|
||||
}
|
||||
}
|
||||
validate_function_calls_in_block(operations, functions)?;
|
||||
if debug
|
||||
{
|
||||
println!("Successfully validated function calls in main operations");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_function_calls_in_block(block: &Vec<Operation>, functions: &Vec<Function>) -> Result<(), String>
|
||||
{
|
||||
for operation in block
|
||||
{
|
||||
match operation
|
||||
{
|
||||
Operation::Depth(_, _) | Operation::QueueDiagnostic(_, _) | Operation::Intrinsic(_, _, _) | Operation::Enqueue(_, _, _, _) | Operation::Dequeue(_, _) |
|
||||
Operation::Requeue(_, _) | Operation::Dup(_, _) | Operation::Swap(_, _) | Operation::Apply(_, _, _, _) | Operation::Interrupt(_, _) => {},
|
||||
Operation::FunctionCall(function_name, line, col) =>
|
||||
{
|
||||
if !functions.iter().any(|x| &x.name == function_name)
|
||||
{
|
||||
return Err(format!("Call to unknown function '{}' at {}:{}", function_name, line, col));
|
||||
}
|
||||
}
|
||||
Operation::If(if_block, maybe_else_block, _, _) =>
|
||||
{
|
||||
validate_function_calls_in_block(if_block, functions)?;
|
||||
if let Some(else_block) = maybe_else_block
|
||||
{
|
||||
validate_function_calls_in_block(else_block, functions)?;
|
||||
}
|
||||
}
|
||||
Operation::While(while_block, _, _) =>
|
||||
{
|
||||
validate_function_calls_in_block(while_block, functions)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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 arrays: Vec<Arr> = Vec::new();
|
||||
let mut new_tokens: Vec<Token> = Vec::new();
|
||||
while let Some(token) = tokens_iter.next()
|
||||
{
|
||||
if let Token::Keyword(word, line, col) = token
|
||||
{
|
||||
if word == "arr"
|
||||
{
|
||||
if debug
|
||||
{
|
||||
println!("Found an array at {}:{}", line, col);
|
||||
}
|
||||
if let Some(Token::Keyword(name, _, _)) = tokens_iter.next()
|
||||
{
|
||||
if functions.iter().any(|x| &x.name == name)
|
||||
{
|
||||
return Err(format!("Cannot redeclare an array with the same name as a function {}:{}", line, col));
|
||||
}
|
||||
if arrays.iter().any(|x| &x.name == name)
|
||||
{
|
||||
return Err(format!("Cannot redeclare an array with the same name as an array {}:{}", line, col));
|
||||
}
|
||||
if intrinsics.contains_key(name.as_str())
|
||||
{
|
||||
return Err(format!("An array cannot have the same name as an intrinsic ({}) at {}:{}", name, line, col));
|
||||
}
|
||||
if let Some(Token::Keyword(open_curly, _, _)) = tokens_iter.next()
|
||||
{
|
||||
if open_curly != "{"
|
||||
{
|
||||
return Err(format!("Expected '{{' in array declaration at {}:{}", line, col));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
panic!("Expected '{{' to open the function's body at {}:{}", line, col)
|
||||
return Err(format!("Reached the end of the file while parsing an array at {}:{}", line, col));
|
||||
}
|
||||
if let Some(Token::Keyword(typ, _, _)) = tokens_iter.next()
|
||||
{
|
||||
let datatype = str_to_datatype(typ, *line, *col)?;
|
||||
if let Some(Token::IntLit(size_str, _, _)) = tokens_iter.next()
|
||||
{
|
||||
let size = size_str.parse::<i64>().unwrap();
|
||||
if let Some(Token::Keyword(close_curly, _, _)) = tokens_iter.next()
|
||||
{
|
||||
if close_curly != "}"
|
||||
{
|
||||
return Err(format!("Expected '}}' in array declaration at {}:{}", line, col));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Err(format!("Reached the end of the file while parsing an array at {}:{}", line, col));
|
||||
}
|
||||
let mut data: Vec<String> = Vec::new();
|
||||
let default_val = match datatype
|
||||
{
|
||||
Datatype::String => String::new(),
|
||||
Datatype::Bool => String::from("false"),
|
||||
Datatype::Int => String::from("0"),
|
||||
};
|
||||
for _ in 0..size
|
||||
{
|
||||
data.push(default_val.clone());
|
||||
}
|
||||
arrays.push(Arr { name: sanitize_name(name.clone()), datatype, length: size , data });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Err(format!("Reached the end of the file while parsing an array at {}:{}", line, col))
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
panic!("Expected '{{' to open the function's body") // TODO: Add location
|
||||
return Err(format!("Expected array name, at {}:{}", line, col));
|
||||
}
|
||||
}
|
||||
FunctionExtractionState::Body =>
|
||||
else
|
||||
{
|
||||
if let Token::Keyword(name, _, _) = token
|
||||
{
|
||||
if name == "}"
|
||||
{
|
||||
state = FunctionExtractionState::Outside;
|
||||
functions.push(Function { name: function_name.clone(), ins: ins.clone() , outs: outs.clone(), content: content.clone()});
|
||||
function_name.clear();
|
||||
ins.clear();
|
||||
outs.clear();
|
||||
content.clear();
|
||||
indices_to_remove.push(i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
content.push(token.clone());
|
||||
new_tokens.push(token.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if state != FunctionExtractionState::Outside
|
||||
else
|
||||
{
|
||||
indices_to_remove.push(i);
|
||||
new_tokens.push(token.clone());
|
||||
}
|
||||
}
|
||||
indices_to_remove.reverse();
|
||||
for i in indices_to_remove
|
||||
{
|
||||
tokens.remove(i);
|
||||
}
|
||||
return functions;
|
||||
tokens.clear();
|
||||
tokens.extend_from_slice(&new_tokens);
|
||||
Ok(arrays)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum FunctionExtractionState
|
||||
fn sanitize_name(name: String) -> String
|
||||
{
|
||||
Outside,
|
||||
Ins,
|
||||
Outs,
|
||||
OpenCurly,
|
||||
Body,
|
||||
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>
|
||||
{
|
||||
match s
|
||||
{
|
||||
//"any" => Ok(Datatype::Any),
|
||||
"bool" => Ok(Datatype::Bool),
|
||||
"int" => Ok(Datatype::Int),
|
||||
"str" => Ok(Datatype::String),
|
||||
_ => Err(format!("Expected a datatype for the array, got {} instead at {}:{}", s, line, col))
|
||||
}
|
||||
}
|
||||
|
||||
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 new_tokens: Vec<Token> = Vec::new();
|
||||
while let Some(token) = tokens_iter.next()
|
||||
{
|
||||
if let Token::Keyword(word, line, col) = token
|
||||
{
|
||||
if word == "function"
|
||||
{
|
||||
if debug
|
||||
{
|
||||
println!("Found a function at {}:{}", line, col);
|
||||
}
|
||||
let mut ins: Vec<Datatype> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let maybe_token = tokens_iter.next();
|
||||
match maybe_token
|
||||
{
|
||||
Some(token) =>
|
||||
{
|
||||
match token
|
||||
{
|
||||
Token::IntLit(_, line, col) | Token::StringLit(_, line, col) | Token::BoolLit(_, line, col) | Token::Import(line, col) |
|
||||
Token::Apply(_, _, line, col) =>
|
||||
{
|
||||
return Err(format!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col));
|
||||
}
|
||||
Token::Keyword(word, line, col) =>
|
||||
{
|
||||
if word == "=>"
|
||||
{
|
||||
break;
|
||||
}
|
||||
match word.as_str()
|
||||
{
|
||||
//"any" => ins.push(Datatype::Any),
|
||||
"str" => ins.push(Datatype::String),
|
||||
"int" => ins.push(Datatype::Int),
|
||||
"bool" => ins.push(Datatype::Bool),
|
||||
_ => return Err(format!("Expected input parameters for a function but got {} instead at {}:{}", word, line, col))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => return Err("Unexpected end of file while extracting a function".to_string())
|
||||
}
|
||||
}
|
||||
if debug
|
||||
{
|
||||
println!("ins: {:?}", ins);
|
||||
}
|
||||
let mut outs: Vec<Datatype> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let maybe_token = tokens_iter.next();
|
||||
match maybe_token
|
||||
{
|
||||
Some(token) =>
|
||||
{
|
||||
match token
|
||||
{
|
||||
Token::IntLit(_, line, col) | Token::StringLit(_, line, col) | Token::BoolLit(_, line, col) | Token::Import(line, col) |
|
||||
Token::Apply(_, _, line, col) =>
|
||||
{
|
||||
return Err(format!("Expected input parameters for a function but got {:?} instead at {}:{}", token, line, col));
|
||||
}
|
||||
Token::Keyword(word, line, col) =>
|
||||
{
|
||||
match word.as_str()
|
||||
{
|
||||
//"any" => outs.push(Datatype::Any),
|
||||
"str" => outs.push(Datatype::String),
|
||||
"int" => outs.push(Datatype::Int),
|
||||
"bool" => outs.push(Datatype::Bool),
|
||||
"{" | "}" | "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)
|
||||
{
|
||||
return Err(format!("Redeclaration of function '{}' at {}:{}", word, line, col));
|
||||
}
|
||||
if intrinsics.contains_key(word.as_str())
|
||||
{
|
||||
return Err(format!("Function name {} at {}:{} is already an intrinsic", word, line, col));
|
||||
}
|
||||
if debug
|
||||
{
|
||||
println!("outs: {:?}", outs);
|
||||
}
|
||||
let block = parse_block(&mut tokens_iter, intrinsics, debug)?;
|
||||
functions.push(Function {name: sanitize_name(word.clone()), ins, outs, content: block});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => return Err("Unexpected end of file while extracting a function".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new_tokens.push(token.clone());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new_tokens.push(token.clone());
|
||||
}
|
||||
}
|
||||
tokens.clear();
|
||||
tokens.extend_from_slice(&new_tokens);
|
||||
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>
|
||||
{
|
||||
if let Some(Token::Keyword(word, line, col)) = tokens_iter.next()
|
||||
{
|
||||
if word != "{"
|
||||
{
|
||||
return Err(format!("Expected '{{' to open a block but got {} at {}:{}", word, line, col));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Err("Expected '{' to open a block".to_string());
|
||||
}
|
||||
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>
|
||||
{
|
||||
let mut operations: Vec<Operation> = Vec::new();
|
||||
loop
|
||||
{
|
||||
let maybe_token = tokens_iter.next();
|
||||
match maybe_token
|
||||
{
|
||||
Some(token) =>
|
||||
{
|
||||
match token
|
||||
{
|
||||
Token::IntLit(value, line, col) =>
|
||||
{
|
||||
operations.push(Operation::Enqueue(Datatype::Int, value.clone(), *line, *col));
|
||||
}
|
||||
Token::StringLit(value, line, col) =>
|
||||
{
|
||||
operations.push(Operation::Enqueue(Datatype::String, value.clone(), *line, *col));
|
||||
}
|
||||
Token::BoolLit(value, line, col) =>
|
||||
{
|
||||
operations.push(Operation::Enqueue(Datatype::Bool, value.clone(), *line, *col));
|
||||
}
|
||||
Token::Apply(name, word, 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) =>
|
||||
{
|
||||
if intrinsics.contains_key(word.as_str())
|
||||
{
|
||||
operations.push(Operation::Intrinsic(word.clone(), *line, *col));
|
||||
}
|
||||
else if word == "if"
|
||||
{
|
||||
let block = parse_block(tokens_iter, intrinsics, debug)?;
|
||||
let else_block =
|
||||
if let Some(Token::Keyword(maybe_else, _, _)) = tokens_iter.peek()
|
||||
{
|
||||
if maybe_else == "else"
|
||||
{
|
||||
tokens_iter.next();
|
||||
Some(parse_block(tokens_iter, intrinsics, debug)?)
|
||||
}
|
||||
else
|
||||
{
|
||||
None
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
None
|
||||
};
|
||||
operations.push(Operation::If(block, else_block, *line, *col));
|
||||
}
|
||||
else if word == "while"
|
||||
{
|
||||
operations.push(Operation::While(parse_block(tokens_iter, intrinsics, debug)?, *line, *col));
|
||||
}
|
||||
else if word == "deq"
|
||||
{
|
||||
operations.push(Operation::Dequeue(*line, *col));
|
||||
}
|
||||
else if word == "req"
|
||||
{
|
||||
operations.push(Operation::Requeue(*line, *col));
|
||||
}
|
||||
else if word == "dup"
|
||||
{
|
||||
operations.push(Operation::Dup(*line, *col));
|
||||
}
|
||||
else if word == "swp"
|
||||
{
|
||||
operations.push(Operation::Swap(*line, *col));
|
||||
}
|
||||
else if word == "depth"
|
||||
{
|
||||
operations.push(Operation::Depth(*line, *col));
|
||||
}
|
||||
else if word == "???"
|
||||
{
|
||||
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);
|
||||
}
|
||||
else if word == "{" || word == "function"
|
||||
{
|
||||
return Err(format!("Unexpected keyword {} at {}:{}", word, line, col));
|
||||
}
|
||||
else
|
||||
{
|
||||
operations.push(Operation::FunctionCall(sanitize_name(word.clone()), *line, *col));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None =>
|
||||
{
|
||||
if delimiter.is_some()
|
||||
{
|
||||
return Err("Reached the end of the file while parsing a block".to_string());
|
||||
}
|
||||
else
|
||||
{
|
||||
return Ok(operations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn usage()
|
||||
@ -200,7 +1855,7 @@ fn usage()
|
||||
exit(0);
|
||||
}
|
||||
|
||||
fn tokenize(text: &str) -> Vec<Token>
|
||||
fn tokenize(text: &str) -> Result<Vec<Token>, String>
|
||||
{
|
||||
let mut tokens: Vec<Token> = Vec::new();
|
||||
let mut line = 1;
|
||||
@ -208,6 +1863,7 @@ fn tokenize(text: &str) -> Vec<Token>
|
||||
let mut state = TokenizerState::Whitespace;
|
||||
let mut word = String::new();
|
||||
let mut iter = text.chars().peekable();
|
||||
let mut application_name = String::new();
|
||||
while let Some(ch) = iter.next()
|
||||
{
|
||||
if ch == '/' && iter.peek() == Some(&'/')
|
||||
@ -247,7 +1903,7 @@ fn tokenize(text: &str) -> Vec<Token>
|
||||
if ch == '"'
|
||||
{
|
||||
state = TokenizerState::Whitespace;
|
||||
tokens.push(Token::StringLit(word.clone(), line, col));
|
||||
tokens.push(Token::StringLit(word.clone().replace("\\n", "\n"), line, col));
|
||||
word.clear();
|
||||
}
|
||||
else
|
||||
@ -260,13 +1916,29 @@ fn tokenize(text: &str) -> Vec<Token>
|
||||
if ch.is_whitespace()
|
||||
{
|
||||
state = TokenizerState::Whitespace;
|
||||
if let Ok(number) = word.parse::<i64>()
|
||||
if application_name.is_empty()
|
||||
{
|
||||
tokens.push(Token::IntLit(number, line, col));
|
||||
if word.parse::<i64>().is_ok()
|
||||
{
|
||||
tokens.push(Token::IntLit(word.clone(), line, col));
|
||||
}
|
||||
else if word == "true" || word == "false"
|
||||
{
|
||||
tokens.push(Token::BoolLit(word.clone(), line, col));
|
||||
}
|
||||
else if word == "import"
|
||||
{
|
||||
tokens.push(Token::Import(line, col));
|
||||
}
|
||||
else
|
||||
{
|
||||
tokens.push(Token::Keyword(word.clone(), line, col));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tokens.push(Token::Keyword(word.clone(), line, col));
|
||||
tokens.push(Token::Apply(sanitize_name(application_name.clone()), word.clone(), line, col));
|
||||
application_name.clear();
|
||||
}
|
||||
word.clear();
|
||||
}
|
||||
@ -274,7 +1946,12 @@ fn tokenize(text: &str) -> Vec<Token>
|
||||
{
|
||||
match ch
|
||||
{
|
||||
'"' => panic!("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();
|
||||
word.clear();
|
||||
}
|
||||
_ =>
|
||||
{
|
||||
word.push(ch);
|
||||
@ -294,13 +1971,20 @@ fn tokenize(text: &str) -> Vec<Token>
|
||||
{
|
||||
TokenizerState::Quote =>
|
||||
{
|
||||
panic!("Encountered EOF before closing string");
|
||||
return Err("Encountered EOF before closing string".to_string());
|
||||
}
|
||||
TokenizerState::Whitespace | TokenizerState::Comment => {},
|
||||
TokenizerState::Keyword =>
|
||||
{
|
||||
tokens.push(Token::Keyword(word.clone(), line, col));
|
||||
if application_name.is_empty()
|
||||
{
|
||||
tokens.push(Token::Keyword(word.clone(), line, col));
|
||||
}
|
||||
else
|
||||
{
|
||||
tokens.push(Token::Apply(sanitize_name(application_name.clone()), word.clone(), line, col));
|
||||
}
|
||||
}
|
||||
}
|
||||
tokens
|
||||
Ok(tokens)
|
||||
}
|
||||
|
30
std.qbl
Normal file
30
std.qbl
Normal 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 +
|
||||
}
|
10
test.qbl
10
test.qbl
@ -1,10 +0,0 @@
|
||||
"Hello, World!\n" print 43 foo foo deq
|
||||
|
||||
|
||||
// Dequeues, enqueues 42 and 17, prints the head
|
||||
function any => int foo
|
||||
{
|
||||
deq 42 17 print
|
||||
}
|
||||
|
||||
"test2" print
|
6
tests/array_oob_high_read.qbl
Normal file
6
tests/array_oob_high_read.qbl
Normal file
@ -0,0 +1,6 @@
|
||||
//invalid,Attempted array out-of-bounds access
|
||||
//:END:
|
||||
|
||||
arr test { bool 4 }
|
||||
|
||||
5 test.read deq
|
6
tests/array_oob_high_write.qbl
Normal file
6
tests/array_oob_high_write.qbl
Normal file
@ -0,0 +1,6 @@
|
||||
//invalid,Attempted array out-of-bounds access
|
||||
//:END:
|
||||
|
||||
arr test { bool 4 }
|
||||
|
||||
5 false test.write
|
6
tests/array_oob_low_read.qbl
Normal file
6
tests/array_oob_low_read.qbl
Normal file
@ -0,0 +1,6 @@
|
||||
//invalid,Attempted array out-of-bounds access
|
||||
//:END:
|
||||
|
||||
arr test { bool 4 }
|
||||
|
||||
-1 test.read deq
|
6
tests/array_oob_low_write.qbl
Normal file
6
tests/array_oob_low_write.qbl
Normal file
@ -0,0 +1,6 @@
|
||||
//invalid,Attempted array out-of-bounds access
|
||||
//:END:
|
||||
|
||||
arr test { bool 4 }
|
||||
|
||||
-1 true test.write
|
26
tests/array_tests.qbl
Normal file
26
tests/array_tests.qbl
Normal file
@ -0,0 +1,26 @@
|
||||
//valid,0000
|
||||
//0100
|
||||
//2100
|
||||
//2103
|
||||
//:END:
|
||||
arr test { int 4 }
|
||||
|
||||
function => dump
|
||||
{
|
||||
true 0
|
||||
while
|
||||
{
|
||||
// i
|
||||
dup test.read req intToStr req print
|
||||
1 + test.length dup < req
|
||||
}
|
||||
deq "" println
|
||||
}
|
||||
|
||||
dump
|
||||
1 1 test.write
|
||||
dump
|
||||
0 2 test.write
|
||||
dump
|
||||
3 3 test.write
|
||||
dump
|
7
tests/basic_import.qbl
Normal file
7
tests/basic_import.qbl
Normal file
@ -0,0 +1,7 @@
|
||||
//valid,true
|
||||
//:END:
|
||||
|
||||
import "../std.qbl"
|
||||
|
||||
true boolToStr println
|
||||
|
26
tests/comparisons.qbl
Normal file
26
tests/comparisons.qbl
Normal 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
|
355
tests/conway.qbl
Normal file
355
tests/conway.qbl
Normal file
@ -0,0 +1,355 @@
|
||||
//valid,.#......
|
||||
//..#.....
|
||||
//###.....
|
||||
//........
|
||||
//........
|
||||
//........
|
||||
//........
|
||||
//........
|
||||
//--------
|
||||
//........
|
||||
//#.#.....
|
||||
//.##.....
|
||||
//.#......
|
||||
//........
|
||||
//........
|
||||
//........
|
||||
//........
|
||||
//--------
|
||||
//........
|
||||
//..#.....
|
||||
//#.#.....
|
||||
//.##.....
|
||||
//........
|
||||
//........
|
||||
//........
|
||||
//........
|
||||
//--------
|
||||
//........
|
||||
//.#......
|
||||
//..##....
|
||||
//.##.....
|
||||
//........
|
||||
//........
|
||||
//........
|
||||
//........
|
||||
//--------
|
||||
//........
|
||||
//..#.....
|
||||
//...#....
|
||||
//.###....
|
||||
//........
|
||||
//........
|
||||
//........
|
||||
//........
|
||||
//--------
|
||||
//:END:
|
||||
|
||||
arr field { int 64 }
|
||||
|
||||
arr field2 { int 64 }
|
||||
|
||||
1 0 coordToIndex 1 field.write
|
||||
2 1 coordToIndex 1 field.write
|
||||
0 2 coordToIndex 1 field.write
|
||||
1 2 coordToIndex 1 field.write
|
||||
2 2 coordToIndex 1 field.write
|
||||
|
||||
printArray
|
||||
|
||||
true 3
|
||||
while
|
||||
{
|
||||
// i
|
||||
iteration
|
||||
0 dup 1 > -
|
||||
}
|
||||
deq
|
||||
|
||||
function => iteration
|
||||
{
|
||||
true 0
|
||||
while
|
||||
{
|
||||
// y
|
||||
true 0 req
|
||||
// true x y
|
||||
while
|
||||
{
|
||||
// x y
|
||||
dup swp req
|
||||
// y x x
|
||||
dup swp
|
||||
// x y x y
|
||||
neighbourhood
|
||||
// x y c
|
||||
dup swp req
|
||||
// x y x c
|
||||
req dup swp req
|
||||
// x y x y c
|
||||
coordToIndex req req req
|
||||
// i x y c
|
||||
dup field.read
|
||||
// x y c i v
|
||||
req req req swp
|
||||
// x y c v i
|
||||
req req
|
||||
// c v i x y
|
||||
update
|
||||
// x y
|
||||
check
|
||||
req
|
||||
// lt x+1 y
|
||||
}
|
||||
// x y
|
||||
deq check
|
||||
// lt y+1
|
||||
}
|
||||
deq
|
||||
copyArrays
|
||||
printArray
|
||||
}
|
||||
|
||||
function => copyArrays
|
||||
{
|
||||
true 0
|
||||
while
|
||||
{
|
||||
// 0
|
||||
dup field2.read
|
||||
dup field.write
|
||||
checkArr
|
||||
}
|
||||
deq
|
||||
}
|
||||
|
||||
function => printArray
|
||||
{
|
||||
true 0
|
||||
while
|
||||
{
|
||||
// y
|
||||
true 0 req
|
||||
// true x y
|
||||
while
|
||||
{
|
||||
// x y
|
||||
dup req dup swp
|
||||
// x y x y
|
||||
coordToIndex
|
||||
// x y i
|
||||
req req field.read
|
||||
// x y v
|
||||
req req printSym
|
||||
// x y
|
||||
check req
|
||||
// lt x+1 y
|
||||
}
|
||||
// x y
|
||||
"" deq req println check
|
||||
// lt y+1
|
||||
}
|
||||
deq
|
||||
"--------" println
|
||||
}
|
||||
|
||||
function int => printSym
|
||||
{
|
||||
// x
|
||||
1 ==
|
||||
// eq
|
||||
if
|
||||
{
|
||||
"#"
|
||||
}
|
||||
else
|
||||
{
|
||||
"."
|
||||
}
|
||||
print
|
||||
}
|
||||
|
||||
function int => bool int checkArr
|
||||
{
|
||||
// i
|
||||
1 + field.length dup
|
||||
// i+1 l i+1
|
||||
< req
|
||||
// lt i+1
|
||||
}
|
||||
|
||||
function int => bool int check
|
||||
{
|
||||
//???
|
||||
// i
|
||||
1 + 8 dup < req
|
||||
//???
|
||||
// i+1 8 i+1
|
||||
}
|
||||
|
||||
function int int int => update
|
||||
{
|
||||
// count value index
|
||||
3 dup req req req
|
||||
// 3 c c v i
|
||||
== req req req
|
||||
// eq c v i
|
||||
if
|
||||
{
|
||||
1 deq deq field2.write
|
||||
}
|
||||
else
|
||||
{
|
||||
// c v i
|
||||
2 req req req
|
||||
// 2 c v i
|
||||
== 1 req req req
|
||||
// 1 v i eq
|
||||
== req && req
|
||||
// teq i
|
||||
if
|
||||
{
|
||||
1
|
||||
}
|
||||
else
|
||||
{
|
||||
0
|
||||
}
|
||||
field2.write
|
||||
}
|
||||
}
|
||||
|
||||
function int int => int neighbourhood
|
||||
{
|
||||
// x y
|
||||
dup 1 req req -
|
||||
// x y x-1
|
||||
0 req req dup >=
|
||||
// x y x-1 ge
|
||||
req req req
|
||||
// ge x y x-1
|
||||
if
|
||||
{
|
||||
req dup swp
|
||||
// x y x-1 y
|
||||
req req
|
||||
// x-1 y x y
|
||||
row
|
||||
// x y abc
|
||||
}
|
||||
else
|
||||
{
|
||||
// x y x-1
|
||||
req req
|
||||
// x-1 x y
|
||||
deq 0
|
||||
// x y 0
|
||||
}
|
||||
dup swp req req
|
||||
// y x abc x
|
||||
dup swp req row
|
||||
// x y abc def
|
||||
req req +
|
||||
// x y abcdef
|
||||
|
||||
dup swp req req
|
||||
// y x abcdef x
|
||||
dup swp req
|
||||
// x y x y abcdef
|
||||
coordToIndex
|
||||
// x y abcdef i
|
||||
req req req
|
||||
// i x y abcdef
|
||||
field.read
|
||||
// x y abcdef v
|
||||
req req -
|
||||
// x y abcdef-v
|
||||
|
||||
1 req req req
|
||||
// 1 x y abcdef
|
||||
+ req req
|
||||
// x+1 y abcdef
|
||||
dup 8 req req req
|
||||
// x+1 l x+1 y abcdef
|
||||
< req req req
|
||||
// lt x+1 y abcdef
|
||||
if
|
||||
{
|
||||
row
|
||||
// abcdef ghi
|
||||
}
|
||||
else
|
||||
{
|
||||
deq deq 0
|
||||
// abcdef 0
|
||||
}
|
||||
+
|
||||
// abcdefghi
|
||||
}
|
||||
|
||||
function int int => int row
|
||||
{
|
||||
// x y
|
||||
1 req dup -
|
||||
// x y y-1
|
||||
dup req req
|
||||
// y-1 x x y
|
||||
0 dup swp req req
|
||||
// 0 y-1 x y-1 x y
|
||||
> req req req req
|
||||
// gt x y-1 x y
|
||||
if
|
||||
{
|
||||
deq deq 0
|
||||
// x y 0
|
||||
}
|
||||
else
|
||||
{
|
||||
// x y-1 x y
|
||||
coordToIndex req req
|
||||
// i x y
|
||||
field.read
|
||||
// x y a
|
||||
}
|
||||
// x y a
|
||||
dup swp req req
|
||||
// y x a x
|
||||
dup swp req
|
||||
// x y x y a
|
||||
coordToIndex
|
||||
// x y a i
|
||||
req req req field.read
|
||||
// x y a b
|
||||
req req +
|
||||
// x y a+b
|
||||
req 1 req
|
||||
// a+b x 1 y
|
||||
req req + req
|
||||
// x y+1 a+b
|
||||
req req 8
|
||||
// a+b x y+1 l
|
||||
req req dup
|
||||
// y+1 l a+b x y+1
|
||||
< req req req
|
||||
// lt a+b x y+1
|
||||
if
|
||||
{
|
||||
// a+b x y+1
|
||||
req coordToIndex
|
||||
// a+b i
|
||||
req field.read
|
||||
// a+b c
|
||||
}
|
||||
else
|
||||
{
|
||||
req deq deq 0
|
||||
// a+b 0
|
||||
}
|
||||
+
|
||||
// a+b+c
|
||||
}
|
||||
|
||||
function int int => int coordToIndex
|
||||
{
|
||||
req 8 req * +
|
||||
}
|
374
tests/conway_diehard.qbl
Normal file
374
tests/conway_diehard.qbl
Normal file
@ -0,0 +1,374 @@
|
||||
//valid,.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//...................#.........
|
||||
//.............##..............
|
||||
//..............#...###........
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//--------
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//.............................
|
||||
//--------
|
||||
//:END:
|
||||
|
||||
arr field { int 841 }
|
||||
|
||||
arr field2 { int 841 }
|
||||
|
||||
13 9 coordToIndex 1 field.write
|
||||
14 9 coordToIndex 1 field.write
|
||||
14 10 coordToIndex 1 field.write
|
||||
19 8 coordToIndex 1 field.write
|
||||
18 10 coordToIndex 1 field.write
|
||||
19 10 coordToIndex 1 field.write
|
||||
20 10 coordToIndex 1 field.write
|
||||
|
||||
printArray
|
||||
|
||||
true 129
|
||||
while
|
||||
{
|
||||
// i
|
||||
iteration
|
||||
0 dup 1 > -
|
||||
}
|
||||
deq
|
||||
|
||||
printArray
|
||||
|
||||
function => iteration
|
||||
{
|
||||
true 0
|
||||
while
|
||||
{
|
||||
// y
|
||||
true 0 req
|
||||
// true x y
|
||||
while
|
||||
{
|
||||
// x y
|
||||
dup swp req
|
||||
// y x x
|
||||
dup swp
|
||||
// x y x y
|
||||
neighbourhood
|
||||
// x y c
|
||||
dup swp req
|
||||
// x y x c
|
||||
req dup swp req
|
||||
// x y x y c
|
||||
coordToIndex req req req
|
||||
// i x y c
|
||||
dup field.read
|
||||
// x y c i v
|
||||
req req req swp
|
||||
// x y c v i
|
||||
req req
|
||||
// c v i x y
|
||||
update
|
||||
// x y
|
||||
check
|
||||
req
|
||||
// lt x+1 y
|
||||
}
|
||||
// x y
|
||||
deq check
|
||||
// lt y+1
|
||||
}
|
||||
deq
|
||||
copyArrays
|
||||
}
|
||||
|
||||
function => copyArrays
|
||||
{
|
||||
true 0
|
||||
while
|
||||
{
|
||||
// 0
|
||||
dup field2.read
|
||||
dup field.write
|
||||
checkArr
|
||||
}
|
||||
deq
|
||||
}
|
||||
|
||||
function => printArray
|
||||
{
|
||||
true 0
|
||||
while
|
||||
{
|
||||
// y
|
||||
true 0 req
|
||||
// true x y
|
||||
while
|
||||
{
|
||||
// x y
|
||||
dup req dup swp
|
||||
// x y x y
|
||||
coordToIndex
|
||||
// x y i
|
||||
req req field.read
|
||||
// x y v
|
||||
req req printSym
|
||||
// x y
|
||||
check req
|
||||
// lt x+1 y
|
||||
}
|
||||
// x y
|
||||
"" deq req println check
|
||||
// lt y+1
|
||||
}
|
||||
deq
|
||||
"--------" println
|
||||
}
|
||||
|
||||
function int => printSym
|
||||
{
|
||||
// x
|
||||
1 ==
|
||||
// eq
|
||||
if
|
||||
{
|
||||
"#"
|
||||
}
|
||||
else
|
||||
{
|
||||
"."
|
||||
}
|
||||
print
|
||||
}
|
||||
|
||||
function int => bool int checkArr
|
||||
{
|
||||
// i
|
||||
1 + field.length dup
|
||||
// i+1 l i+1
|
||||
< req
|
||||
// lt i+1
|
||||
}
|
||||
|
||||
function int => bool int check
|
||||
{
|
||||
//???
|
||||
// i
|
||||
1 + 29 dup < req
|
||||
//???
|
||||
// i+1 29 i+1
|
||||
}
|
||||
|
||||
function int int int => update
|
||||
{
|
||||
// count value index
|
||||
3 dup req req req
|
||||
// 3 c c v i
|
||||
== req req req
|
||||
// eq c v i
|
||||
if
|
||||
{
|
||||
1 deq deq field2.write
|
||||
}
|
||||
else
|
||||
{
|
||||
// c v i
|
||||
2 req req req
|
||||
// 2 c v i
|
||||
== 1 req req req
|
||||
// 1 v i eq
|
||||
== req && req
|
||||
// teq i
|
||||
if
|
||||
{
|
||||
1
|
||||
}
|
||||
else
|
||||
{
|
||||
0
|
||||
}
|
||||
field2.write
|
||||
}
|
||||
}
|
||||
|
||||
function int int => int neighbourhood
|
||||
{
|
||||
// x y
|
||||
dup 1 req req -
|
||||
// x y x-1
|
||||
0 req req dup >=
|
||||
// x y x-1 ge
|
||||
req req req
|
||||
// ge x y x-1
|
||||
if
|
||||
{
|
||||
req dup swp
|
||||
// x y x-1 y
|
||||
req req
|
||||
// x-1 y x y
|
||||
row
|
||||
// x y abc
|
||||
}
|
||||
else
|
||||
{
|
||||
// x y x-1
|
||||
req req
|
||||
// x-1 x y
|
||||
deq 0
|
||||
// x y 0
|
||||
}
|
||||
dup swp req req
|
||||
// y x abc x
|
||||
dup swp req row
|
||||
// x y abc def
|
||||
req req +
|
||||
// x y abcdef
|
||||
|
||||
dup swp req req
|
||||
// y x abcdef x
|
||||
dup swp req
|
||||
// x y x y abcdef
|
||||
coordToIndex
|
||||
// x y abcdef i
|
||||
req req req
|
||||
// i x y abcdef
|
||||
field.read
|
||||
// x y abcdef v
|
||||
req req -
|
||||
// x y abcdef-v
|
||||
|
||||
1 req req req
|
||||
// 1 x y abcdef
|
||||
+ req req
|
||||
// x+1 y abcdef
|
||||
dup 29 req req req
|
||||
// x+1 l x+1 y abcdef
|
||||
< req req req
|
||||
// lt x+1 y abcdef
|
||||
if
|
||||
{
|
||||
row
|
||||
// abcdef ghi
|
||||
}
|
||||
else
|
||||
{
|
||||
deq deq 0
|
||||
// abcdef 0
|
||||
}
|
||||
+
|
||||
// abcdefghi
|
||||
}
|
||||
|
||||
function int int => int row
|
||||
{
|
||||
// x y
|
||||
1 req dup -
|
||||
// x y y-1
|
||||
dup req req
|
||||
// y-1 x x y
|
||||
0 dup swp req req
|
||||
// 0 y-1 x y-1 x y
|
||||
> req req req req
|
||||
// gt x y-1 x y
|
||||
if
|
||||
{
|
||||
deq deq 0
|
||||
// x y 0
|
||||
}
|
||||
else
|
||||
{
|
||||
// x y-1 x y
|
||||
coordToIndex req req
|
||||
// i x y
|
||||
field.read
|
||||
// x y a
|
||||
}
|
||||
// x y a
|
||||
dup swp req req
|
||||
// y x a x
|
||||
dup swp req
|
||||
// x y x y a
|
||||
coordToIndex
|
||||
// x y a i
|
||||
req req req field.read
|
||||
// x y a b
|
||||
req req +
|
||||
// x y a+b
|
||||
req 1 req
|
||||
// a+b x 1 y
|
||||
req req + req
|
||||
// x y+1 a+b
|
||||
req req 29
|
||||
// a+b x y+1 l
|
||||
req req dup
|
||||
// y+1 l a+b x y+1
|
||||
< req req req
|
||||
// lt a+b x y+1
|
||||
if
|
||||
{
|
||||
// a+b x y+1
|
||||
req coordToIndex
|
||||
// a+b i
|
||||
req field.read
|
||||
// a+b c
|
||||
}
|
||||
else
|
||||
{
|
||||
req deq deq 0
|
||||
// a+b 0
|
||||
}
|
||||
+
|
||||
// a+b+c
|
||||
}
|
||||
|
||||
function int int => int coordToIndex
|
||||
{
|
||||
// y x
|
||||
req 29 req * +
|
||||
}
|
8
tests/invalid_function_name_intrinsic.qbl
Normal file
8
tests/invalid_function_name_intrinsic.qbl
Normal file
@ -0,0 +1,8 @@
|
||||
//invalid,Function name print at 3:22 is already an intrinsic:END:
|
||||
|
||||
function str => print
|
||||
{
|
||||
deq
|
||||
}
|
||||
|
||||
"42" print
|
8
tests/invalid_function_name_operation.qbl
Normal file
8
tests/invalid_function_name_operation.qbl
Normal file
@ -0,0 +1,8 @@
|
||||
//invalid,Expected function name but got deq at 3:20:END:
|
||||
|
||||
function str => deq
|
||||
{
|
||||
deq
|
||||
}
|
||||
|
||||
"42" print
|
8
tests/missing_function_name.qbl
Normal file
8
tests/missing_function_name.qbl
Normal file
@ -0,0 +1,8 @@
|
||||
//invalid,Expected function name but got { at 4:2:END:
|
||||
|
||||
function str =>
|
||||
{
|
||||
deq
|
||||
}
|
||||
|
||||
"42" print
|
17
tests/recursion.qbl
Normal file
17
tests/recursion.qbl
Normal file
@ -0,0 +1,17 @@
|
||||
//valid,10946
|
||||
//:END:
|
||||
|
||||
function int int int int => int fibonacci
|
||||
{
|
||||
dup 0 == req req req req
|
||||
if
|
||||
{
|
||||
req deq deq deq
|
||||
}
|
||||
else
|
||||
{
|
||||
dup + req 1 req swp - req req req fibonacci
|
||||
}
|
||||
}
|
||||
|
||||
20 0 1 0 fibonacci intToStr println
|
8
tests/recursive_import.qbl
Normal file
8
tests/recursive_import.qbl
Normal file
@ -0,0 +1,8 @@
|
||||
//valid,:END:
|
||||
import "recursive_import_2.qbl"
|
||||
import "recursive_import.qbl"
|
||||
|
||||
function => test
|
||||
{
|
||||
"test" println
|
||||
}
|
5
tests/recursive_import_2.qbl
Normal file
5
tests/recursive_import_2.qbl
Normal file
@ -0,0 +1,5 @@
|
||||
//valid,test
|
||||
//:END:
|
||||
import "recursive_import.qbl"
|
||||
|
||||
test
|
8
tests/req_impl.qbl
Normal file
8
tests/req_impl.qbl
Normal file
@ -0,0 +1,8 @@
|
||||
//valid,231
|
||||
//231
|
||||
//:END:
|
||||
|
||||
function int => int req_impl { }
|
||||
|
||||
1 2 3 req_impl intToStr intToStr intToStr print print println
|
||||
1 2 3 req intToStr intToStr intToStr print print println
|
159
tests/rule110.qbl
Normal file
159
tests/rule110.qbl
Normal file
@ -0,0 +1,159 @@
|
||||
//valid,---------------------------------#
|
||||
//--------------------------------##
|
||||
//-------------------------------###
|
||||
//------------------------------##-#
|
||||
//-----------------------------#####
|
||||
//----------------------------##---#
|
||||
//---------------------------###--##
|
||||
//--------------------------##-#-###
|
||||
//-------------------------#######-#
|
||||
//------------------------##-----###
|
||||
//-----------------------###----##-#
|
||||
//----------------------##-#---#####
|
||||
//---------------------#####--##---#
|
||||
//--------------------##---#-###--##
|
||||
//-------------------###--####-#-###
|
||||
//------------------##-#-##--#####-#
|
||||
//-----------------########-##---###
|
||||
//----------------##------####--##-#
|
||||
//---------------###-----##--#-#####
|
||||
//--------------##-#----###-####---#
|
||||
//-------------#####---##-###--#--##
|
||||
//------------##---#--#####-#-##-###
|
||||
//-----------###--##-##---########-#
|
||||
//----------##-#-######--##------###
|
||||
//---------#######----#-###-----##-#
|
||||
//--------##-----#---####-#----#####
|
||||
//-------###----##--##--###---##---#
|
||||
//------##-#---###-###-##-#--###--##
|
||||
//-----#####--##-###-######-##-#-###
|
||||
//----##---#-#####-###----########-#
|
||||
//---###--####---###-#---##------###
|
||||
//--##-#-##--#--##-###--###-----##-#
|
||||
//-########-##-#####-#-##-#----#####
|
||||
//##------######---########---##---#
|
||||
//##-----##----#--##------#--###--##
|
||||
//:END:
|
||||
function bool bool bool => bool rule110
|
||||
{
|
||||
if
|
||||
{
|
||||
if
|
||||
{
|
||||
if
|
||||
{
|
||||
false
|
||||
}
|
||||
else
|
||||
{
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if
|
||||
{
|
||||
deq true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arr val { bool 34 }
|
||||
|
||||
33 true val.write
|
||||
printArrays
|
||||
|
||||
function int => bool int check
|
||||
{
|
||||
0 dup 1 > -
|
||||
}
|
||||
|
||||
function => fullApply
|
||||
{
|
||||
// 0 b c
|
||||
0 1 false val.read val.read
|
||||
0 rule110 val2.write
|
||||
val.length 1 - 1 dup - req
|
||||
val.read val.read false
|
||||
rule110 val.length 1
|
||||
req - req val2.write
|
||||
true 1
|
||||
while
|
||||
{
|
||||
// i-1 i
|
||||
1 dup - req
|
||||
// i a | i a i | a i b | i b a i 1
|
||||
val.read dup val.read req dup 1
|
||||
// i a b i+1 | i+1 i a b | i a b c
|
||||
req swp + req req req val.read
|
||||
// i d
|
||||
req rule110
|
||||
// i d i | i
|
||||
dup val2.write
|
||||
checkUp-1
|
||||
}
|
||||
deq
|
||||
}
|
||||
|
||||
function int => bool int checkUp
|
||||
{
|
||||
// i+1 l i+1
|
||||
1 + val.length dup < req
|
||||
}
|
||||
|
||||
function int => bool int checkUp-1
|
||||
{
|
||||
// i+1 l 1 | l 1 i+1 | i+1 l-1 i+1
|
||||
1 + val.length 1 req - dup
|
||||
// i+1 b
|
||||
< req
|
||||
}
|
||||
|
||||
arr val2 { bool 34 }
|
||||
|
||||
function => copyArrays
|
||||
{
|
||||
true 0
|
||||
while
|
||||
{
|
||||
dup val2.read
|
||||
dup val.write
|
||||
checkUp
|
||||
}
|
||||
deq
|
||||
}
|
||||
|
||||
function bool => str boolToSym
|
||||
{
|
||||
if
|
||||
{
|
||||
"#"
|
||||
}
|
||||
else
|
||||
{
|
||||
"-"
|
||||
}
|
||||
}
|
||||
|
||||
function => printArrays
|
||||
{
|
||||
true 0
|
||||
while
|
||||
{
|
||||
dup val.read req boolToSym req print
|
||||
checkUp
|
||||
}
|
||||
deq
|
||||
"" println
|
||||
}
|
||||
|
||||
true 33
|
||||
while
|
||||
{
|
||||
fullApply
|
||||
copyArrays
|
||||
printArrays
|
||||
check
|
||||
}
|
||||
deq
|
42
tests/test.qbl
Normal file
42
tests/test.qbl
Normal file
@ -0,0 +1,42 @@
|
||||
//valid,Hello, World!
|
||||
//4242test2Falsetesttesttest:END:
|
||||
|
||||
"Hello, World!" println 43 foo foo deq
|
||||
|
||||
|
||||
// Dequeues, enqueues 42 and 17, prints the head
|
||||
function int => int foo
|
||||
{
|
||||
deq 42 17 intToStr req print
|
||||
}
|
||||
|
||||
"test2" print false
|
||||
check
|
||||
print
|
||||
|
||||
|
||||
function bool => str check
|
||||
{
|
||||
if
|
||||
{
|
||||
"True"
|
||||
}
|
||||
else
|
||||
{
|
||||
"False"
|
||||
}
|
||||
}
|
||||
|
||||
function int => whileFunction
|
||||
{
|
||||
dup
|
||||
0 req >
|
||||
req while
|
||||
{
|
||||
1 - 0 dup >
|
||||
"test" req req print req
|
||||
}
|
||||
deq
|
||||
}
|
||||
|
||||
3 whileFunction
|
5
tests/transitive_import.qbl
Normal file
5
tests/transitive_import.qbl
Normal file
@ -0,0 +1,5 @@
|
||||
//valid,false
|
||||
//:END:
|
||||
import "basic_import.qbl"
|
||||
|
||||
false boolToStr println
|
9
tests/typecheck_function_multiple_io.qbl
Normal file
9
tests/typecheck_function_multiple_io.qbl
Normal file
@ -0,0 +1,9 @@
|
||||
//valid,42footesttest2stuff
|
||||
//:END:
|
||||
|
||||
function int str str => str str str foo
|
||||
{
|
||||
intToStr req req print req deq "test" "test2" "stuff" print
|
||||
}
|
||||
|
||||
42 "foo" "bar" foo print print println
|
2
tests/unknown_function_basic.qbl
Normal file
2
tests/unknown_function_basic.qbl
Normal file
@ -0,0 +1,2 @@
|
||||
//invalid,Call to unknown function 'foo' at 2:4:END:
|
||||
foo
|
19
tests/unknown_function_hard.qbl
Normal file
19
tests/unknown_function_hard.qbl
Normal file
@ -0,0 +1,19 @@
|
||||
//invalid,Call to unknown function 'bar' at 9:7:END:
|
||||
|
||||
function int => foo
|
||||
{
|
||||
while
|
||||
{
|
||||
if
|
||||
{
|
||||
bar
|
||||
}
|
||||
else
|
||||
{
|
||||
1 -
|
||||
}
|
||||
}
|
||||
deq
|
||||
}
|
||||
|
||||
42 foo
|
8
tests/unknown_function_in_function.qbl
Normal file
8
tests/unknown_function_in_function.qbl
Normal file
@ -0,0 +1,8 @@
|
||||
//invalid,Call to unknown function 'bar' at 5:5:END:
|
||||
|
||||
function => foo
|
||||
{
|
||||
bar
|
||||
}
|
||||
|
||||
foo
|
25
tests/while.qbl
Normal file
25
tests/while.qbl
Normal file
@ -0,0 +1,25 @@
|
||||
//valid,10987654321falsefalse
|
||||
//:END:
|
||||
|
||||
import "../std.qbl"
|
||||
|
||||
true while
|
||||
{
|
||||
false
|
||||
}
|
||||
|
||||
10 0 dup > req
|
||||
// true 10
|
||||
while
|
||||
{
|
||||
// i
|
||||
dup intToStr req print
|
||||
1 - 0 dup > req
|
||||
}
|
||||
deq
|
||||
|
||||
true true true while
|
||||
{
|
||||
false
|
||||
}
|
||||
boolToStr boolToStr print println
|
Reference in New Issue
Block a user