forked from n3tael/labast
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