Skip to content

Commit

Permalink
Merge pull request #67 from mica-lang/lists
Browse files Browse the repository at this point in the history
`List` type
  • Loading branch information
liquidev committed Apr 5, 2022
2 parents 4f48091 + c30a06c commit 334c52e
Show file tree
Hide file tree
Showing 18 changed files with 439 additions and 14 deletions.
2 changes: 2 additions & 0 deletions mica-hl/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,14 @@ impl Engine {
let boolean = get_dtables!("Boolean", define_boolean);
let number = get_dtables!("Number", define_number);
let string = get_dtables!("String", define_string);
let list = get_dtables!("List", define_list);
env.builtin_dtables = BuiltinDispatchTables {
nil: Gc::clone(&nil.instance_dtable),
boolean: Gc::clone(&boolean.instance_dtable),
number: Gc::clone(&number.instance_dtable),
string: Gc::clone(&string.instance_dtable),
function: Gc::new(DispatchTable::new_for_instance("Function")),
list: Gc::clone(&list.instance_dtable),
};

let mut engine = Self {
Expand Down
1 change: 0 additions & 1 deletion mica-hl/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ impl<'a> Arguments<'a> {
/// Wrapper struct for marking functions that use the with-raw-self calling convention.
///
/// This `Deref`s to the inner value.
#[doc(hidden)]
#[repr(transparent)]
pub struct RawSelf<'a>(&'a RawValue);

Expand Down
5 changes: 5 additions & 0 deletions mica-hl/src/stdlib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use mica_language::value::RawValue;

use crate::TypeBuilder;

/// Definitions of basic types provided by a standard library.
Expand All @@ -16,4 +18,7 @@ pub trait StandardLibrary {

/// Defines the `String` type using the given type builder.
fn define_string(&mut self, builder: TypeBuilder<String>) -> TypeBuilder<String>;

/// Defines the `List` type using the given type builder.
fn define_list(&mut self, builder: TypeBuilder<Vec<RawValue>>) -> TypeBuilder<Vec<RawValue>>;
}
32 changes: 31 additions & 1 deletion mica-hl/src/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::borrow::Cow;
use std::fmt;

use mica_language::gc::Gc;
use mica_language::value::{self, Closure, Struct, UserData};
use mica_language::value::{self, Closure, List, RawValue, Struct, UserData};

use crate::{Error, Object};

Expand All @@ -31,6 +31,7 @@ pub enum Value {
String(Gc<String>),
Function(Hidden<Closure>),
Struct(Hidden<Struct>),
List(Hidden<List>),
UserData(Gc<Box<dyn UserData>>),
}

Expand All @@ -46,6 +47,7 @@ impl Value {
Value::Function(_) => "Function",
// Hopefully this doesn't explode.
Value::Struct(s) => &unsafe { s.0.dtable() }.type_name,
Value::List(_) => "List",
Value::UserData(u) => &unsafe { u.dtable() }.type_name,
}
}
Expand Down Expand Up @@ -73,6 +75,14 @@ impl fmt::Display for Value {
}
}

/// **NOTE:** You should generally avoid dealing with raw values.
#[doc(hidden)]
impl From<RawValue> for Value {
fn from(raw: RawValue) -> Self {
Self::from_raw(raw)
}
}

/// The unit type translates to `Value::Nil`.
impl From<()> for Value {
fn from(_: ()) -> Self {
Expand Down Expand Up @@ -151,6 +161,16 @@ where
}
}

/// **NOTE:** You should generally avoid dealing with raw values. This method in particular could
/// cause you a bad time if you feed temporary `Value`s converted into `RawValue`s into the
/// vector.
#[doc(hidden)]
impl From<Vec<RawValue>> for Value {
fn from(v: Vec<RawValue>) -> Self {
Value::List(Hidden(Gc::new(List::new(v))))
}
}

impl<T> From<Object<T>> for Value
where
T: Any,
Expand Down Expand Up @@ -182,6 +202,16 @@ impl TryFromValue for Value {
}
}

