Initial commit
This commit is contained in:
commit
a005400477
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
/archive
|
||||
/bin
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "labast"
|
||||
version = "1.0.1"
|
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "labast"
|
||||
version = "1.0.1"
|
||||
authors = [ "n3tael" ]
|
||||
edition = "2021"
|
||||
description = "A zero-dependencies Labaski interpreter written in Rust."
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
publish = false
|
||||
|
||||
[profile.dev]
|
||||
overflow-checks = false
|
||||
|
||||
[dependencies]
|
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 n3tael
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Labast
|
||||
A zero-dependencies Labaski interpreter written in Rust. Fully supports [Labashki specs 1.4.1](./SPECIFICATION.md).
|
||||
|
||||
## Examples
|
||||
See `example-scripts` directory.
|
51
SPECIFICATION.md
Normal file
51
SPECIFICATION.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Labaski specification
|
||||
Last update: 2024-02-05
|
||||
|
||||
Программа состоит из команд в формате `ИНСТРУКЦИЯ АРГУМЕНТ` (assembler-like)
|
||||
|
||||
Аргумент всегда должен быть `uint16` (`unsigned short`, `u16`)
|
||||
|
||||
Все данные которые программа использует хранятся в стеке, сам стек это массив из этих `unsigned short`'ов
|
||||
|
||||
Размер стека без разницы, но желательно минимум 256
|
||||
|
||||
Выполнение программы начинается либо с начала файла, либо с лейбла 0, если он есть
|
||||
|
||||
Почти каждая инструкция которые читает что-то стека должна POP'ать прочитаннное значение
|
||||
|
||||
Если название инструкции начинается с #, то парсер программы должен прочитать аргумент к инструкции как строку(до первого пробела)
|
||||
|
||||
## Инструкции
|
||||
|
||||
| Инструкция | Аргумент | Описание |
|
||||
|-------------|----------------|----------------------------------------------------------------------|
|
||||
| PUSH | `uint16` | Добавить в стек |
|
||||
| POP | - | Удалить из стека |
|
||||
| DUP | - | Дублировать последнее значение в стеке |
|
||||
| SWAP | - | Поменять местами последние 2 значения в стеке |
|
||||
| ADD | - | Прибавление последних 2-х значений из стека |
|
||||
| SUB | - | Вычитание последних 2-х значений из стека |
|
||||
| MUL | - | Умножение последних 2-х значений из стека |
|
||||
| DIV | - | Деление последних 2-х значений из стека |
|
||||
| JMP | `uint16` лейбл | Перейти к лейблу <имя> |
|
||||
| JNZ | `uint16` лейбл | Перейти к лейблу <имя> если значение с верхушки стека не 0 |
|
||||
| JZ | `uint16` лейбл | Перейти к лейблу <имя> если значение с верхушки стека 0 |
|
||||
| NOP | - | Ничего не делает |
|
||||
| EXIT | - | Выход из программы с кодом 0 |
|
||||
| PUTC | - | Вывести символ в консоль |
|
||||
| GETC | - | Получить символ с консоли, добавить символ в стек |
|
||||
| MEOW | - | Принт, POP'ает со стека |
|
||||
| DUMP | - | Вывести весь стек в консоль, понятное дело не попает со стека |
|
||||
| SCAN | - | Сканирует ввод из консоли на `short`'ы и пушит в стек |
|
||||
| #EXEC | arg | Выполнить код из файла, указанного в аргументе* |
|
||||
| ARGS | `uint16` | POP'ает аргумент значений с основного стека и пушит их в стек модуля, если указать аргумент 0, то нужное кол-во аргументов она возьмет(попнув) с оригинального стека |
|
||||
| SIZE | | Пушит в стек значение размера стека |
|
||||
| QUIT | `uint16` | Полностью завершает выполнение программы. Как аргумент попает exit-код. |
|
||||
|
||||
> \*путь относительно места вызова интерпретатора. Создает отдельный стек для модуля, но передает основной для того чтобы взять оттуда аргументы. После того как модуль выполнился, пушит весь стек модуля в основной.
|
||||
|
||||
## Лейблы
|
||||
|
||||
Лейблы могут быть только `unsigned short`'ами
|
||||
От 0 (main-лейбл) до минимум 256
|
||||
Если юзер высрал два лейбла с одинаковыми идентификаторами, то интерпретатор/компилятор должен выдать ошибку
|
5
build.bat
Normal file
5
build.bat
Normal file
@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
cargo build --release
|
||||
cargo zigbuild --release --target x86_64-unknown-linux-gnu
|
||||
copy target\release\labast.exe bin\ /Y
|
||||
copy target\x86_64-unknown-linux-gnu\release\labast bin\ /Y
|
9
example-scripts/expr.lb
Normal file
9
example-scripts/expr.lb
Normal file
@ -0,0 +1,9 @@
|
||||
; written by Labashki developer - aeris
|
||||
@ 0
|
||||
#EXPR >++:+\
|
||||
DUMP
|
||||
|
||||
; Expected output
|
||||
;
|
||||
; 0: 3
|
||||
; 1: 2
|
30
example-scripts/hello-world.lb
Normal file
30
example-scripts/hello-world.lb
Normal file
@ -0,0 +1,30 @@
|
||||
PUSH 72 ; H
|
||||
PUTC ; Print to console
|
||||
PUSH 101 ; e
|
||||
PUTC
|
||||
PUSH 108 ; l
|
||||
PUTC
|
||||
PUSH 108 ; l
|
||||
PUTC
|
||||
PUSH 111 ; o
|
||||
PUTC
|
||||
PUSH 32 ; <space>
|
||||
PUTC
|
||||
PUSH 76 ; L
|
||||
PUTC
|
||||
PUSH 97 ; a
|
||||
PUTC
|
||||
PUSH 98 ; b
|
||||
PUTC
|
||||
PUSH 97 ; a
|
||||
PUTC
|
||||
PUSH 115 ; s
|
||||
PUTC
|
||||
PUSH 104 ; h
|
||||
PUTC
|
||||
PUSH 107 ; k
|
||||
PUTC
|
||||
PUSH 105 ; i
|
||||
PUTC
|
||||
PUSH 33 ; !
|
||||
PUTC
|
20
example-scripts/labels.lb
Normal file
20
example-scripts/labels.lb
Normal file
@ -0,0 +1,20 @@
|
||||
@ 1
|
||||
PUSH 115 ; s
|
||||
PUTC
|
||||
PUSH 104 ; h
|
||||
PUTC
|
||||
PUSH 107 ; k
|
||||
PUTC
|
||||
PUSH 105 ; i
|
||||
PUTC
|
||||
EXIT
|
||||
@ 0
|
||||
PUSH 76 ; L
|
||||
PUTC
|
||||
PUSH 97 ; a
|
||||
PUTC
|
||||
PUSH 98 ; b
|
||||
PUTC
|
||||
PUSH 97 ; a
|
||||
PUTC
|
||||
JMP 1
|
23
example-scripts/math.lb
Normal file
23
example-scripts/math.lb
Normal file
@ -0,0 +1,23 @@
|
||||
; Addition
|
||||
PUSH 2 ; a = 2
|
||||
PUSH 3 ; b = 2
|
||||
ADD ; a + b
|
||||
MEOW ; = 5
|
||||
|
||||
; Subtraction
|
||||
PUSH 9 ; a = 9
|
||||
PUSH 4 ; b = 4
|
||||
SUB ; b - a
|
||||
MEOW ; = 5
|
||||
|
||||
; Multiplication
|
||||
PUSH 9 ; a = 9
|
||||
PUSH 5 ; b = 5
|
||||
MUL ; b * a
|
||||
MEOW ; = 45
|
||||
|
||||
; Division
|
||||
PUSH 81; a = 81
|
||||
PUSH 9 ; b = 9
|
||||
DIV ; b / a
|
||||
MEOW ; = 9
|
12
example-scripts/modules/main.lb
Normal file
12
example-scripts/modules/main.lb
Normal file
@ -0,0 +1,12 @@
|
||||
PUSH 76 ; L
|
||||
PUTC
|
||||
PUSH 97 ; a
|
||||
PUTC
|
||||
PUSH 98 ; b
|
||||
PUTC
|
||||
PUSH 97 ; a
|
||||
PUTC
|
||||
#EXEC second_part.lb
|
||||
PUSH 33 ; !
|
||||
PUTC
|
||||
QUIT
|
8
example-scripts/modules/second_part.lb
Normal file
8
example-scripts/modules/second_part.lb
Normal file
@ -0,0 +1,8 @@
|
||||
PUSH 115 ; s
|
||||
PUTC
|
||||
PUSH 104 ; h
|
||||
PUTC
|
||||
PUSH 107 ; k
|
||||
PUTC
|
||||
PUSH 105 ; i
|
||||
PUTC
|
15
example-scripts/pick.lb
Normal file
15
example-scripts/pick.lb
Normal file
@ -0,0 +1,15 @@
|
||||
; written by Labashki developer - aeris
|
||||
@ 0
|
||||
PUSH 2
|
||||
PUSH 3
|
||||
PUSH 4
|
||||
PUSH 1
|
||||
PICK
|
||||
DUMP
|
||||
|
||||
; Expected output
|
||||
;
|
||||
; 0: 2
|
||||
; 1: 3
|
||||
; 2: 4
|
||||
; 3: 3
|
10
example-scripts/size.lb
Normal file
10
example-scripts/size.lb
Normal file
@ -0,0 +1,10 @@
|
||||
@ 0
|
||||
PUSH 1
|
||||
PUSH 2
|
||||
PUSH 3
|
||||
SIZE
|
||||
MEOW
|
||||
|
||||
; Expected output
|
||||
;
|
||||
; 3
|
45
src/errors.rs
Normal file
45
src/errors.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
pub enum RunError {
|
||||
RequestArgsInMainModule,
|
||||
ExecuteItself(String),
|
||||
FailToReadFile,
|
||||
FailToGetCharFromConsole,
|
||||
InvalidInputUShortInt,
|
||||
PickTooDeep,
|
||||
PickOutOfBounds,
|
||||
FailToReadLineFromConsole,
|
||||
UnknownLabel(u16),
|
||||
InvalidExpressionUnknownOperator(String),
|
||||
MemoryEmpty,
|
||||
}
|
||||
|
||||
impl Display for RunError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
RunError::RequestArgsInMainModule => write!(f, "Can't require arguments in the main module"),
|
||||
RunError::ExecuteItself(mod_name) => write!(f, "Module {}: Can't execute itself", mod_name),
|
||||
RunError::FailToReadFile => write!(f, "Unable to read file"),
|
||||
RunError::FailToGetCharFromConsole => write!(f, "Failed to get character from console"),
|
||||
RunError::InvalidInputUShortInt => write!(f, "Invalid input. Please enter a valid unsigned short integer"),
|
||||
RunError::PickTooDeep => write!(f, "Picking too deep"),
|
||||
RunError::PickOutOfBounds => write!(f, "Trying to get a value out of bounds"),
|
||||
RunError::FailToReadLineFromConsole => write!(f, "Failed to read line from console"),
|
||||
RunError::UnknownLabel(label_name) => write!(f, "Unknown label: {}", label_name),
|
||||
RunError::InvalidExpressionUnknownOperator(name) => write!(f, "Invalid expression: unknown operator '{}'", name),
|
||||
RunError::MemoryEmpty => write!(f, "No elements in memory!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ParseError {
|
||||
DataNotAUInt(String, usize)
|
||||
}
|
||||
|
||||
impl Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ParseError::DataNotAUInt(name, line) => write!(f, "Argument for {0} at {1} isn't a number", name, line),
|
||||
}
|
||||
}
|
||||
}
|
86
src/execute.rs
Normal file
86
src/execute.rs
Normal file
@ -0,0 +1,86 @@
|
||||
use crate::{instructions, stack::Stack};
|
||||
|
||||
fn find_labels(stack: &mut Stack) {
|
||||
while stack.program_counter < (stack.program.len() as u16) {
|
||||
let label = &stack.program[stack.program_counter as usize];
|
||||
stack.program_counter += 1;
|
||||
|
||||
if !label.name.starts_with('@') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if stack.labels[label.data as usize].is_some() {
|
||||
eprintln!("Label {} already defined", label.data);
|
||||
return;
|
||||
}
|
||||
|
||||
if label.data == 0 {
|
||||
stack.labels[0] = Some(stack.program_counter as i16);
|
||||
continue;
|
||||
}
|
||||
|
||||
stack.labels[label.data as usize] = Some(stack.program_counter as i16);
|
||||
}
|
||||
|
||||
if stack.labels[0].is_some() {
|
||||
stack.program_counter = stack.labels[0].unwrap() as u16;
|
||||
} else {
|
||||
stack.program_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(stack: &mut Stack, mut origin_stack: Option<&mut Stack>, mod_name: &String) {
|
||||
find_labels(stack);
|
||||
|
||||
while let Some(instruction) = stack.program.get(stack.program_counter as usize) {
|
||||
stack.program_counter += 1;
|
||||
|
||||
if instruction.name.starts_with('@') {
|
||||
continue;
|
||||
}
|
||||
|
||||
match instruction.name.as_str() {
|
||||
// Stack operations
|
||||
"PUSH" => instructions::push::push(&mut stack.memory, &instruction.data),
|
||||
"PICK" => instructions::pick::pick(&mut stack.memory, &instruction.data),
|
||||
"POP" => instructions::pop::pop(&mut stack.memory),
|
||||
"DUP" => instructions::dup::dup(&mut stack.memory),
|
||||
"SWAP" => instructions::swap::swap(&mut stack.memory),
|
||||
"MEOW" => instructions::meow::meow(&mut stack.memory),
|
||||
"DUMP" => instructions::dump::dump(&mut stack.memory),
|
||||
"SIZE" => instructions::size::size(&mut stack.memory),
|
||||
|
||||
"#EXPR" => instructions::expr::expr(&mut stack.memory, &instruction.arg),
|
||||
|
||||
// Labels
|
||||
"JMP" => instructions::jmp::jmp(&mut stack.labels, &mut stack.program_counter, &instruction.data),
|
||||
"JNZ" => instructions::jnz::jnz(&mut stack.memory, &mut stack.labels, &mut stack.program_counter, &instruction.data),
|
||||
"JZ" => instructions::jz::jz(&mut stack.memory, &mut stack.labels, &mut stack.program_counter, &instruction.data),
|
||||
|
||||
// Math operations
|
||||
"ADD" => instructions::add::add(&mut stack.memory),
|
||||
"SUB" => instructions::sub::sub(&mut stack.memory),
|
||||
"MUL" => instructions::mul::mul(&mut stack.memory),
|
||||
"DIV" => instructions::div::div(&mut stack.memory),
|
||||
|
||||
// Console operations
|
||||
"PUTC" => instructions::putc::putc(&mut stack.memory),
|
||||
"GETC" => instructions::getc::getc(&mut stack.memory),
|
||||
"SCAN" => instructions::scan::scan(&mut stack.memory),
|
||||
|
||||
// Modules
|
||||
"ARGS" => instructions::args::args(&mut stack.memory, &mut origin_stack, &instruction.data),
|
||||
"#EXEC" => instructions::exec::exec(stack, &instruction.arg.clone(), &mod_name),
|
||||
|
||||
// ?
|
||||
"NOP" => continue,
|
||||
"EXIT" => instructions::exit::exit(&instruction.data),
|
||||
"QUIT" => std::process::exit(0),
|
||||
|
||||
_ => {
|
||||
eprintln!("Unknown instruction: {}", instruction.name);
|
||||
std::process::exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
src/instructions/add.rs
Normal file
8
src/instructions/add.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn add(memory: &mut Vec<u16>) {
|
||||
let a: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
let b: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
|
||||
memory.push(a + b);
|
||||
}
|
21
src/instructions/args.rs
Normal file
21
src/instructions/args.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use crate::{errors::RunError, stack::Stack};
|
||||
|
||||
pub fn args(memory: &mut Vec<u16>, origin_stack: &mut Option<&mut Stack>, data: &u16) {
|
||||
let origin_memory = &mut origin_stack.as_mut().expect("msg").memory;
|
||||
|
||||
if *origin_memory == *memory {
|
||||
eprintln!("{}", RunError::RequestArgsInMainModule);
|
||||
std::process::exit(2);
|
||||
}
|
||||
|
||||
let quanity: u16;
|
||||
if *data == 0 {
|
||||
quanity = origin_memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
} else {
|
||||
quanity = *data;
|
||||
}
|
||||
|
||||
for _ in 0..quanity {
|
||||
memory.push(origin_memory.pop().expect(&format!("{}", RunError::MemoryEmpty)));
|
||||
}
|
||||
}
|
8
src/instructions/div.rs
Normal file
8
src/instructions/div.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn div(memory: &mut Vec<u16>) {
|
||||
let a: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
let b: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
|
||||
memory.push(b / a);
|
||||
}
|
5
src/instructions/dump.rs
Normal file
5
src/instructions/dump.rs
Normal file
@ -0,0 +1,5 @@
|
||||
pub fn dump(memory: &mut Vec<u16>) {
|
||||
for (i, entry) in memory.iter().enumerate() {
|
||||
println!("{0}: {1}", i, entry);
|
||||
}
|
||||
}
|
5
src/instructions/dup.rs
Normal file
5
src/instructions/dup.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn dup(memory: &mut Vec<u16>) {
|
||||
memory.push(*memory.last().expect(&format!("{}", RunError::MemoryEmpty)));
|
||||
}
|
20
src/instructions/exec.rs
Normal file
20
src/instructions/exec.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use std::fs;
|
||||
use crate::{parse, execute, Stack, errors::RunError};
|
||||
|
||||
pub fn exec(mut stack: &mut Stack, arg: &String, mod_name: &String) {
|
||||
if mod_name == arg {
|
||||
eprintln!("{}", RunError::ExecuteItself(arg.to_string()));
|
||||
std::process::exit(2);
|
||||
}
|
||||
|
||||
let lines = fs::read_to_string(&arg).expect(&format!("{}", RunError::FailToReadFile));
|
||||
let mut temp_stack = Stack::new();
|
||||
|
||||
parse(&mut temp_stack, &lines);
|
||||
|
||||
execute(&mut temp_stack, Some(&mut stack), &arg.clone());
|
||||
|
||||
for item in temp_stack.memory {
|
||||
stack.memory.push(item);
|
||||
}
|
||||
}
|
3
src/instructions/exit.rs
Normal file
3
src/instructions/exit.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub fn exit(data: &u16) {
|
||||
std::process::exit((*data).into());
|
||||
}
|
31
src/instructions/expr.rs
Normal file
31
src/instructions/expr.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn expr(memory: &mut Vec<u16>, arg: &String) {
|
||||
for code in arg.chars() {
|
||||
match code {
|
||||
'>' => memory.push(0),
|
||||
'$' => { memory.pop(); },
|
||||
':' => memory.push(*memory.last().expect(&format!("{}", RunError::MemoryEmpty))),
|
||||
'\\' => {
|
||||
let ax = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
let ax2 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
|
||||
memory.push(ax);
|
||||
memory.push(ax2);
|
||||
}
|
||||
'+' => {
|
||||
let ax = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
memory.push(ax + 1);
|
||||
}
|
||||
'-' => {
|
||||
let ax = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
memory.push(ax - 1);
|
||||
}
|
||||
|
||||
_ => {
|
||||
eprintln!("{}", RunError::InvalidExpressionUnknownOperator(code.to_string()));
|
||||
std::process::exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
src/instructions/getc.rs
Normal file
13
src/instructions/getc.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use std::io::Read;
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn getc(memory: &mut Vec<u16>) {
|
||||
let char = std::io::stdin()
|
||||
.bytes()
|
||||
.next()
|
||||
.and_then(|result| result.ok())
|
||||
.map(|byte| byte as u16)
|
||||
.expect(&format!("{}", RunError::FailToGetCharFromConsole));
|
||||
|
||||
memory.push(char);
|
||||
}
|
10
src/instructions/jmp.rs
Normal file
10
src/instructions/jmp.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn jmp(labels: &mut [Option<i16>; 256], program_counter: &mut u16, data: &u16) {
|
||||
if labels[*data as usize].is_none() {
|
||||
eprintln!("{}", RunError::UnknownLabel(*data));
|
||||
std::process::exit(2);
|
||||
}
|
||||
|
||||
*program_counter = (labels[*data as usize].unwrap() - 1) as u16;
|
||||
}
|
12
src/instructions/jnz.rs
Normal file
12
src/instructions/jnz.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn jnz(memory: &mut Vec<u16>, labels: &mut [Option<i16>; 256], program_counter: &mut u16, data: &u16) {
|
||||
if labels[*data as usize].is_none() {
|
||||
eprintln!("{}", RunError::UnknownLabel(*data));
|
||||
std::process::exit(2);
|
||||
}
|
||||
|
||||
if memory.pop() != Some(0) {
|
||||
*program_counter = (labels[*data as usize].unwrap() - 1) as u16;
|
||||
}
|
||||
}
|
12
src/instructions/jz.rs
Normal file
12
src/instructions/jz.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn jz(memory: &mut Vec<u16>, labels: &mut [Option<i16>; 256], program_counter: &mut u16, data: &u16) {
|
||||
if labels[*data as usize].is_none() {
|
||||
eprintln!("{}", RunError::UnknownLabel(*data));
|
||||
std::process::exit(2);
|
||||
}
|
||||
|
||||
if memory.pop() == Some(0) {
|
||||
*program_counter = (labels[*data as usize].unwrap() - 1) as u16;
|
||||
}
|
||||
}
|
5
src/instructions/meow.rs
Normal file
5
src/instructions/meow.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn meow(memory: &mut Vec<u16>) {
|
||||
println!("{}", memory.pop().expect(&format!("{}", RunError::MemoryEmpty)));
|
||||
}
|
22
src/instructions/mod.rs
Normal file
22
src/instructions/mod.rs
Normal file
@ -0,0 +1,22 @@
|
||||
pub mod push;
|
||||
pub mod pop;
|
||||
pub mod dup;
|
||||
pub mod swap;
|
||||
pub mod add;
|
||||
pub mod sub;
|
||||
pub mod mul;
|
||||
pub mod div;
|
||||
pub mod jmp;
|
||||
pub mod jnz;
|
||||
pub mod jz;
|
||||
pub mod pick;
|
||||
pub mod putc;
|
||||
pub mod getc;
|
||||
pub mod meow;
|
||||
pub mod dump;
|
||||
pub mod scan;
|
||||
pub mod exec;
|
||||
pub mod args;
|
||||
pub mod expr;
|
||||
pub mod size;
|
||||
pub mod exit;
|
8
src/instructions/mul.rs
Normal file
8
src/instructions/mul.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn mul(memory: &mut Vec<u16>) {
|
||||
let a: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
let b: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
|
||||
memory.push(b * a);
|
||||
}
|
11
src/instructions/pick.rs
Normal file
11
src/instructions/pick.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn pick(memory: &mut Vec<u16>, data: &u16) {
|
||||
if *data > memory.len() as u16 {
|
||||
eprintln!("{}", RunError::PickTooDeep);
|
||||
std::process::exit(2);
|
||||
}
|
||||
|
||||
let arg = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
memory.push(*memory.get(memory.len() - (arg as usize) - 1).expect(&format!("{}", RunError::PickOutOfBounds)));
|
||||
}
|
3
src/instructions/pop.rs
Normal file
3
src/instructions/pop.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub fn pop(memory: &mut Vec<u16>) {
|
||||
memory.pop();
|
||||
}
|
3
src/instructions/push.rs
Normal file
3
src/instructions/push.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub fn push(memory: &mut Vec<u16>, data: &u16) {
|
||||
memory.push(*data);
|
||||
}
|
5
src/instructions/putc.rs
Normal file
5
src/instructions/putc.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn putc(memory: &mut Vec<u16>) {
|
||||
print!("{}", memory.pop().expect(&format!("{}", RunError::MemoryEmpty)) as u8 as char);
|
||||
}
|
10
src/instructions/scan.rs
Normal file
10
src/instructions/scan.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn scan(memory: &mut Vec<u16>) {
|
||||
let mut input = String::new();
|
||||
|
||||
std::io::stdin().read_line(&mut input).expect(&format!("{}", RunError::FailToReadLineFromConsole));
|
||||
|
||||
let num = input.trim().parse().expect(&format!("{}", RunError::InvalidInputUShortInt));
|
||||
memory.push(num);
|
||||
}
|
3
src/instructions/size.rs
Normal file
3
src/instructions/size.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub fn size(memory: &mut Vec<u16>) {
|
||||
memory.push(memory.len() as u16);
|
||||
}
|
8
src/instructions/sub.rs
Normal file
8
src/instructions/sub.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn sub(memory: &mut Vec<u16>) {
|
||||
let a: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
let b: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
|
||||
memory.push(b - a);
|
||||
}
|
9
src/instructions/swap.rs
Normal file
9
src/instructions/swap.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use crate::errors::RunError;
|
||||
|
||||
pub fn swap(memory: &mut Vec<u16>) {
|
||||
let a: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
let b: u16 = memory.pop().expect(&format!("{}", RunError::MemoryEmpty));
|
||||
|
||||
memory.push(a);
|
||||
memory.push(b);
|
||||
}
|
27
src/main.rs
Normal file
27
src/main.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use std::{env, fs};
|
||||
use errors::RunError;
|
||||
use execute::execute;
|
||||
use parse::parse;
|
||||
use stack::Stack;
|
||||
|
||||
mod stack;
|
||||
mod parse;
|
||||
mod execute;
|
||||
mod instructions;
|
||||
mod errors;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() < 2 {
|
||||
eprintln!("Usage: {0} [FILE]", args[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut stack = Stack::new();
|
||||
let lines = fs::read_to_string(&args[1]).expect(&format!("{}", RunError::FailToReadFile));
|
||||
|
||||
parse(&mut stack, &lines);
|
||||
|
||||
execute(&mut stack, None, &args[1]);
|
||||
}
|
34
src/parse.rs
Normal file
34
src/parse.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use crate::{errors::ParseError, stack::{Instruction, Stack}};
|
||||
|
||||
pub fn parse(stack: &mut Stack, file_content: &str) {
|
||||
for (i, line) in file_content.lines().enumerate() {
|
||||
if line.trim().starts_with(';') || line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let inst_line: String;
|
||||
if let Some(comment_index) = line.trim().find(';') {
|
||||
inst_line = line.trim().chars().take(comment_index).collect();
|
||||
} else {
|
||||
inst_line = line.trim().chars().collect();
|
||||
}
|
||||
|
||||
let command: Vec<String> = inst_line.split_whitespace().map(String::from).collect();
|
||||
|
||||
let name: String;
|
||||
let mut arg: String = String::new();
|
||||
let mut data: u16 = 0;
|
||||
|
||||
name = command[0].clone();
|
||||
|
||||
if command.len() >= 2 {
|
||||
match name.chars().nth(0) {
|
||||
Some('#') => arg = command[1].to_string(),
|
||||
_ => data = command[1].parse().expect(&format!("{}", ParseError::DataNotAUInt(command[0].to_string(), i + 1))),
|
||||
}
|
||||
}
|
||||
|
||||
let inst = Instruction { name, arg, data };
|
||||
stack.program.push(inst);
|
||||
}
|
||||
}
|
25
src/stack.rs
Normal file
25
src/stack.rs
Normal file
@ -0,0 +1,25 @@
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Instruction {
|
||||
pub name: String,
|
||||
pub arg: String,
|
||||
pub data: u16
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Stack {
|
||||
pub program: Vec<Instruction>,
|
||||
pub program_counter: u16,
|
||||
pub labels: [Option<i16>; 256],
|
||||
pub memory: Vec<u16>
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
pub fn new() -> Stack {
|
||||
return Stack {
|
||||
program: Vec::new(),
|
||||
program_counter: 0,
|
||||
labels: [None; 256],
|
||||
memory: Vec::new()
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user