wip: working on the compiler.

This commit is contained in:
James Harton 2022-08-09 12:39:52 +12:00
parent e60cf9c4dd
commit 2efe1a3cf1
61 changed files with 799 additions and 665 deletions

14
Cargo.lock generated
View file

@ -35,14 +35,25 @@ dependencies = [
"outrun-parser",
]
[[package]]
name = "outrun-common"
version = "0.1.0"
[[package]]
name = "outrun-compiler"
version = "0.1.0"
dependencies = [
"case",
"outrun-common",
"outrun-lexer",
"outrun-parser",
"outrun-vm",
]
[[package]]
name = "outrun-core"
version = "0.1.0"
dependencies = [
"outrun-compiler",
]
[[package]]
@ -68,6 +79,7 @@ dependencies = [
name = "outrun-vm"
version = "0.1.0"
dependencies = [
"outrun-common",
"outrun-lexer",
"outrun-parser",
]

View file

@ -1,7 +1,9 @@
[workspace]
members = [
"outrun-common",
"outrun-compiler",
"outrun-core",
"outrun-lexer",
"outrun-parser",
"outrun-vm",

11
outrun-common/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
authors = ["James Harton <james@harton.nz>"]
description = "🌅 common data structures for the most retro-futuristic toy language in the world."
edition = "2021"
license-file = "../LICENSE.md"
name = "outrun-common"
version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,32 @@
use std::fmt::Debug;
use std::marker::PhantomData;
#[derive(Debug)]
pub struct IndexedVec<T: Debug>(Vec<T>);
impl<T: Debug> IndexedVec<T> {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn insert(&mut self, value: T) -> IndexedVecKey<T> {
let key = IndexedVecKey(self.0.len(), PhantomData);
self.0.push(value);
key
}
pub fn into_vec(self) -> Vec<T> {
self.0
}
pub fn as_slice(&self) -> &[T] {
&self.0
}
pub fn get_mut(&mut self, key: IndexedVecKey<T>) -> Option<&mut T> {
self.0.get_mut(key.0)
}
}
#[derive(Debug, Clone, Copy)]
pub struct IndexedVecKey<T: Debug>(usize, PhantomData<T>);

3
outrun-common/src/lib.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod indexed_vec;
pub mod stack;
pub mod unique_vec;

View file

@ -0,0 +1,69 @@
use std::default::Default;
#[derive(Debug)]
pub enum StackError {
Overflow,
Underflow,
}
type Result<T> = std::result::Result<T, StackError>;
#[derive(Debug)]
pub struct Stack<T>(Vec<T>);
impl<T> Stack<T> {
pub fn new() -> Stack<T> {
Stack(Vec::new())
}
pub fn push(&mut self, value: T) -> Result<usize> {
self.0.push(value);
Ok(self.0.len())
}
pub fn pop(&mut self) -> Result<T> {
self.0.pop().ok_or(StackError::Underflow)
}
pub fn peek(&self) -> Option<&T> {
self.0.last()
}
}
impl<T> Default for Stack<T> {
fn default() -> Self {
Stack::new()
}
}
#[derive(Debug)]
pub struct LimitedStack<T>(usize, Stack<T>);
impl<T> LimitedStack<T> {
pub fn new(limit: usize) -> LimitedStack<T> {
LimitedStack(limit, Stack::new())
}
pub fn push(&mut self, value: T) -> Result<usize> {
let len = self.1 .0.len();
if len >= self.0 {
return Err(StackError::Overflow);
}
self.1.push(value)
}
pub fn pop(&mut self) -> Result<T> {
self.1.pop()
}
pub fn peek(&self) -> Option<&T> {
self.1 .0.last()
}
}
impl<T> Default for LimitedStack<T> {
fn default() -> Self {
LimitedStack::new(4096)
}
}

View file

@ -0,0 +1,92 @@
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
pub struct UniqueVec<T: Debug> {
data: Vec<T>,
cmp: fn(&T, &T) -> bool,
}
impl<T: Debug> UniqueVec<T> {
pub fn new(cmp: fn(&T, &T) -> bool) -> Self {
Self {
data: Vec::new(),
cmp,
}
}
pub fn insert(&mut self, value: T) -> UniqueVecKey<T> {
let cmp = self.cmp;
if let Some(idx) = self.data.iter().position(|x| cmp(x, &value)) {
return UniqueVecKey(idx, PhantomData);
}
let idx = self.data.len();
self.data.push(value);
UniqueVecKey(idx, PhantomData)
}
pub fn get(&self, key: UniqueVecKey<T>) -> Option<&T> {
self.data.get(key.0)
}
pub fn find_by<F>(&self, pred: F) -> Option<UniqueVecKey<T>>
where
F: Fn(&T) -> bool,
{
self.data
.iter()
.position(pred)
.map(|i| UniqueVecKey(i, PhantomData))
}
pub fn replace(&mut self, key: UniqueVecKey<T>, value: T) {
let elem = self.data.get_mut(key.0).unwrap();
*elem = value;
}
pub fn into_vec(self) -> Vec<T> {
self.data
}
pub fn as_slice(&self) -> &[T] {
&self.data
}
}
impl<T: Debug> Debug for UniqueVec<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UniqueVec")
.field("data", &self.data)
.finish()
}
}
impl<T: Debug + PartialEq> Default for UniqueVec<T> {
fn default() -> Self {
UniqueVec {
data: Vec::new(),
cmp: PartialEq::eq,
}
}
}
#[derive(Debug, Eq, Clone, Copy)]
pub struct UniqueVecKey<T: Debug>(usize, PhantomData<T>);
impl<T: Debug> PartialEq for UniqueVecKey<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: Debug> Hash for UniqueVecKey<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl<T: Debug> From<UniqueVecKey<T>> for usize {
fn from(key: UniqueVecKey<T>) -> Self {
key.0
}
}

View file

@ -10,6 +10,6 @@ version = "0.1.0"
[dependencies]
case = "1.0.0"
outrun-common = {path = "../outrun-common"}
outrun-lexer = {path = "../outrun-lexer"}
outrun-parser = {path = "../outrun-parser"}
outrun-vm = {path = "../outrun-vm"}

14
outrun-compiler/README.md Normal file
View file