/// **NOTE:** You should generally avoid dealing with raw values. This implementation is especially
/// unsafe as the resulting RawValue is **unmanaged**, which means it may outlive the original value
/// and cause memory safety issues.
#[doc(hidden)]
impl TryFromValue for RawValue {
fn try_from_value(value: &Value) -> Result<Self, Error> {
Ok(value.to_raw_unmanaged())
}
}

impl TryFromValue for () {
fn try_from_value(value: &Value) -> Result<Self, Error> {
if let Value::Nil = value {
Expand Down
21 changes: 20 additions & 1 deletion mica-hl/src/value/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ impl Value {
Value::String(s) => RawValue::from(gc.manage(s)),
Value::Function(f) => RawValue::from(gc.manage(&f.0)),
Value::Struct(s) => RawValue::from(gc.manage(&s.0)),
Value::List(l) => RawValue::from(gc.manage(&l.0)),
Value::UserData(u) => RawValue::from(gc.manage(u)),
}
}

/// Converts a safe value to a raw value. Gives management rights of the value to the given GC.
/// Converts a safe value to a raw value. Does not give management rights to a GC.
pub(crate) fn to_raw_unmanaged(&self) -> RawValue {
match self {
Value::Nil => RawValue::from(()),
Expand All @@ -30,6 +31,7 @@ impl Value {
Value::String(s) => RawValue::from(Gc::as_raw(s)),
Value::Function(f) => RawValue::from(Gc::as_raw(&f.0)),
Value::Struct(s) => RawValue::from(Gc::as_raw(&s.0)),
Value::List(l) => RawValue::from(Gc::as_raw(&l.0)),
Value::UserData(u) => RawValue::from(Gc::as_raw(u)),
}
}
Expand All @@ -46,6 +48,7 @@ impl Value {
Self::Function(Hidden(Gc::from_raw(raw.get_raw_function_unchecked())))
}
ValueKind::Struct => Self::Struct(Hidden(Gc::from_raw(raw.get_raw_struct_unchecked()))),
ValueKind::List => Self::List(Hidden(Gc::from_raw(raw.get_raw_list_unchecked()))),
ValueKind::UserData => Self::UserData(Gc::from_raw(raw.get_raw_user_data_unchecked())),
}
}
Expand Down Expand Up @@ -111,6 +114,14 @@ impl SelfFromRawValue for String {
}
}

impl SelfFromRawValue for Vec<RawValue> {
type Guard = ();

unsafe fn self_from_raw_value(v: &RawValue) -> Result<(&Self, Self::Guard), Error> {
Ok((&*v.get_raw_list_unchecked().get().get_mut(), ()))
}
}

impl<T> SelfFromRawValue for T
where
T: UserData,
Expand Down Expand Up @@ -150,6 +161,14 @@ where
unsafe fn mut_self_from_raw_value(value: &RawValue) -> Result<(&mut Self, Self::Guard), Error>;
}

impl MutSelfFromRawValue for Vec<RawValue> {
type Guard = ();

unsafe fn mut_self_from_raw_value(v: &RawValue) -> Result<(&mut Self, Self::Guard), Error> {
Ok((&mut *v.get_raw_list_unchecked().get().get_mut(), ()))
}
}

impl<T> MutSelfFromRawValue for T
where
T: UserData,
Expand Down
4 changes: 4 additions & 0 deletions mica-language/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ pub enum Opcode {
/// Creates a struct instance from the type at the top of the stack, with the specified amount
/// of fields.
CreateStruct,
/// Creates a list from `operand` values that are at the top of the stack.
CreateList,

/// Assigns the value at the top of the stack to a global. The value stays on the stack.
AssignGlobal,
Expand Down Expand Up @@ -746,6 +748,7 @@ pub struct BuiltinDispatchTables {
pub number: Gc<DispatchTable>,
pub string: Gc<DispatchTable>,
pub function: Gc<DispatchTable>,
pub list: Gc<DispatchTable>,
}

/// Default dispatch tables for built-in types are empty and do not implement any methods.
Expand All @@ -757,6 +760,7 @@ impl BuiltinDispatchTables {
number: Gc::new(DispatchTable::new("Number", "Boolean")),
string: Gc::new(DispatchTable::new("String", "String")),
function: Gc::new(DispatchTable::new("Function", "Function")),
list: Gc::new(DispatchTable::new("List", "List")),
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions mica-language/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,18 @@ impl<'e> CodeGenerator<'e> {
})
}

