Initial commit

This commit is contained in:
n3taeli 2024-02-05 09:05:48 +02:00
commit a005400477
Signed by: n3tael
GPG Key ID: F305925762F035A8
43 changed files with 685 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
/archive
/bin

7
Cargo.lock generated Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,9 @@
; written by Labashki developer - aeris
@ 0
#EXPR >++:+\
DUMP
; Expected output
;
; 0: 3
; 1: 2

View 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
View 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
View 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

View 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

View 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
View 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
View File

@ -0,0 +1,10 @@
@ 0
PUSH 1
PUSH 2
PUSH 3
SIZE
MEOW
; Expected output
;
; 3

45
src/errors.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
pub fn exit(data: &u16) {
std::process::exit((*data).into());
}

31
src/instructions/expr.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
pub fn pop(memory: &mut Vec<u16>) {
memory.pop();
}

3
src/instructions/push.rs Normal file
View File

@ -0,0 +1,3 @@
pub fn push(memory: &mut Vec<u16>, data: &u16) {
memory.push(*data);
}

5
src/instructions/putc.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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()
};
}
}