@ -0,0 +1,14 @@
[![Hippocratic License HL3-FULL](https://img.shields.io/static/v1?label=Hippocratic%20License&message=HL3-FULL&labelColor=5e2751&color=bc8c3d)](https://firstdonoharm.dev/version/3/0/full.html)
[![Issues](https://img.shields.io/gitlab/issues/open/jimsy/outrun)](https://gitlab.com/jimsy/outrun/-/issues)
# 🌅 Outrun Lexer
This crate is the compiler for the Outrun programming language.
It is deliberately simple as we're still in the bootstrapping phase of development.
# License
Outrun Lexer is distributed under the terms of the the Hippocratic Version 3.0 Full License.
See [LICENSE.md](https://gitlab.com/jimsy/outrun/-/blob/main/outrun-lexer/LICENSE.md) for details.

View file

@ -0,0 +1,20 @@
//! Block
//!
//! Holds an instruction sequence, including the arity and types of any values.
use crate::instructions::Instruction;
#[derive(Debug, Clone)]
pub struct Block {
code: Vec<Instruction>,
}
impl Block {
pub fn new() -> Self {
Block { code: Vec::new() }
}
pub fn push_instruction(&mut self, instruction: Instruction) {
self.code.push(instruction);
}
}

View file

@ -0,0 +1,43 @@
use crate::instructions::Instruction;
use crate::module::{Module, StringKey, TypeKey};
#[derive(Debug)]
pub struct Builder<'a> {
module: &'a mut Module,
}
impl<'a> Builder<'a> {
pub fn new(module: &'a mut Module) -> Builder<'a> {
Builder { module }
}
pub fn push_atom(&mut self, value: StringKey) {
self.push_instruction(Instruction::PushAtom(value.into()))
}
pub fn push_boolean(&mut self, value: bool) {
self.push_instruction(Instruction::PushBoolean(value));
}
pub fn push_constant(&mut self, value: TypeKey) {
self.push_instruction(Instruction::PushConstant(value.into()));
}
pub fn push_float(&mut self, value: f64) {
self.push_instruction(Instruction::PushFloat(value))
}
pub fn push_integer(&mut self, value: i64) {
self.push_instruction(Instruction::PushInteger(value));
}
pub fn push_string<T: ToString>(&mut self, value: T) {
let value = self.module.strings.insert(value.to_string());
self.push_instruction(Instruction::PushString(value.into()));
}
fn push_instruction(&mut self, inst: Instruction) {
let block = self.module.current_block_mut();
block.push_instruction(inst);
}
}

View file

@ -0,0 +1,9 @@
#[derive(Debug, Clone)]
pub enum Instruction {
PushAtom(usize),
PushBoolean(bool),
PushConstant(usize),
PushFloat(f64),
PushInteger(i64),
PushString(usize),
}

View file

@ -1,25 +1,19 @@
extern crate outrun_common;
extern crate outrun_lexer;
extern crate outrun_parser;
use std::fs;
use std::path::{Path, PathBuf};
use outrun_parser::{Node, NodeKind, NodeValue};
use std::path::Path;
mod block;
mod builder;
mod error;
mod instructions;
mod module;
use builder::Builder;
pub use error::Error;
pub fn init() -> Result<(), Error> {
let path = Path::new(env!("CARGO_MANIFEST_DIR"));
let path = path.parent().map(|p| p.join("lib")).unwrap();
let path = path.as_path();
let files = collect_files(path, Vec::new())?;
for path in files.iter() {
compile_file(path)?;
}
Ok(())
}
pub use module::Module;
pub fn compile_file(path: &Path) -> Result<(), Error> {
println!("*** Compiling file: {}", path.to_str().unwrap());
@ -27,36 +21,42 @@ pub fn compile_file(path: &Path) -> Result<(), Error> {
.map_err(|e| Error::from((path, e)))
.and_then(|tokens| outrun_parser::parse(&tokens).map_err(|e| Error::from((path, e))))?;
// println!("nodes: {:#?}", nodes);
let mut module = Module::new(Some(path.to_str().unwrap()));
for node in nodes {
visit(node, &mut module);
}
println!("MODULE: {:?}", module);
Ok(())
}
fn collect_files(path: &Path, mut files: Vec<PathBuf>) -> Result<Vec<PathBuf>, Error> {
let entries = fs::read_dir(path).map_err(|e| Error::from((path, e)))?;
for entry in entries {
let entry = entry.map_err(|e| Error::from((path, e)))?;
let path = entry.path();
let path = path.as_path();
if path.is_dir() {
files = collect_files(&path, files)?;
} else if let Some(extension) = path.extension() {
if extension == "run" {
files.push(path.to_owned());
}
fn visit(node: Node, module: &mut Module) {
match node.kind {
NodeKind::Atom => {
let atom = module.strings.insert(node.value.as_string().unwrap());
Builder::new(module).push_atom(atom);
}
}
Ok(files)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_init() {
let result = init().unwrap();
println!("result: {:?}", result);
assert!(false);
NodeKind::Boolean => {
let value = node.value.as_bool().unwrap();
Builder::new(module).push_boolean(value);
}
NodeKind::Constant => {
let value = module.reference_type(node.value.as_string().unwrap());
Builder::new(module).push_constant(value);
}
NodeKind::Float => {
let value = node.value.as_float().unwrap();
Builder::new(module).push_float(value);
}
NodeKind::Integer => {
let value = node.value.as_integer().unwrap();
Builder::new(module).push_integer(value);
}
NodeKind::String => {
let value = node.value.as_string().unwrap();
Builder::new(module).push_string(value);
}
_ => unreachable!(),
}
}

View file

@ -0,0 +1,220 @@
use crate::block::Block;
use outrun_common::indexed_vec::{IndexedVec, IndexedVecKey};
use outrun_common::stack::Stack;
use outrun_common::unique_vec::{UniqueVec, UniqueVecKey};
use std::collections::HashMap;
pub type TypeKey = UniqueVecKey<Type>;
pub type StringKey = UniqueVecKey<String>;
#[derive(Debug)]
pub struct Module {
pub strings: UniqueVec<String>,
location: Option<StringKey>,
blocks: IndexedVec<Block>,
functions: HashMap<StringKey, Function>,
current_block: Stack<IndexedVecKey<Block>>,
current_function: Stack<StringKey>,
types: UniqueVec<Type>,
}
impl Module {
pub fn new<T: ToString>(location: Option<T>) -> Self {
let mut strings = UniqueVec::default();
let location = location.map(|s| strings.insert(s.to_string()));
let blocks = IndexedVec::new();
let functions = HashMap::new();
let current_block = Stack::new();
let current_function = Stack::new();
let types = UniqueVec::new(|l: &Type, r: &Type| l.name() == r.name());
Module {
strings,
location,
blocks,
functions,
current_block,
current_function,
types,
}
}
pub fn push_function<T: ToString>(
&mut self,
name: T,
arguments: &[(StringKey, TypeKey)],
return_type: TypeKey,
) {
let name = self.strings.insert(name.to_string());
let block = Block::new();
let block_id = self.blocks.insert(block);
let arity = arguments.len();
let argument_names = arguments.iter().map(|arg| arg.0.clone()).collect();
let argument_types = arguments.iter().map(|arg| arg.1.clone()).collect();
self.current_block.push(block_id.clone()).unwrap();
let function = Function {
name: Some(name.clone()),
arity,
argument_names,
argument_types,
entry_block: block_id,
return_type,
};
self.functions.insert(name, function);
}
pub fn pop_function(&mut self) -> Option<StringKey> {
self.current_function.pop().ok()
}
pub fn push_block(&mut self) -> IndexedVecKey<Block> {
let block = Block::new();
let block_id = self.blocks.insert(block);
self.current_block.push(block_id.clone()).unwrap();
block_id
}
pub fn current_block_mut(&mut self) -> &mut Block {
let block_id = self.current_block.peek().unwrap();
self.blocks.get_mut(block_id.clone()).unwrap()
}
pub fn integer_type(&mut self) -> TypeKey {
let name = self.strings.insert("Outrun.Core.Integer".to_string());
self.types.insert(Type::Integer(name))
}
pub fn float_type(&mut self) -> TypeKey {
let name = self.strings.insert("Outrun.Core.Float".to_string());
self.types.insert(Type::Float(name))
}
pub fn boolean_type(&mut self) -> TypeKey {
let name = self.strings.insert("Outrun.Core.Boolean".to_string());
self.types.insert(Type::Boolean(name))
}
pub fn new_protocol_type<T: ToString>(
&mut self,
name: Option<T>,
requirements: Vec<TypeKey>,
) -> TypeKey {
let name = name
.map(|s| s.to_string())
.or_else(|| {
Some(
requirements
.iter()
.map(|k| self.name_of(k.clone()))
.collect::<Vec<&str>>()
.join("+"),
)
})
.map(|s| self.strings.insert(s))
.unwrap();
let new_type = Type::Protocol {
name: name.clone(),
requirements,
};
if let Some(key) = self.types.find_by(|t| t.name() == name) {
let r#type = self.types.get(key.clone()).unwrap();
if r#type.is_reference() {
self.types.replace(key.clone(), new_type);
return key;
}
panic!("Attempt to overwrite existing type {:#?}", r#type);
}
self.types.insert(new_type)
}
pub fn new_struct_type<T: ToString>(&mut self, name: T, fields: Vec<(T, TypeKey)>) -> TypeKey {
let name = self.strings.insert(name.to_string());
let (field_names, field_types) = fields.iter().fold(
(Vec::new(), Vec::new()),
|(mut field_names, mut field_types), (name, r#type)| {
field_names.push(self.strings.insert(name.to_string()));
field_types.push(r#type.clone());
(field_names, field_types)
},
);
let new_type = Type::Struct {
name: name.clone(),
field_names,
field_types,
};
if let Some(key) = self.types.find_by(|t| t.name() == name) {
let r#type = self.types.get(key.clone()).unwrap();
if r#type.is_reference() {
self.types.replace(key.clone(), new_type);
return key;
}
panic!("Attempt to overwrite existing type {:#?}", r#type);
}
self.types.insert(new_type)
}
pub fn reference_type<T: ToString>(&mut self, name: T) -> TypeKey {
let name = self.strings.insert(name.to_string());
self.types
.find_by(|t| t.name() == name)
.unwrap_or_else(|| self.types.insert(Type::Reference(name)))
}
fn name_of(&self, typekey: TypeKey) -> &str {
self.types
.get(typekey)
.and_then(|t| self.strings.get(t.name()))
.unwrap()
}
}
#[derive(Debug)]
pub struct Function {
name: Option<StringKey>,
arity: usize,
entry_block: IndexedVecKey<Block>,
argument_names: Vec<StringKey>,
argument_types: Vec<TypeKey>,
return_type: TypeKey,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Type {
Struct {
name: StringKey,
field_names: Vec<StringKey>,
field_types: Vec<TypeKey>,
},
Protocol {
name: StringKey,
requirements: Vec<TypeKey>,
},
Integer(StringKey),
Boolean(StringKey),
Float(StringKey),
Reference(StringKey),
}
impl Type {
fn name(&self) -> StringKey {
match self {
Type::Struct { name, .. } => name.clone(),
Type::Protocol { name, .. } => name.clone(),
Type::Integer(name) => name.clone(),
Type::Boolean(name) => name.clone(),
Type::Float(name) => name.clone(),
Type::Reference(name) => name.clone(),
}
}
fn is_reference(&self) -> bool {
matches!(self, Type::Reference(_))
}
}

View file

@ -1,40 +0,0 @@
use outrun_lexer::Span;
#[derive(Debug)]
pub enum NoticeSeverity {
Info,
Warning,
Error,
}
#[derive(Debug)]
pub enum NoticeKind {
AtomCase,
ConstantCase,
ExprInModule,
DuplicateKeyword,
}
#[derive(Debug)]
pub struct Notice {
pub severity: NoticeSeverity,
pub error: NoticeKind,
pub message: String,
pub span: Span,
}
impl Notice {
pub fn new<T: ToString, U: Into<Span>>(
severity: NoticeSeverity,
error: NoticeKind,
message: T,
span: U,
) -> Self {
Notice {
severity,
error,
message: message.to_string(),
span: span.into(),
}
}
}

View file

@ -1,401 +0,0 @@
extern crate outrun_lexer;
extern crate outrun_parser;
extern crate outrun_vm;
mod notice;
mod ty;
mod unique_vec;
use crate::notice::{Notice, NoticeKind, NoticeSeverity};
use crate::ty::Type;
use case::CaseExt;
use outrun_lexer::{Span, TokenKind};
use outrun_parser::{Node, NodeKind, NodeValue};
use outrun_vm::{Constant, Instruction, OpCode, Stack};
use std::collections::HashMap;
use unique_vec::UniqueVec;
#[derive(Debug, Clone, Default)]
enum Scope {
#[default]
Module,
Protocol(usize),
Struct(usize),
Function(usize),
}
#[derive(Debug, Default)]
pub struct Compiler {
data: UniqueVec<Constant>,
code: Vec<Instruction>,
spans: Vec<Span>,
notices: Vec<Notice>,
types: HashMap<usize, Type>,
current_definition: Stack<Scope>,
}
impl Compiler {
pub fn new() -> Self {
Compiler {
data: UniqueVec::new(),
code: Vec::new(),
spans: Vec::new(),
notices: Vec::new(),
types: HashMap::new(),
current_definition: Stack::default(),
}
}
pub fn compile(&mut self, node: Node) {
do_compile(&node, self);
}
pub fn info<T: ToString, U: Into<Span>>(&mut self, kind: NoticeKind, message: T, span: U) {
self.notices.push(Notice::new(
NoticeSeverity::Info,
kind,
message.to_string(),
span.into(),
));
}
pub fn warning<T: ToString, U: Into<Span>>(&mut self, kind: NoticeKind, message: T, span: U) {
self.notices.push(Notice::new(
NoticeSeverity::Warning,
kind,
message.to_string(),
span.into(),
));
}
pub fn error<T: ToString, U: Into<Span>>(&mut self, kind: NoticeKind, message: T, span: U) {
self.notices.push(Notice::new(
NoticeSeverity::Error,
kind,
message.to_string(),
span.into(),
));
}
pub fn push_code<T: Into<Span>>(&mut self, inst: Instruction, span: T) -> usize {
let idx = self.code.len();
self.code.push(inst);
self.spans.push(span.into());
idx
}
pub fn push_data<T: Into<Constant>>(&mut self, value: T) -> usize {
self.data.push(value.into())
}
fn has_no_errors(&self) -> bool {
self.notices
.iter()
.any(|n| matches!(n.severity, NoticeSeverity::Error))
}
}
fn is_camel_case(s: &str) -> bool {
s.to_camel() == s
}
fn is_snake_case(s: &str) -> bool {
s.to_snake() == s
}
fn do_compile(node: &Node, compiler: &mut Compiler) {
match node.kind {
NodeKind::Atom => do_compile_atom(node, compiler),
NodeKind::Block => do_compile_block(node, compiler),
NodeKind::Boolean => do_compile_boolean(node, compiler),
NodeKind::Call => do_compile_call(node, compiler),
NodeKind::Constant => do_compile_constant(node, compiler),
NodeKind::Float => do_compile_float(node, compiler),
NodeKind::GetLocal => do_compile_get_local(node, compiler),
NodeKind::Infix => do_compile_infix(node, compiler),
NodeKind::Integer => do_compile_integer(node, compiler),
NodeKind::KeywordPair => do_compile_keyword_pair(node, compiler),
NodeKind::List => do_compile_list(node, compiler),
NodeKind::Map => do_compile_map(node, compiler),
NodeKind::Prefix => do_compile_prefix(node, compiler),
NodeKind::SetLocal => do_compile_set_local(node, compiler),
NodeKind::Definition => do_compile_definition(node, compiler),
NodeKind::String => do_compile_string(node, compiler),
NodeKind::Module => do_compile_module(node, compiler),
_ => unreachable!(),
}
}
fn do_compile_atom(node: &Node, compiler: &mut Compiler) {
let name = node.value.as_str().unwrap();
if !is_snake_case(name) {
compiler.info(
NoticeKind::AtomCase,
format!(":{} should be snake case", name),
&node,
);
}
let idx = compiler.push_data(name);
let atom = compiler.push_data(Constant::Atom(idx));
compiler.push_code(Instruction::new_with_arg(OpCode::PushAtom, atom), node);
}
fn do_compile_block(node: &Node, compiler: &mut Compiler) {}
fn do_compile_boolean(node: &Node, compiler: &mut Compiler) {
let value = node.value.as_bool().unwrap();
let op = if value {
OpCode::PushTrue
} else {
OpCode::PushFalse
};
compiler.push_code(Instruction::new(op), node);
}
fn do_compile_call(node: &Node, compiler: &mut Compiler) {}
fn do_compile_constant(node: &Node, compiler: &mut Compiler) {
let name = node.value.as_strings().unwrap().join(".");
if !is_camel_case(&name) {
compiler.info(
NoticeKind::ConstantCase,
format!("{} should be camel case", name),
&node,
);
}
let idx = compiler.push_data(name);
let constant = compiler.push_data(Constant::Identifier(idx));
compiler.push_code(
Instruction::new_with_arg(OpCode::PushConstant, constant),
node,
);
}
fn do_compile_float(node: &Node, compiler: &mut Compiler) {
let float = node.value.as_float().unwrap();
let idx = compiler.push_data(float);
compiler.push_code(Instruction::new_with_arg(OpCode::PushFloat, idx), node);
}
fn do_compile_get_local(node: &Node, compiler: &mut Compiler) {
let name = node.value.as_string().unwrap();
let idx = compiler.push_data(name);
let ident = compiler.push_data(Constant::Identifier(idx));
compiler.push_code(Instruction::new_with_arg(OpCode::Load, ident), node);
}
fn do_compile_infix(node: &Node, compiler: &mut Compiler) {
match node.value {
NodeValue::Infix(ref lhs, ref op, ref rhs) => {
do_compile(lhs, compiler);
do_compile(rhs, compiler);
let inst = infix_operator(*op, compiler);
compiler.push_code(inst, node);
}
_ => unreachable!(),
}
}
fn do_compile_integer(node: &Node, compiler: &mut Compiler) {
let integer = node.value.as_integer().unwrap();
let idx = compiler.push_data(integer);
compiler.push_code(Instruction::new_with_arg(OpCode::PushInteger, idx), node);
}
fn do_compile_keyword_pair(node: &Node, compiler: &mut Compiler) {}
fn do_compile_list(node: &Node, compiler: &mut Compiler) {
let nodes = node.value.as_nodes().unwrap();
for node in nodes {
do_compile(node, compiler);
}
compiler.push_code(
Instruction::new_with_arg(OpCode::PushList, nodes.len()),
node,
);
}
fn do_compile_map(node: &Node, compiler: &mut Compiler) {
let nodes = node.value.as_nodes().unwrap();
for node in nodes {
do_compile(node, compiler);
}
compiler.push_code(
Instruction::new_with_arg(OpCode::PushMap, nodes.len()),
node,
);
}
fn infix_operator(token_kind: TokenKind, _: &mut Compiler) -> Instruction {
match token_kind {
TokenKind::And => Instruction::new(OpCode::BitwiseAnd),
TokenKind::AndAnd => Instruction::new(OpCode::LogicalAnd),
TokenKind::BangEq => Instruction::new(OpCode::NotEqual),
TokenKind::Dot => Instruction::new(OpCode::Index),
TokenKind::Eq => Instruction::new(OpCode::Assign),
TokenKind::EqEq => Instruction::new(OpCode::Equal),
TokenKind::ForwardSlash => Instruction::new(OpCode::Divide),
TokenKind::Gt => Instruction::new(OpCode::Greater),
TokenKind::GtEq => Instruction::new(OpCode::GreaterOrEqual),
TokenKind::GtGt => Instruction::new(OpCode::BitwiseShiftRight),
TokenKind::Lt => Instruction::new(OpCode::Less),
TokenKind::LtEq => Instruction::new(OpCode::LessOrEqual),
TokenKind::LtLt => Instruction::new(OpCode::BitwiseShiftLeft),
TokenKind::Minus => Instruction::new(OpCode::Subtract),
TokenKind::Percent => Instruction::new(OpCode::Modulo),
TokenKind::Pipe => Instruction::new(OpCode::BitwiseOr),
TokenKind::PipePipe => Instruction::new(OpCode::LogicalOr),
TokenKind::Plus => Instruction::new(OpCode::Add),
TokenKind::Star => Instruction::new(OpCode::Multiply),
_ => unreachable!(),
}
}
fn do_compile_prefix(node: &Node, compiler: &mut Compiler) {
match node.value {
NodeValue::Prefix(TokenKind::Bang, ref operand) => {
do_compile(operand.as_ref(), compiler);
compiler.push_code(Instruction::new(OpCode::UnaryNot), node);
}
NodeValue::Prefix(TokenKind::Minus, ref operand) => {
do_compile(operand.as_ref(), compiler);
compiler.push_code(Instruction::new(OpCode::UnaryMinus), node);
}
_ => unreachable!(),
}
}
fn do_compile_set_local(node: &Node, compiler: &mut Compiler) {
match node.value {
NodeValue::SetLocal(ref name, ref value) => {
let idx = compiler.push_data(name);
compiler.push_code(Instruction::new_with_arg(OpCode::Store, idx), node);
}
_ => unreachable!(),
}
}
fn collect_keywords(fields: &Vec<Node>, compiler: &mut Compiler) -> HashMap<String, Node> {
let mut result = HashMap::new();
for pair in fields {
match &pair.value {
NodeValue::KeywordPair(key, value) => {
let name = key.value.as_string().unwrap();
let value = value.as_ref().clone();
if result.contains_key(&name) {
compiler.warning(
NoticeKind::DuplicateKeyword,
format!("Ignoring duplicate keyword: {}", name),
key.as_ref(),
);
}
result.insert(name, value);
}
_ => unreachable!(),
}
}
result
}
fn define_public_function(node: &Node, compiler: &mut Compiler) {}
fn define_private_function(node: &Node, compiler: &mut Compiler) {}
fn define_anonymous_function(node: &Node, compiler: &mut Compiler) {}
fn define_impl(node: &Node, compiler: &mut Compiler) {}
fn define_let(node: &Node, compiler: &mut Compiler) {}
fn define_protocol(node: &Node, compiler: &mut Compiler) {}
fn define_struct(node: &Node, compiler: &mut Compiler) {
match node.value {
NodeValue::Definition {
ref name,
ref fields,
ref arguments,
..
} => {
let name = name.value.as_strings().unwrap().join(".");
let idx = compiler.push_data(name);
let fields = collect_keywords(fields, compiler);
}
_ => unreachable!(),
}
}
fn define_use(node: &Node, compiler: &mut Compiler) {}
fn do_compile_definition(node: &Node, compiler: &mut Compiler) {
match node.value.as_definition_kind().unwrap() {
TokenKind::KeywordDef => define_public_function(node, compiler),
TokenKind::KeywordDefPrivate => define_private_function(node, compiler),
TokenKind::KeywordFn => define_anonymous_function(node, compiler),
TokenKind::KeywordImpl => define_impl(node, compiler),
TokenKind::KeywordLet => define_let(node, compiler),
TokenKind::KeywordProtocol => define_protocol(node, compiler),
TokenKind::KeywordStruct => define_struct(node, compiler),
TokenKind::KeywordUse => define_use(node, compiler),
_ => unreachable!(),
}
}
fn do_compile_string(node: &Node, compiler: &mut Compiler) {
let value = node.value.as_string().unwrap();
let idx = compiler.push_data(value);
compiler.push_code(Instruction::new_with_arg(OpCode::PushString, idx), node);
}
fn do_compile_module(node: &Node, compiler: &mut Compiler) {
if node.is_module() {
compiler.current_definition.push(Scope::Module).unwrap();
let nodes = node.value.as_nodes().unwrap();
for node in nodes {
if !node.is_definition() {
compiler.notices.push(Notice::new(
NoticeSeverity::Error,
NoticeKind::ExprInModule,
"Expected statement, found expression",
node,
))
}
do_compile(node, compiler);
}
} else {
unreachable!();
}
}
#[cfg(test)]
mod test {
use super::*;
use outrun_lexer::{Scanner, Token};
use outrun_parser::Parser;
fn expr(input: &str) -> Node {
let tokens = Scanner::new(input).into_iter().collect::<Vec<Token>>();
let parser = Parser::new(&tokens);
let (_, ast) = parser.parse_expr().unwrap();
ast
}
// #[test]
// fn test_atom() {
// let mut module = Module::default();
// let node = expr(":scandroid");
// do_compile_atom(node, &mut module);
// println!("{:#?}", module);
// assert!(false);
// }
}

View file

@ -1,25 +0,0 @@
#[derive(Debug)]
pub struct Type {
pub name: Option<String>,
pub kind: TypeKind,
pub value: TypeValue,
}
#[derive(Debug)]
pub enum TypeKind {
Boolean,
Integer,
Float,
String,
List,
Map,
Struct,
Protocol,
Function,
}
#[derive(Debug)]
pub enum TypeValue {
None,
Protocol,
}

View file

@ -1,24 +0,0 @@
#[derive(Debug, Clone, PartialEq)]
pub struct UniqueVec<T: PartialEq>(Vec<T>);
impl<T: PartialEq> UniqueVec<T> {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn push(&mut self, value: T) -> usize {
if let Some(idx) = self.0.iter().position(|element| element == &value) {
return idx;
}
let idx = self.0.len();
self.0.push(value);
idx
}
}
impl<T: PartialEq> Default for UniqueVec<T> {
fn default() -> Self {
Self::new()
}
}

12
outrun-core/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
authors = ["James Harton <james@harton.nz>"]
description = "🌅 The core library for the most retro-futuristic toy language in the world."
edition = "2021"
license-file = "../LICENSE.md"
name = "outrun-core"
version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
outrun-compiler = {path = "../outrun-compiler"}

14
outrun-core/README.md Normal file
View file

@ -0,0 +1,14 @@
[![Hippocratic License HL3-FULL](https://img.shields.io/static/v1?label=Hippocratic%20License&message=HL3-FULL&labelColor=5e2751&color=bc8c3d)](https://firstdonoharm.dev/version/3/0/full.html)
[![Issues](https://img.shields.io/gitlab/issues/open/jimsy/outrun)](https://gitlab.com/jimsy/outrun/-/issues)
# 🌅 Outrun Lexer
This crate provides the core library for Outrun.
It is provided as a crate so that it can contain pre-compiled Outrun assets.
# License
Outrun Lexer is distributed under the terms of the the Hippocratic Version 3.0 Full License.
See [LICENSE.md](https://gitlab.com/jimsy/outrun/-/blob/main/outrun-lexer/LICENSE.md) for details.

9
outrun-core/lib/abs.run Normal file
View file

@ -0,0 +1,9 @@
```doc
# Absolute value
```
protocol AbsoluteValue, as: do
```doc
Return the absolute value (or magnitude) of `self`.
```
def abs(self: Self): Self
end

19
outrun-core/lib/float.run Normal file
View file

@ -0,0 +1,19 @@
```doc
# Float
This protocol defines all the protocols and functions required for a value to masquerade as a floating point number.
```
protocol Float,
requires:
AbsoluteValue + Addition + Division + Equality + Greater + GreaterOrEqual +
Less + LessOrEqual + Multiplication + Negation,
as: do
def nan?(self: Self): Boolean
def infinite?(self: Self): Boolean
def finite?(self: Self): Boolean
def subnormal?(self: Self): Boolean
def normal?(self: Self): Boolean
def sign_positive?(self: Self): Boolean
def sign_negative?(self: Self): Boolean
def inverse(self: Self): Option
end

View file

@ -1,22 +1,16 @@
```doc
# Integer
This protocol defines all the protocols required for a value to masquerade as an integer.
This protocol defines all the protocols and functions required for a value to masquerade as an integer.
For the concrete implementation see `Outrun.Core.Integer`.
```
protocol Integer,
requires:
Addition + BitwiseAnd + BitwiseOr + BitwiseXor + Division + Equality +
Exponentiation + GreaterOrEqual + Greater + LessOrEqual + Less + Modulus +
Multiplication + ShiftLeft + ShiftRight + Subtraction,
AbsoluteValue + Addition + BitwiseAnd + BitwiseOr + BitwiseXor + Division +
Equality + Exponentiation + GreaterOrEqual + Greater + LessOrEqual + Less +
Modulus + Multiplication + Negation + ShiftLeft + ShiftRight + Subtraction,
as: do
```doc
Computes the absolute value of `self`.
```
def abs(self: Self): Option
```doc
Returns whether the value of `self` is greater than zero.
```

View file

@ -0,0 +1,11 @@
```doc
# Negation
Perform a unary negation on the value. Used by the unary `-` operator.
```
protocol Negation, as: do
```doc
Return the negative value of `self`.
```
def neg(self: Self): Self
end

View file

@ -0,0 +1,65 @@
```doc
# Outrun.Core.Float
The native implementation of `Float` based around Rust's `f64` type.
Has no constructor as it is created internally in the virtual machine by literals.
```
struct Outrun.Core.Float, as: do
def nan?(self: Self): Boolean, as: Outrun.NIF.float_is_nan(self)
def infinite?(self: Self): Boolean, as: Outrun.NIF.float_is_infinite(self)
def finite?(self: Self): Boolean, as: Outrun.NIF.float_is_finite(self)
def subnormal?(self: Self): Boolean, as: Outrun.NIF.float_is_subnormal(self)
def normal?(self: Self): Boolean, as: Outrun.NIF.float_is_normal(self)
def sign_positive?(self: Self): Boolean, as: Outrun.NIF.float_is_sign_positive(self)
def sign_negative?(self: Self): Boolean, as: Outrun.NIF.float_is_sign_negative(self)
def inverse(self: Self): Option, as: Outrun.NIF.float_inverse(self)
end
impl AbsoluteValue, for: Outrun.Core.Float, as: do
def abs(self: Self): Option, as: Outrun.NIF.float_abs(self)
end
impl Addition, for: Outrun.Core.Float, as: do
def add(self: Self, other: Self): Option, as: Outrun.NIF.float_add(self, other)
def add(_self: Self, _other: Any): Option, as: Option.none()
end
impl Division, for: Outrun.Core.Float, as: do
def divide(self: Self, other: Self): Option, as: Outrun.NIF.float_div(self, other)
def divide(_self: Self, _other: Any): Option, as: Option.none()
end
impl Equality, for: Outrun.Core.Float, as: do
def equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.float_is_eq(self, other)
def equal?(_self: Self, _other: Self): Boolean, as: false
end
impl Greater, for: Outrun.Core.Float, as: do
def greater?(self: Self, other: Self): Boolean, as: Outrun.NIF.float_gt(self, other)
def greater?(_self: Self, _other: Self): Boolean, as: false
end
impl GreaterOrEqual, for: Outrun.Core.Float, as: do
def greater_or_equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.float_gte(self, other)
def greater_or_equal?(_self: Self, _other: Self): Boolean, as: false
end
impl Less, for: Outrun.Core.Float, as: do
def less?(self: Self, other: Self): Boolean, as: Outrun.NIF.float_lt(self, other)
def less?(_self: Self, _other: Self): Boolean, as: false
end
impl LessOrEqual, for: Outrun.Core.Float, as: do
def less_or_equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.float_lte(self, other)
def less_or_equal?(_self: Self, _other: Self): Boolean, as: false
end
impl Multiplication, for: Outrun.Core.Float, as: do
def multiply(self: Self, other: Self): Option, as: Outrun.NIF.float_mul(self, other)
def multiply(_self: Self, _other: Any): Option, as: Option.none()
end
impl Negation, for: Outrun.Core.Float, as: do
def neg(self: Self): Option, as: Outrun.NIF.float_neg(self)
end

View file

@ -29,116 +29,82 @@ struct Outrun.Core.Integer, as: do
def negative?(self: Self): Self, as: Outrun.NIF.integer_lt(self, 0)
end
impl Addition, for: Outrun.Core.Integer, as: do
def add(self: Self, other: Self): Option, as: do
Outrun.NIF.integer_add(self, other)
end
impl AbsoluteValue, for: Outrun.Core.Integer, as: do
def abs(self: Self): Self, as: Outrun.NIF.integer_abs(self)
end
impl Addition, for: Outrun.Core.Integer, as: do
def add(self: Self, other: Self): Option, as: Outrun.NIF.integer_add(self, other)
def add(_self: Self, _other: Any): Option, as: Option.none()
end
impl BitwiseAnd, for: Outrun.Core.Integer, as: do
def and(self: Self, other: Self): Option, as: do
Outrun.NIF.integer_band(self, other)
end
def and(self: Self, other: Self): Option, as: Outrun.NIF.integer_band(self, other)
def and(_self: Self, _other: Any): Option, as: Option.none()
end
impl BitwiseOr, for: Outrun.Core.Integer, as: do
def or(self: Self, other: Self): Option, as: do
Outrun.NIF.integer_bor(self, other)
end
def or(self: Self, other: Self): Option, as: Outrun.NIF.integer_bor(self, other)
def or(_self: Self, _other: Any): Option, as: Option.none()
end
impl BitwiseXor, for: Outrun.Core.Integer, as: do
def xor(self: Self, other: Self): Option, as: do
Outrun.NIF.integer_bxor(self, other)
end
def xor(self: Self, other: Self): Option, as: Outrun.NIF.integer_bxor(self, other)
def xor(_self: Self, _other: Any): Option, as: Option.none()
end
impl Division, for: Outrun.Core.Integer, as: do
def divide(self: Self, other: Self): Option, as: do
Outrun.NIF.integer_div(self, other)
end
def divide(self: Self, other: Self): Option, as: Outrun.NIF.integer_div(self, other)
def divide(_self: Self, _other: Any): Option, as: Option.none()
end
impl Equality, for: Outrun.Core.Integer, as: do
def equal?(self: Self, other: Self): Boolean, as: do
Outrun.NIF.integer_eq(self, other)
end
def equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.integer_eq(self, other)
def equal?(_self: Self, _other: Any): Boolean, as: false
end
impl Exponentiation, for: Outrun.Core.Integer, as: do
def raise(self: Self, other: Self): Option, as: do
Outrun.NIF.integer_expo(self, other)
end
def raise(self: Self, other: Self): Option, as: Outrun.NIF.integer_expo(self, other)
def raise(_self: Self, _other: Any): Option, as: Option.none()
end
impl GreaterOrEqual, for: Outrun.Core.Integer, as: do
def greater_or_equal?(self: Self, other: Self): Boolean, as: do
Outrun.NIF.integer_gte(self, other)
end
def greater_or_equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.integer_gte(self, other)
def greater_or_equal?(_self: Self, _other: Any): Boolean, as: false
end
impl Greater, for: Outrun.Core.Integer, as: do
def greater?(self: Self, other: Self): Boolean, as: do
Outrun.NIF.integer_gt(self, other)
end
def greater?(self: Self, other: Self): Boolean, as: Outrun.NIF.integer_gt(self, other)
def greater?(_self: Self, _other: Any): Boolean, as: false
end
impl Integer, for: Outrun.Core.Integer
impl LessOrEqual, for: Outrun.Core.Integer, as: do
def less_or_equal?(self: Self, other: Self): Boolean, as: do
Outrun.NIF.integer_lte(self, other)
end
def less_or_equal?(self: Self, other: Self): Boolean, as: Outrun.NIF.integer_lte(self, other)
def less_or_equal?(_self: Self, _other: Self): Boolean, as: false
end
impl Less, for: Outrun.Core.Integer, as: do
def less?(self: Self, other: Self): Boolean, as: do
Outrun.NIF.integer_lt(self, other)
end
def less?(self: Self, other: Self): Boolean, as: Outrun.NIF.integer_lt(self, other)
def less?(_self: Self, _other: Self): Boolean, as: false
end
impl Modulus, for: Outrun.Core.Integer, as: do
def modulo(self: Self, other: Self): Option, as: do
Outrun.NIF.integer_mod(self, other)
end
def modulo(self: Self, other: Self): Option, as: Outrun.NIF.integer_mod(self, other)
def modulo(_self: Self, _other: Any): Option, as: Option.none()
end
impl Multiplication, for: Outrun.Core.Integer, as: do
def multiply(self: Self, other: Self): Option, as: do
Outrun.NIF.integer_mul(self, other)
end
def multiply(self: Self, other: Self): Option, as: Outrun.NIF.integer_mul(self, other)
def multiply(_self: Self, _other: Any): Option, as: Option.none()
end
impl Subtraction, for: Outrun.Core.Integer, as: do
def subtract(self: Self, other: Self): Option, as: do
Outrun.NIF.integer_sub(self, other)
end
impl Negation, for: Outrun.Core.Integer, as: do
def neg(self: Self): Option, as: Outrun.NIF.integer_neg(self)
end
impl Subtraction, for: Outrun.Core.Integer, as: do
def subtract(self: Self, other: Self): Option, as: Outrun.NIF.integer_sub(self, other)
def subtract(_self: Self, _other: Any): Option, as: Option.none()
end

49
outrun-core/src/lib.rs Normal file
View file

@ -0,0 +1,49 @@
extern crate outrun_compiler;
use outrun_compiler::Error;
use std::fs;
use std::path::{Path, PathBuf};
pub fn init() -> Result<(), Error> {
let path = Path::new(env!("CARGO_MANIFEST_DIR"));
let path = path.join("lib");
let path = path.as_path();
let files = collect_files(path, Vec::new())?;
for path in files.iter() {
outrun_compiler::compile_file(path)?;
}
Ok(())
}
fn collect_files(path: &Path, mut files: Vec<PathBuf>) -> Result<Vec<PathBuf>, Error> {
let entries = fs::read_dir(path).map_err(|e| Error::from((path, e)))?;
for entry in entries {
let entry = entry.map_err(|e| Error::from((path, e)))?;
let path = entry.path();
let path = path.as_path();
if path.is_dir() {
files = collect_files(&path, files)?;
} else if let Some(extension) = path.extension() {
if extension == "run" {
files.push(path.to_owned());
}
}
}
Ok(files)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_init() {
let result = init().unwrap();
println!("result: {:?}", result);
assert!(false);
}
}

View file

@ -324,7 +324,7 @@ mod test {
fn match_constant_test() {
let scanner = Scanner::new("DarkAllDay");
let (_, token) = match_constant(scanner).unwrap();
assert_eq!(token.kind, TokenKind::Identifier);
assert_eq!(token.kind, TokenKind::Constant);
assert_eq!(token.value, TokenValue::String("DarkAllDay".to_string()));
assert_eq!(token.span, Span::new(0, 10));
}

View file

@ -660,12 +660,13 @@ mod test {
let tokens = tokens_for("Celldweller.EndOfAnEmpire");
let parser = Parser::new(&tokens);
let (_, node) = expr_constant(parser).unwrap();
assert_eq!(node.kind, NodeKind::Constant);
assert_eq!(
node.value.as_string().unwrap(),
"Celldweller",
"EndOfAnEmpire"
);
assert_matches!(node.kind, NodeKind::Infix);
assert_matches!(node.value, NodeValue::Infix(ref lhs, ref op, ref rhs) => {
assert_matches!(op, TokenKind::Dot);
assert_eq!(lhs.value.as_string().unwrap(), "Celldweller");
assert_eq!(rhs.value.as_string().unwrap(), "EndOfAnEmpire");
});
}
#[test]
@ -988,12 +989,17 @@ mod test {
let parser = Parser::new(&tokens);
let (_, node) = expr(parser).unwrap();
assert_matches!(node.kind, NodeKind::Call);
assert_matches!(node.value, NodeValue::Call { ref arguments } => {
assert_eq!(arguments.len(), 3);
assert_is_integer(&arguments[0], 1);
assert_is_integer(&arguments[1], 2);
assert_is_integer(&arguments[2], 3);
assert_matches!(node.kind, NodeKind::Infix);
assert_matches!(node.value, NodeValue::Infix(ref lhs, ref op, ref rhs) => {
assert_is_get_local(&lhs, "example");
assert_matches!(op, TokenKind::LeftParen);
assert_matches!(rhs.kind, NodeKind::Call);
assert_matches!(rhs.value, NodeValue::Call { ref arguments } => {
assert_eq!(arguments.len(), 3);
assert_is_integer(&arguments[0], 1);
assert_is_integer(&arguments[1], 2);
assert_is_integer(&arguments[2], 3);
});
});
}

View file

@ -6,5 +6,6 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
"outrun-common" = {path = "../outrun-common"}
"outrun-lexer" = {path = "../outrun-lexer"}
"outrun-parser" = {path = "../outrun-parser"}

View file

@ -19,11 +19,9 @@ mod ext;
mod inst;
mod machine;
mod module;
mod stack;
mod table;
pub use constant::Constant;
pub use env::Environment;
pub use inst::{Instruction, OpCode};
pub use module::Module;
pub use stack::Stack;

View file

@ -1,10 +1,10 @@
use crate::ext::{OutrunType, OutrunValue};
use crate::stack::Stack;
use outrun_common::stack::LimitedStack;
use std::collections::HashMap;
pub struct Machine<'a> {
types: HashMap<&'a str, OutrunType>,
stack: Stack<Box<OutrunValue>>,
stack: LimitedStack<Box<OutrunValue>>,
}
#[derive(Clone, Debug)]

View file

@ -1,47 +0,0 @@
use std::default::Default;
#[derive(Debug)]
pub enum StackError {
Overflow,
Underflow,
}
type Result<T> = std::result::Result<T, StackError>;
#[derive(Debug)]
pub struct Stack<T> {
stack: Vec<T>,
limit: usize,
}
impl<T> Stack<T> {
pub fn new(limit: usize) -> Stack<T> {
Stack {
stack: Vec::with_capacity(limit),
limit,
}
}
pub fn push(&mut self, value: T) -> Result<()> {
if self.stack.len() == self.limit {
return Err(StackError::Overflow);
}
self.stack.push(value);
Ok(())
}
pub fn pop(&mut self) -> Result<T> {
self.stack.pop().ok_or(StackError::Underflow)
}
pub fn peek(&self) -> Option<&T> {
self.stack.last()
}
}
impl<T> Default for Stack<T> {
fn default() -> Self {
Stack::new(4096)
}
}