/// Generates code for a list literal.
fn generate_list(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let children = ast.children(node).unwrap();
let len =
Opr24::try_from(children.len()).map_err(|_| ast.error(node, ErrorKind::ListIsTooLong))?;
for &child in children {
self.generate_node(ast, child, Expression::Used)?;
}
self.chunk.emit((Opcode::CreateList, len));
Ok(ExpressionResult::Present)
}

/// Generates code for a `do..end` expression.
fn generate_do(&mut self, ast: &Ast, node: NodeId) -> Result<ExpressionResult, Error> {
let children = ast.children(node).unwrap();
Expand Down Expand Up @@ -1028,6 +1040,8 @@ impl<'e> CodeGenerator<'e> {

NodeKind::Identifier => self.generate_variable(ast, node)?,

NodeKind::List => self.generate_list(ast, node)?,

NodeKind::Negate | NodeKind::Not => self.generate_unary(ast, node)?,

| NodeKind::Add
Expand Down
2 changes: 2 additions & 0 deletions mica-language/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ pub enum ErrorKind {
FieldDoesNotExist(Rc<str>),
FieldOutsideOfImpl,
MissingFields(Vec<Rc<str>>),
ListIsTooLong,

// Runtime
TypeError {
Expand Down Expand Up @@ -202,6 +203,7 @@ impl std::fmt::Display for ErrorKind {
"the following fields were not assigned in this constructor: {fields}"
)
}
Self::ListIsTooLong => write!(f, "list literal has too many elements"),

Self::TypeError { expected, got } => {
write!(f, "type mismatch, expected {expected} but got {got}")
Expand Down
10 changes: 10 additions & 0 deletions mica-language/src/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,16 @@ impl Memory {
}
}
}
ValueKind::List => {
let raw = value.get_raw_list_unchecked();
if !raw.get_mem().reachable.get() {
raw.mark_reachable();
let elements = raw.get().as_slice();
for &element in elements {
self.gray_stack.push(element);
}
}
}
ValueKind::Struct => {
let raw = value.get_raw_struct_unchecked();
if !raw.get_mem().reachable.get() {
Expand Down
10 changes: 7 additions & 3 deletions mica-language/src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ pub enum TokenKind {
Dot, // .
At, // @

LeftParen, // (
RightParen, // )
Comma, // ,
LeftParen, // (
RightParen, // )
LeftBracket, // [
RightBracket, // ]
Comma, // ,

Eof,
}
Expand Down Expand Up @@ -486,6 +488,8 @@ impl Lexer {

'(' => Ok(self.single_char_token(TokenKind::LeftParen)),
')' => Ok(self.single_char_token(TokenKind::RightParen)),
'[' => Ok(self.single_char_token(TokenKind::LeftBracket)),
']' => Ok(self.single_char_token(TokenKind::RightBracket)),
',' => Ok(self.single_char_token(TokenKind::Comma)),
Self::EOF => Ok(self.token(TokenKind::Eof)),
other => Err(self.error(ErrorKind::InvalidCharacter(other))),
Expand Down
15 changes: 15 additions & 0 deletions mica-language/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,20 @@ impl Parser {
}
}

/// Parses a list literal.
fn parse_list(&mut self, token: Token) -> Result<NodeId, Error> {
let mut elements = Vec::new();
self.parse_comma_separated(&mut elements, TokenKind::RightBracket, |p| {
p.parse_expression(0)
})?;
Ok(self
.ast
.build_node(NodeKind::List, ())
.with_location(token.location)
.with_children(elements)
.done())
}

/// Parses a `do` block.
fn parse_do_block(&mut self, token: Token) -> Result<NodeId, Error> {
let mut children = Vec::new();
Expand Down Expand Up @@ -371,6 +385,7 @@ impl Parser {
}
Ok(inner)
}
TokenKind::LeftBracket => self.parse_list(token),

TokenKind::Do => self.parse_do_block(token),
TokenKind::If => self.parse_if_expression(token),
Expand Down
Loading

0 comments on commit 334c52e

Please sign in to comment.