aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGustav Sörnäs <gustav@sornas.net>2021-01-28 22:05:44 +0100
committerGustav Sörnäs <gustav@sornas.net>2021-01-28 22:05:44 +0100
commit8eea80192eb1d913e1d4840af3a4a2b2c5a82b9d (patch)
tree1858c7601dc56f9047427a1d333a9d4734871755
parente255b79af57d1e202816ccbb59e73115b27e9c38 (diff)
parent3a987d3dfe0014bed43151874882c5b4f20eb7af (diff)
downloadsylt-8eea80192eb1d913e1d4840af3a4a2b2c5a82b9d.tar.gz
Merge branch 'structs'
-rw-r--r--src/compiler.rs175
-rw-r--r--src/error.rs4
-rw-r--r--src/lib.rs28
-rw-r--r--src/tokenizer.rs10
-rw-r--r--src/vm.rs76
5 files changed, 277 insertions, 16 deletions
diff --git a/src/compiler.rs b/src/compiler.rs
index 70c9a51..78b2671 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -1,6 +1,8 @@
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::cell::RefCell;
+use std::collections::HashMap;
+use std::collections::hash_map::Entry;
use crate::error::{Error, ErrorKind};
use crate::tokenizer::{Token, TokenStream};
@@ -47,6 +49,13 @@ nextable_enum!(Prec {
Factor,
});
+
+#[derive(Debug, Clone)]
+pub struct Prog {
+ pub blocks: Vec<Rc<RefCell<Block>>>,
+ pub blobs: Vec<Rc<Blob>>,
+}
+
#[derive(Debug, Clone)]
pub enum Type {
Void,
@@ -56,12 +65,16 @@ pub enum Type {
Bool,
String,
Function(Vec<Type>, Box<Type>),
+ Blob(usize),
+ BlobInstance(usize),
}
impl PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Type::Void, Type::Void) => true,
+ (Type::BlobInstance(a), Type::BlobInstance(b)) => a == b,
+ (Type::Blob(a), Type::Blob(b)) => a == b,
(Type::Int, Type::Int) => true,
(Type::Float, Type::Float) => true,
(Type::Bool, Type::Bool) => true,
@@ -76,6 +89,8 @@ impl PartialEq for Type {
impl From<&Value> for Type {
fn from(value: &Value) -> Type {
match value {
+ Value::BlobInstance(i, _) => Type::BlobInstance(*i),
+ Value::Blob(i) => Type::Blob(*i),
Value::Int(_) => Type::Int,
Value::Float(_) => Type::Float,
Value::Bool(_) => Type::Bool,
@@ -97,6 +112,8 @@ impl Type {
pub fn as_value(&self) -> Value {
match self {
Type::Void => Value::Nil,
+ Type::Blob(i) => Value::Blob(*i),
+ Type::BlobInstance(i) => Value::BlobInstance(*i, Rc::new(RefCell::new(Vec::new()))),
Type::UnknownType => Value::Unkown,
Type::Int => Value::Int(1),
Type::Float => Value::Float(1.0),
@@ -164,6 +181,33 @@ impl Frame {
}
}
+#[derive(Debug, Clone)]
+pub struct Blob {
+ pub name: String,
+
+ pub name_to_field: HashMap<String, (usize, Type)>,
+}
+
+impl Blob {
+ pub fn new(name: &str) -> Self {
+ Self {
+ name: String::from(name),
+ name_to_field: 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(())
+ }
+ }
+}
+
struct Compiler {
curr: usize,
tokens: TokenStream,
@@ -175,6 +219,7 @@ struct Compiler {
errors: Vec<Error>,
blocks: Vec<Rc<RefCell<Block>>>,
+ blobs: Vec<Blob>,
}
macro_rules! push_frame {
@@ -237,6 +282,7 @@ impl Compiler {
errors: vec![],
blocks: Vec::new(),
+ blobs: Vec::new(),
}
}
@@ -475,6 +521,12 @@ impl Compiler {
return Self::find_and_capture_variable(name, self.frames.iter_mut().rev());
}
+ fn find_blob(&self, name: &str) -> Option<usize> {
+ self.blobs.iter().enumerate()
+ .find(|(_, x)| x.name == name)
+ .map(|(i, _)| i)
+ }
+
fn call(&mut self, block: &mut Block) {
expect!(self, Token::LeftParen, "Expected '(' at start of function call.");
@@ -598,6 +650,25 @@ impl Compiler {
} else {
block.add(Op::ReadLocal(var.slot), self.line());
}
+ loop {
+ match self.peek() {
+ Token::Dot => {
+ self.eat();
+ if let Token::Identifier(field) = self.eat() {
+ block.add(Op::Get(String::from(field)), self.line());
+ } else {
+ error!(self, "Expected fieldname after '.'.");
+ break;
+ }
+ }
+ Token::LeftParen => {
+ self.call(block);
+ }
+ _ => { break }
+ }
+ }
+ } else if let Some(blob) = self.find_blob(&name) {
+ block.add(Op::Constant(Value::Blob(blob)), self.line());
if self.peek() == Token::LeftParen {
self.call(block);
}
@@ -787,6 +858,89 @@ impl Compiler {
}
}
+ fn blob_statement(&mut self, _block: &mut Block) {
+ expect!(self, Token::Blob, "Expected blob when declaring a blob");
+ let name = if let Token::Identifier(name) = self.eat() {
+ name
+ } else {
+ error!(self, "Expected identifier after 'blob'.");
+ return;
+ };
+
+ expect!(self, Token::LeftBrace, "Expected 'blob' body. AKA '{'.");
+
+ let mut blob = Blob::new(&name);
+ loop {
+ if matches!(self.peek(), Token::EOF | Token::RightBrace) { break; }
+ if matches!(self.peek(), Token::Newline) { self.eat(); continue; }
+
+ let name = if let Token::Identifier(name) = self.eat() {
+ name
+ } else {
+ error!(self, "Expected identifier for field.");
+ continue;
+ };
+
+ expect!(self, Token::Colon, "Expected ':' after field name.");
+
+ let ty = if let Ok(ty) = self.parse_type() {
+ ty
+ } else {
+ error!(self, "Failed to parse blob-field type.");
+ continue;
+ };
+
+ if let Err(_) = blob.add_field(&name, ty) {
+ error!(self, format!("A field named '{}' is defined twice for '{}'", name, blob.name));
+ }
+ }
+
+ expect!(self, Token::RightBrace, "Expected '}' after 'blob' body. AKA '}'.");
+
+ self.blobs.push(blob);
+ }
+
+ fn blob_field(&mut self, block: &mut Block) {
+ let name = match self.eat() {
+ Token::Identifier(name) => name,
+ _ => unreachable!(),
+ };
+ if let Some(var) = self.find_variable(&name) {
+ if var.upvalue {
+ block.add(Op::ReadUpvalue(var.slot), self.line());
+ } else {
+ block.add(Op::ReadLocal(var.slot), self.line());
+ }
+ loop {
+ match self.peek() {
+ Token::Dot => {
+ self.eat();
+ let field = if let Token::Identifier(field) = self.eat() {
+ String::from(field)
+ } else {
+ error!(self, "Expected fieldname after '.'.");
+ return;
+ };
+
+ if self.peek() == Token::Equal {
+ self.eat();
+ self.expression(block);
+ block.add(Op::Set(field), self.line());
+ } else {
+ block.add(Op::Get(field), self.line());
+ }
+ }
+ Token::LeftParen => {
+ self.call(block);
+ }
+ _ => { break }
+ }
+ }
+ } else {
+ error!(self, format!("Using undefined variable {}.", name));
+ }
+ }
+
fn statement(&mut self, block: &mut Block) {
self.clear_panic();
@@ -795,7 +949,11 @@ impl Compiler {
self.eat();
self.expression(block);
block.add(Op::Print, self.line());
- },
+ }
+
+ (Token::Identifier(_), Token::Dot, ..) => {
+ self.blob_field(block);
+ }
(Token::Identifier(name), Token::Colon, ..) => {
self.eat();
@@ -820,6 +978,10 @@ impl Compiler {
self.assign(&name, block);
}
+ (Token::Blob, Token::Identifier(_), ..) => {
+ self.blob_statement(block);
+ }
+
(Token::If, ..) => {
self.if_statment(block);
}
@@ -853,7 +1015,8 @@ impl Compiler {
}
- pub fn compile(&mut self, name: &str, file: &Path) -> Result<Vec<Rc<RefCell<Block>>>, Vec<Error>> {
+ pub fn compile(&mut self, name: &str, file: &Path) -> Result<Prog, Vec<Error>> {
+ println!("=== START COMPILATION ===");
self.stack_mut().push(Variable {
name: String::from("/main/"),
typ: Type::Void,
@@ -877,14 +1040,18 @@ impl Compiler {
self.blocks.insert(0, Rc::new(RefCell::new(block)));
+ println!("=== END COMPILATION ===");
if self.errors.is_empty() {
- Ok(self.blocks.clone())
+ Ok(Prog {
+ blocks: self.blocks.clone(),
+ blobs: self.blobs.iter().map(|x| Rc::new(x.clone())).collect(),
+ })
} else {
Err(self.errors.clone())
}
}
}
-pub fn compile(name: &str, file: &Path, tokens: TokenStream) -> Result<Vec<Rc<RefCell<Block>>>, Vec<Error>> {
+pub fn compile(name: &str, file: &Path, tokens: TokenStream) -> Result<Prog, Vec<Error>> {
Compiler::new(file, tokens).compile(name, file)
}
diff --git a/src/error.rs b/src/error.rs
index 1430a52..60eea3b 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -33,13 +33,13 @@ impl fmt::Display for ErrorKind {
ErrorKind::TypeError(op, types) => {
let types = types
.iter()
- .fold(String::new(), |a, v| { format!("{}, {:?}", a, v) });
+ .fold(String::new(), |a, v| { format!("{}{:?}, ", a, v) });
write!(f, "{} Cannot apply {:?} to types {}", "Type Error".bold(), op, types)
}
ErrorKind::RuntimeTypeError(op, values) => {
let values = values
.iter()
- .fold(String::new(), |a, v| { format!("{}, {:?}", a, v) });
+ .fold(String::new(), |a, v| { format!("{}{:?}, ", a, v) });
write!(f, "{} Cannot apply {:?} to values {}", "Runtime Type Error".bold(), op, values)
}
ErrorKind::Assert => {
diff --git a/src/lib.rs b/src/lib.rs
index e83d11f..c190918 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,4 @@
use std::path::Path;
-use std::rc::Rc;
pub mod compiler;
pub mod tokenizer;
@@ -23,7 +22,7 @@ pub fn run(tokens: TokenStream, path: &Path, print: bool) -> Result<(), Vec<Erro
Ok(blocks) => {
let mut vm = vm::VM::new().print_blocks(print).print_ops(print);
vm.typecheck(&blocks)?;
- if let Err(e) = vm.run(Rc::clone(&blocks[0])) {
+ if let Err(e) = vm.run(&blocks) {
Err(vec![e])
} else {
Ok(())
@@ -273,6 +272,31 @@ a() <=> 4
*/
);
+ test_multiple!(
+ blob,
+ simple: "blob A {}",
+ instantiate: "blob A {}
+ a := A()",
+ field: "blob A { a: int }",
+ field_assign: "blob A { a: int }
+ a := A()
+ a.a = 2",
+ field_get: "blob A { a: int }
+ a := A()
+ a.a = 2
+ //TODO a.a <=> 2
+ 2 <=> a.a",
+ multiple_fields: "blob A {
+ a: int
+ b: int
+ }
+ a := A()
+ a.a = 2
+ a.b = 3
+ //TODO a.a + a.b <=> 5
+ 5 <=> a.a + a.b"
+ );
+
test_file!(scoping, "tests/scoping.tdy");
test_file!(for_, "tests/for.tdy");
}
diff --git a/src/tokenizer.rs b/src/tokenizer.rs
index 54712d0..7bd0849 100644
--- a/src/tokenizer.rs
+++ b/src/tokenizer.rs
@@ -24,10 +24,12 @@ pub enum Token {
Else,
#[token("for")]
For,
- #[token("in")]
- In,
- #[token("loop")]
- Loop,
+ // #[token("in")]
+ // In,
+ // #[token("loop")]
+ // Loop,
+ #[token("blob")]
+ Blob,
// TODO(ed): Remove
#[token("print")]
diff --git a/src/vm.rs b/src/vm.rs
index 5f7e0e3..4e501ba 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -8,6 +8,7 @@ use std::cell::RefCell;
use crate::compiler::Type;
use crate::error::{Error, ErrorKind};
+use crate::compiler::{Prog, Blob};
macro_rules! error {
( $thing:expr, $kind:expr) => {
@@ -20,6 +21,8 @@ macro_rules! error {
#[derive(Clone)]
pub enum Value {
+ Blob(usize),
+ BlobInstance(usize, Rc<RefCell<Vec<Value>>>),
Float(f64),
Int(i64),
Bool(bool),
@@ -74,6 +77,8 @@ impl UpValue {
impl Debug for Value {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
+ Value::Blob(i) => write!(fmt, "(blob {})", i),
+ Value::BlobInstance(i, v) => write!(fmt, "(inst {} {:?})", i, v),
Value::Float(f) => write!(fmt, "(float {})", f),
Value::Int(i) => write!(fmt, "(int {})", i),
Value::Bool(b) => write!(fmt, "(bool {})", b),
@@ -97,6 +102,8 @@ impl Value {
fn as_type(&self) -> Type {
match self {
+ Value::BlobInstance(i, _) => Type::BlobInstance(*i),
+ Value::Blob(i) => Type::Blob(*i),
Value::Float(_) => Type::Float,
Value::Int(_) => Type::Int,
Value::Bool(_) => Type::Bool,
@@ -116,6 +123,9 @@ pub enum Op {
PopUpvalue,
Constant(Value),
+ Get(String),
+ Set(String),
+
Add,
Sub,
Mul,
@@ -276,6 +286,9 @@ pub struct VM {
stack: Vec<Value>,
frames: Vec<Frame>,
+
+ blobs: Vec<Rc<Blob>>,
+
print_blocks: bool,
print_ops: bool,
}
@@ -291,6 +304,7 @@ impl VM {
upvalues: HashMap::new(),
stack: Vec::new(),
frames: Vec::new(),
+ blobs: Vec::new(),
print_blocks: false,
print_ops: false,
}
@@ -404,6 +418,27 @@ impl VM {
self.stack.push(value);
}
+ Op::Get(field) => {
+ let inst = self.stack.pop();
+ if let Some(Value::BlobInstance(ty, values)) = inst {
+ let slot = self.blobs[ty].name_to_field.get(&field).unwrap().0;
+ self.stack.push(values.borrow()[slot].clone());
+ } else {
+ error!(self, ErrorKind::RuntimeTypeError(Op::Get(field.clone()), vec![inst.unwrap()]));
+ }
+ }
+
+ Op::Set(field) => {
+ let value = self.stack.pop().unwrap();
+ let inst = self.stack.pop();
+ if let Some(Value::BlobInstance(ty, values)) = inst {
+ let slot = self.blobs[ty].name_to_field.get(&field).unwrap().0;
+ values.borrow_mut()[slot] = value;
+ } else {
+ error!(self, ErrorKind::RuntimeTypeError(Op::Get(field.clone()), vec![inst.unwrap()]));
+ }
+ }
+
Op::Neg => {
match self.stack.pop().unwrap() {
Value::Float(a) => self.stack.push(Value::Float(-a)),
@@ -552,7 +587,22 @@ impl VM {
Op::Call(num_args) => {
let new_base = self.stack.len() - 1 - num_args;
- match &self.stack[new_base] {
+ match self.stack[new_base].clone() {
+ Value::Blob(blob_id) => {
+ let blob = &self.blobs[blob_id];
+
+ let mut values = Vec::with_capacity(blob.name_to_field.len());
+ for _ in 0..values.capacity() {
+ values.push(Value::Nil);
+ }
+
+ for (slot, ty) in blob.name_to_field.values() {
+ values[*slot] = ty.as_value();
+ }
+
+ self.stack.pop();
+ self.stack.push(Value::BlobInstance(blob_id, Rc::new(RefCell::new(values))));
+ }
Value::Function(_, block) => {
let inner = block.borrow();
let args = inner.args();
@@ -620,7 +670,9 @@ impl VM {
self.frame().block.borrow().ops[self.frame().ip]);
}
- pub fn run(&mut self, block: Rc<RefCell<Block>>) -> Result<(), Error>{
+ pub fn run(&mut self, prog: &Prog) -> Result<(), Error>{
+ let block = Rc::clone(&prog.blocks[0]);
+ self.blobs = prog.blobs.clone();
self.stack.clear();
self.frames.clear();
@@ -744,6 +796,21 @@ impl VM {
Op::Call(num_args) => {
let new_base = self.stack.len() - 1 - num_args;
match self.stack[new_base].clone() {
+ Value::Blob(blob_id) => {
+ let blob = &self.blobs[blob_id];
+
+ let mut values = Vec::with_capacity(blob.name_to_field.len());
+ for _ in 0..values.capacity() {
+ values.push(Value::Nil);
+ }
+
+ for (slot, ty) in blob.name_to_field.values() {
+ values[*slot] = ty.as_value();
+ }
+
+ self.stack.pop();
+ self.stack.push(Value::BlobInstance(blob_id, Rc::new(RefCell::new(values))));
+ }
Value::Function(_, block) => {
let inner = block.borrow();
let args = inner.args();
@@ -834,10 +901,11 @@ impl VM {
errors
}
- pub fn typecheck(&mut self, blocks: &Vec<Rc<RefCell<Block>>>) -> Result<(), Vec<Error>> {
+ pub fn typecheck(&mut self, prog: &Prog) -> Result<(), Vec<Error>> {
let mut errors = Vec::new();
- for block in blocks.iter() {
+ self.blobs = prog.blobs.clone();
+ for block in prog.blocks.iter() {
errors.append(&mut self.typecheck_block(Rc::clone(block)));
}