aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2021-02-05 21:04:43 +0100
committerGustav Sörnäs <gustav@sornas.net>2021-02-05 21:04:43 +0100
commit98df292fe4142fcc836013341579ed4437a2a464 (patch)
tree7a94566f9e7d441db27fc2be5fc9dc143e041faa /src
parentb277ecb9912f231ae96ad40020163ea2a003846b (diff)
parentc43331a6e49c7fac5fcc5d61d6ff3d8d2221dbe0 (diff)
downloadsylt-98df292fe4142fcc836013341579ed4437a2a464.tar.gz
Merge remote-tracking branch 'origin/document/public'
Diffstat (limited to 'src')
-rw-r--r--src/compiler.rs18
-rw-r--r--src/error.rs7
-rw-r--r--src/lib.rs124
-rw-r--r--src/vm.rs68
4 files changed, 103 insertions, 114 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index dcd0317..d12ee4b 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -103,7 +103,7 @@ impl Frame {
}
}
-struct Compiler {
+pub(crate) struct Compiler {
curr: usize,
tokens: TokenStream,
current_file: PathBuf,
@@ -130,7 +130,7 @@ macro_rules! push_frame {
});
// Return value stored as a variable
- $compiler.define_variable("", Type::UnknownType, &mut $block).unwrap();
+ $compiler.define_variable("", Type::Unknown, &mut $block).unwrap();
$code
$compiler.frames.pop().unwrap();
@@ -167,7 +167,7 @@ fn add_op(compiler: &Compiler, block: &mut Block, op: Op) -> usize {
}
impl Compiler {
- pub fn new(current_file: &Path, tokens: TokenStream) -> Self {
+ pub(crate) fn new(current_file: &Path, tokens: TokenStream) -> Self {
Self {
curr: 0,
tokens,
@@ -577,7 +577,7 @@ impl Compiler {
self.scope(&mut function_block);
for var in self.frame().upvalues.iter() {
- function_block.ups.push((var.outer_slot, var.outer_upvalue, var.typ.clone()));
+ function_block.upvalues.push((var.outer_slot, var.outer_upvalue, var.typ.clone()));
}
});
@@ -777,7 +777,7 @@ impl Compiler {
(Token::Identifier(name), Token::ColonEqual, ..) => {
self.eat();
self.eat();
- self.definition_statement(&name, Type::UnknownType, block);
+ self.definition_statement(&name, Type::Unknown, block);
}
(Token::Comma, ..) => {}
@@ -1032,7 +1032,7 @@ impl Compiler {
(Token::Identifier(name), Token::ColonEqual, ..) => {
self.eat();
self.eat();
- self.definition_statement(&name, Type::UnknownType, block);
+ self.definition_statement(&name, Type::Unknown, block);
}
(Token::Blob, Token::Identifier(_), ..) => {
@@ -1072,7 +1072,7 @@ impl Compiler {
}
- pub fn compile(&mut self, name: &str, file: &Path, functions: &[(String, RustFunction)]) -> Result<Prog, Vec<Error>> {
+ pub(crate) fn compile(&mut self, name: &str, file: &Path, functions: &[(String, RustFunction)]) -> Result<Prog, Vec<Error>> {
self.functions = functions
.to_vec()
.into_iter()
@@ -1113,7 +1113,3 @@ impl Compiler {
}
}
}
-
-pub fn compile(name: &str, file: &Path, tokens: TokenStream, functions: &[(String, RustFunction)]) -> Result<Prog, Vec<Error>> {
- Compiler::new(file, tokens).compile(name, file, functions)
-}
diff --git a/src/error.rs b/src/error.rs
index e73d863..deab89e 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -12,15 +12,18 @@ use crate::tokenizer::Token;
#[derive(Debug, Clone)]
pub enum ErrorKind {
TypeError(Op, Vec<Type>),
+ /// (External function, parameters)
ExternTypeMismatch(String, Vec<Type>),
RuntimeTypeError(Op, Vec<Value>),
+ /// (Indexed value, length, index)
IndexOutOfBounds(Value, usize, usize),
- Assert,
+ AssertFailed,
InvalidProgram,
Unreachable,
+ /// (line, token)
SyntaxError(usize, Token),
}
@@ -53,7 +56,7 @@ impl fmt::Display for ErrorKind {
.fold(String::new(), |a, v| { format!("{}{:?}, ", a, v) });
write!(f, "{} Cannot apply {:?} to values {}", "Runtime Type Error".bold(), op, values)
}
- ErrorKind::Assert => {
+ ErrorKind::AssertFailed => {
write!(f, "{}", "Assertion failed".bold())
}
ErrorKind::SyntaxError(line, token) => {
diff --git a/src/lib.rs b/src/lib.rs
index d43cc8b..cca1a58 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,23 +12,24 @@ use tokenizer::TokenStream;
use crate::error::ErrorKind;
-pub mod compiler;
pub mod error;
-pub mod tokenizer;
pub mod vm;
-pub fn run_file(path: &Path, print: bool, functions: Vec<(String, RustFunction)>) -> Result<(), Vec<Error>> {
- run(tokenizer::file_to_tokens(path), path, print, functions)
-}
+mod compiler;
+mod tokenizer;
+/// Compiles a file and links the supplied functions as callable external
+/// functions. Use this if you want your programs to be able to yield.
pub fn compile_file(path: &Path,
print: bool,
functions: Vec<(String, RustFunction)>
) -> Result<vm::VM, Vec<Error>> {
let tokens = tokenizer::file_to_tokens(path);
- match compiler::compile("main", path, tokens, &functions) {
+ match compiler::Compiler::new(path, tokens).compile("main", path, &functions) {
Ok(prog) => {
- let mut vm = vm::VM::new().print_blocks(print).print_ops(print);
+ let mut vm = vm::VM::new();
+ vm.print_blocks = print;
+ vm.print_ops = print;
vm.typecheck(&prog)?;
vm.init(&prog);
Ok(vm)
@@ -37,14 +38,26 @@ pub fn compile_file(path: &Path,
}
}
+/// Compiles, links and runs the given file. Supplied functions are callable
+/// external functions. If you want your program to be able to yield, use
+/// [compile_file].
+pub fn run_file(path: &Path, print: bool, functions: Vec<(String, RustFunction)>) -> Result<(), Vec<Error>> {
+ run(tokenizer::file_to_tokens(path), path, print, functions)
+}
+
+/// Compile and run a string containing source code. The supplied functions are
+/// linked as callable external functions. This is useful for short test
+/// programs.
pub fn run_string(s: &str, print: bool, functions: Vec<(String, RustFunction)>) -> Result<(), Vec<Error>> {
run(tokenizer::string_to_tokens(s), Path::new("builtin"), print, functions)
}
-pub fn run(tokens: TokenStream, path: &Path, print: bool, functions: Vec<(String, RustFunction)>) -> Result<(), Vec<Error>> {
- match compiler::compile("main", path, tokens, &functions) {
+fn run(tokens: TokenStream, path: &Path, print: bool, functions: Vec<(String, RustFunction)>) -> Result<(), Vec<Error>> {
+ match compiler::Compiler::new(path, tokens).compile("main", path, &functions) {
Ok(prog) => {
- let mut vm = vm::VM::new().print_blocks(print).print_ops(print);
+ let mut vm = vm::VM::new();
+ vm.print_blocks = print;
+ vm.print_ops = print;
vm.typecheck(&prog)?;
vm.init(&prog);
if let Err(e) = vm.run() {
@@ -57,12 +70,14 @@ pub fn run(tokens: TokenStream, path: &Path, print: bool, functions: Vec<(String
}
}
+/// A linkable external function. Created either manually or using
+/// [sylt_macro::extern_function].
pub type RustFunction = fn(&[Value], bool) -> Result<Value, ErrorKind>;
#[derive(Debug, Clone)]
pub enum Type {
Void,
- UnknownType,
+ Unknown,
Int,
Float,
Bool,
@@ -117,15 +132,6 @@ impl From<Value> for Type {
}
}
-impl Type {
- pub fn is_unkown(&self) -> bool {
- match self {
- Type::UnknownType => true,
- _ => false,
- }
- }
-}
-
impl From<&Type> for Value {
fn from(ty: &Type) -> Self {
match ty {
@@ -135,7 +141,7 @@ impl From<&Type> for Value {
Type::Tuple(fields) => {
Value::Tuple(Rc::new(fields.iter().map(Value::from).collect()))
}
- Type::UnknownType => Value::Unkown,
+ Type::Unknown => Value::Unknown,
Type::Int => Value::Int(1),
Type::Float => Value::Float(1.0),
Type::Bool => Value::Bool(true),
@@ -165,7 +171,7 @@ pub enum Value {
String(Rc<String>),
Function(Vec<Rc<RefCell<UpValue>>>, Rc<RefCell<Block>>),
ExternFunction(usize),
- Unkown,
+ Unknown,
Nil,
}
@@ -180,7 +186,7 @@ impl Debug for Value {
Value::String(s) => write!(fmt, "(string \"{}\")", s),
Value::Function(_, block) => write!(fmt, "(fn {}: {:?})", block.borrow().name, block.borrow().ty),
Value::ExternFunction(slot) => write!(fmt, "(extern fn {})", slot),
- Value::Unkown => write!(fmt, "(unkown)"),
+ Value::Unknown => write!(fmt, "(unknown)"),
Value::Nil => write!(fmt, "(nil)"),
Value::Tuple(v) => write!(fmt, "({:?})", v),
}
@@ -205,6 +211,7 @@ impl Value {
}
}
+#[doc(hidden)]
#[derive(Clone, Debug)]
pub struct UpValue {
slot: usize,
@@ -249,26 +256,27 @@ impl UpValue {
#[derive(Debug, Clone)]
pub struct Blob {
pub name: String,
-
- pub name_to_field: HashMap<String, (usize, Type)>,
+ /// Maps field names to their slot and type.
+ pub fields: HashMap<String, (usize, Type)>,
}
impl Blob {
- pub fn new(name: &str) -> Self {
+ fn new(name: &str) -> Self {
Self {
name: String::from(name),
- name_to_field: HashMap::new(),
+ fields: HashMap::new(),
}
}
- pub fn add_field(&mut self, name: &str, ty: Type) -> Result<(), ()> {
- let size = self.name_to_field.len();
- let entry = self.name_to_field.entry(String::from(name));
- if matches!(entry, Entry::Occupied(_)) {
- Err(())
- } else {
- entry.or_insert((size, ty));
- Ok(())
+ fn add_field(&mut self, name: &str, ty: Type) -> Result<(), ()> {
+ let size = self.fields.len();
+ let entry = self.fields.entry(String::from(name));
+ match entry {
+ Entry::Occupied(_) => Err(()),
+ Entry::Vacant(v) => {
+ v.insert((size, ty));
+ Ok(())
+ }
}
}
}
@@ -496,8 +504,8 @@ mod op {
pub fn neg(value: &Value) -> Value {
match value {
- Value::Float(a) => Value::Float(-a),
- Value::Int(a) => Value::Int(-a),
+ Value::Float(a) => Value::Float(-*a),
+ Value::Int(a) => Value::Int(-*a),
Value::Tuple(a) => tuple_un_op(a, neg),
_ => Value::Nil,
}
@@ -505,7 +513,7 @@ mod op {
pub fn not(value: &Value) -> Value {
match value {
- Value::Bool(a) => Value::Bool(!a),
+ Value::Bool(a) => Value::Bool(!*a),
Value::Tuple(a) => tuple_un_op(a, not),
_ => Value::Nil,
}
@@ -590,21 +598,21 @@ mod op {
#[derive(Debug)]
pub struct Block {
pub ty: Type,
- pub ups: Vec<(usize, bool, Type)>,
+ upvalues: Vec<(usize, bool, Type)>,
pub name: String,
pub file: PathBuf,
- pub ops: Vec<Op>,
- pub last_line_offset: usize,
- pub line_offsets: HashMap<usize, usize>,
- pub line: usize,
+ ops: Vec<Op>,
+ last_line_offset: usize,
+ line_offsets: HashMap<usize, usize>,
+ line: usize,
}
impl Block {
- pub fn new(name: &str, file: &Path, line: usize) -> Self {
+ fn new(name: &str, file: &Path, line: usize) -> Self {
Self {
ty: Type::Void,
- ups: Vec::new(),
+ upvalues: Vec::new(),
name: String::from(name),
file: file.to_owned(),
ops: Vec::new(),
@@ -614,8 +622,8 @@ impl Block {
}
}
- /// Used to create empty functions.
- pub fn empty_with_type(ty: &Type) -> Self {
+ // Used to create empty functions.
+ fn empty_with_type(ty: &Type) -> Self {
let mut block = Block::new("/empty/", Path::new(""), 0);
block.ty = ty.clone();
block
@@ -637,18 +645,14 @@ impl Block {
}
}
- pub fn id(&self) -> (PathBuf, usize) {
- (self.file.clone(), self.line)
- }
-
- pub fn add_line(&mut self, token_position: usize) {
+ fn add_line(&mut self, token_position: usize) {
if token_position != self.last_line_offset {
self.line_offsets.insert(self.curr(), token_position);
self.last_line_offset = token_position;
}
}
- pub fn line(&self, ip: usize) -> usize {
+ fn line(&self, ip: usize) -> usize {
for i in (0..=ip).rev() {
if let Some(line) = self.line_offsets.get(&i) {
return *line;
@@ -667,32 +671,28 @@ impl Block {
}
println!("{:05} {:?}", i.blue(), s);
}
- println!("");
- }
-
- pub fn last_instruction(&mut self) -> &Op {
- self.ops.last().unwrap()
+ println!();
}
- pub fn add(&mut self, op: Op, token_position: usize) -> usize {
+ fn add(&mut self, op: Op, token_position: usize) -> usize {
let len = self.curr();
self.add_line(token_position);
self.ops.push(op);
len
}
- pub fn add_from(&mut self, ops: &[Op], token_position: usize) -> usize {
+ fn add_from(&mut self, ops: &[Op], token_position: usize) -> usize {
let len = self.curr();
self.add_line(token_position);
self.ops.extend_from_slice(ops);
len
}
- pub fn curr(&self) -> usize {
+ fn curr(&self) -> usize {
self.ops.len()
}
- pub fn patch(&mut self, op: Op, pos: usize) {
+ fn patch(&mut self, op: Op, pos: usize) {
self.ops[pos] = op;
}
}
diff --git a/src/vm.rs b/src/vm.rs
index eabe382..5a7b471 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -9,7 +9,7 @@ use owo_colors::OwoColorize;
use crate::{Blob, Block, Op, Prog, UpValue, Value, op};
use crate::error::{Error, ErrorKind};
use crate::RustFunction;
-pub use crate::Type;
+use crate::Type;
macro_rules! error {
( $thing:expr, $kind:expr) => {
@@ -59,8 +59,8 @@ pub struct VM {
blobs: Vec<Rc<Blob>>,
- print_blocks: bool,
- print_ops: bool,
+ pub print_blocks: bool,
+ pub print_ops: bool,
extern_functions: Vec<RustFunction>,
@@ -69,12 +69,15 @@ pub struct VM {
#[derive(Eq, PartialEq)]
pub enum OpResult {
Yield,
- Continue,
Done,
+
+ // Will never be returned.
+ #[doc(hidden)]
+ Continue,
}
impl VM {
- pub fn new() -> Self {
+ pub(crate) fn new() -> Self {
Self {
upvalues: HashMap::new(),
@@ -88,18 +91,6 @@ impl VM {
}
}
- /// Tells the VM to dump all the blocks to STDOUT.
- pub fn print_blocks(mut self, b: bool) -> Self {
- self.print_blocks = b;
- self
- }
-
- /// Tells the VM to dump all the ops as they are run to STDOUT.
- pub fn print_ops(mut self, b: bool) -> Self {
- self.print_ops = b;
- self
- }
-
fn drop_upvalue(&mut self, slot: usize, value: Value) {
if let Entry::Occupied(entry) = self.upvalues.entry(slot) {
entry.get().borrow_mut().close(value);
@@ -209,7 +200,7 @@ impl VM {
let value = match value {
Value::Function(_, block) => {
let mut ups = Vec::new();
- for (slot, is_up, _) in block.borrow().ups.iter() {
+ for (slot, is_up, _) in block.borrow().upvalues.iter() {
let up = if *is_up {
if let Value::Function(local_ups, _) = &self.stack[offset] {
Rc::clone(&local_ups[*slot])
@@ -252,7 +243,7 @@ impl VM {
Op::Get(field) => {
let inst = self.pop();
if let Value::BlobInstance(ty, values) = inst {
- let slot = self.blobs[ty].name_to_field.get(&field).unwrap().0;
+ let slot = self.blobs[ty].fields.get(&field).unwrap().0;
self.push(values.borrow()[slot].clone());
} else {
error!(self, ErrorKind::RuntimeTypeError(Op::Get(field.clone()), vec![inst]));
@@ -262,7 +253,7 @@ impl VM {
Op::Set(field) => {
let (inst, value) = self.poppop();
if let Value::BlobInstance(ty, values) = inst {
- let slot = self.blobs[ty].name_to_field.get(&field).unwrap().0;
+ let slot = self.blobs[ty].fields.get(&field).unwrap().0;
values.borrow_mut()[slot] = value;
} else {
error!(self, ErrorKind::RuntimeTypeError(Op::Get(field.clone()), vec![inst]));
@@ -305,7 +296,7 @@ impl VM {
Op::Assert => {
if matches!(self.pop(), Value::Bool(false)) {
- error!(self, ErrorKind::Assert);
+ error!(self, ErrorKind::AssertFailed);
}
self.push(Value::Bool(true));
}
@@ -349,7 +340,7 @@ impl VM {
Value::Blob(blob_id) => {
let blob = &self.blobs[blob_id];
- let mut values = Vec::with_capacity(blob.name_to_field.len());
+ let mut values = Vec::with_capacity(blob.fields.len());
for _ in 0..values.capacity() {
values.push(Value::Nil);
}
@@ -416,7 +407,7 @@ impl VM {
Ok(OpResult::Continue)
}
- pub fn print_stack(&self) {
+ fn print_stack(&self) {
let start = self.frame().stack_offset;
print!(" {:3} [", start);
for (i, s) in self.stack.iter().skip(start).enumerate() {
@@ -433,8 +424,8 @@ impl VM {
self.frame().block.borrow().ops[self.frame().ip]);
}
- /// Initalizes the VM for running, run cannot be called before this.
- pub fn init(&mut self, prog: &Prog) {
+ // Initalizes the VM for running. Run cannot be called before this.
+ pub(crate) fn init(&mut self, prog: &Prog) {
let block = Rc::clone(&prog.blocks[0]);
self.blobs = prog.blobs.clone();
self.extern_functions = prog.functions.clone();
@@ -452,7 +443,6 @@ impl VM {
/// Simulates the program.
pub fn run(&mut self) -> Result<OpResult, Error> {
-
if self.print_blocks {
println!("\n [[{}]]\n", "RUNNING".red());
self.frame().block.borrow().debug_print();
@@ -485,7 +475,7 @@ impl VM {
self.push(Value::Function(Vec::new(), block.clone()));
let mut types = Vec::new();
- for (slot, is_up, ty) in block.borrow().ups.iter() {
+ for (slot, is_up, ty) in block.borrow().upvalues.iter() {
if *is_up {
types.push(ty.clone());
} else {
@@ -494,11 +484,11 @@ impl VM {
}
let mut block_mut = block.borrow_mut();
- for (i, (_, is_up, ty)) in block_mut.ups.iter_mut().enumerate() {
+ for (i, (_, is_up, ty)) in block_mut.upvalues.iter_mut().enumerate() {
if *is_up { continue; }
let suggestion = &types[i];
- if ty.is_unkown() {
+ if matches!(ty, Type::Unknown) {
*ty = suggestion.clone();
} else {
if ty != suggestion {
@@ -519,7 +509,7 @@ impl VM {
Op::Get(field) => {
let inst = self.pop();
if let Value::BlobInstance(ty, _) = inst {
- let value = Value::from(&self.blobs[ty].name_to_field.get(&field).unwrap().1);
+ let value = Value::from(&self.blobs[ty].fields.get(&field).unwrap().1);
self.push(value);
} else {
self.push(Value::Nil);
@@ -532,7 +522,7 @@ impl VM {
let inst = self.pop();
if let Value::BlobInstance(ty, _) = inst {
- let ty = &self.blobs[ty].name_to_field.get(&field).unwrap().1;
+ let ty = &self.blobs[ty].fields.get(&field).unwrap().1;
if ty != &Type::from(&value) {
error!(self, ErrorKind::RuntimeTypeError(Op::Set(field.clone()), vec![inst]));
}
@@ -546,12 +536,12 @@ impl VM {
}
Op::ReadUpvalue(slot) => {
- let value = Value::from(&self.frame().block.borrow().ups[slot].2);
+ let value = Value::from(&self.frame().block.borrow().upvalues[slot].2);
self.push(value);
}
Op::AssignUpvalue(slot) => {
- let var = self.frame().block.borrow().ups[slot].2.clone();
+ let var = self.frame().block.borrow().upvalues[slot].2.clone();
let up = self.pop().into();
if var != up {
error!(self, ErrorKind::TypeError(op, vec![var, up]),
@@ -577,8 +567,8 @@ impl VM {
Op::Define(ref ty) => {
let top_type = self.stack.last().unwrap().into();
match (ty, top_type) {
- (Type::UnknownType, top_type)
- if top_type != Type::UnknownType => {}
+ (Type::Unknown, top_type)
+ if top_type != Type::Unknown => {}
(a, b) if a != &b => {
error!(self,
ErrorKind::TypeError(
@@ -597,12 +587,12 @@ impl VM {
Value::Blob(blob_id) => {
let blob = &self.blobs[blob_id];
- let mut values = Vec::with_capacity(blob.name_to_field.len());
+ let mut values = Vec::with_capacity(blob.fields.len());
for _ in 0..values.capacity() {
values.push(Value::Nil);
}
- for (slot, ty) in blob.name_to_field.values() {
+ for (slot, ty) in blob.fields.values() {
values[*slot] = ty.into();
}
@@ -712,8 +702,8 @@ impl VM {
errors
}
- /// Checks the program for type errors.
- pub fn typecheck(&mut self, prog: &Prog) -> Result<(), Vec<Error>> {
+ // Checks the program for type errors.
+ pub(crate) fn typecheck(&mut self, prog: &Prog) -> Result<(), Vec<Error>> {
let mut errors = Vec::new();
self.blobs = prog.blobs.clone();