Skip to content

Commit

Permalink
Support for byte vectors (#25)
Browse files Browse the repository at this point in the history
This adds support for byte-vectors in the compiler and the VM.
It doesn't add procedures to deal with byte-vectors yet.
  • Loading branch information
certainty authored Sep 4, 2021
1 parent 20c0371 commit d934f9f
Show file tree
Hide file tree
Showing 15 changed files with 234 additions and 51 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ how fare I can bring it.

### Language Support

- [ ] literals
- [x] literals
- [x] bool
- [x] symbol
- [x] char
- [x] number
- [x] vector
- [ ] byte-vector
- [x] byte-vector
- [x] proper list
- [x] improper list
- [x] string
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/frontend/parser/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ impl CoreParser {
SExpression::String(_) => ParseResult::accept(LiteralExpression::from(datum)),
SExpression::Number(_) => ParseResult::accept(LiteralExpression::from(datum)),
SExpression::Vector(_) => ParseResult::accept(LiteralExpression::from(datum)),
_ => ParseResult::ignore("Expected literal", datum.source_location()),
SExpression::ByteVector(_) => ParseResult::accept(LiteralExpression::from(datum)),
_ => ParseResult::ignore("Expected literal expression", datum.source_location()),
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/frontend/reader/datum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ impl Datum {
Self::new(SExpression::vector(elements), location.into())
}

pub fn byte_vector<I, L: Into<Location>>(bytes: I, location: L) -> Self
where
I: IntoIterator,
I::Item: Into<u8>,
{
Self::new(SExpression::byte_vector(bytes), location.into())
}

/// Create an s-expression from a value, if that's possible.
///
/// Code is data, but this implementation doesn't use the same representation
Expand Down
10 changes: 10 additions & 0 deletions src/compiler/frontend/reader/sexp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub use parser::*;
pub mod abbreviation;
pub mod boolean;
pub mod byte_vector;
pub mod character;
pub mod list;
pub mod number;
Expand All @@ -9,6 +10,7 @@ pub mod string;
pub mod symbol;
pub mod vector;
pub mod whitespace;

use super::datum::Datum;
use crate::compiler::frontend::syntax::symbol::Symbol;
use crate::vm::value::number::real::RealNumber;
Expand Down Expand Up @@ -71,6 +73,14 @@ impl SExpression {
Self::Vector(elements.into_iter().map(Into::into).collect())
}

pub fn byte_vector<I>(bytes: I) -> Self
where
I: IntoIterator,
I::Item: Into<u8>,
{
Self::ByteVector(bytes.into_iter().map(Into::into).collect())
}

pub fn is_proper_list(&self) -> bool {
match self {
Self::List(_) => true,
Expand Down
72 changes: 72 additions & 0 deletions src/compiler/frontend/reader/sexp/byte_vector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use super::number;
use super::whitespace::parse_inter_token_space;
use super::{map_datum, Input, ParseResult};
use crate::compiler::frontend::reader::{datum::Datum, sexp::SExpression};
use crate::vm::value::number::{Number, SchemeNumber};
use nom::bytes::complete::tag;
use nom::character::complete::char;
use nom::error::{ErrorKind, ParseError};
use nom::multi::many0;
use nom::sequence::delimited;

/// Parse proper list
/// Ref: r7rs 7.1.2
/// ```grammar
/// <byte-vector> -> #u8(<byte>*)
/// <byte> -> (any exact number between 0 and 255)
/// ```

#[inline]
pub fn parse(input: Input) -> ParseResult<Datum> {
let byte = delimited(parse_inter_token_space, parse_byte, parse_inter_token_space);
let vector = delimited(tag("#u8("), many0(byte), char(')'));

map_datum(vector, SExpression::byte_vector)(input)
}

fn parse_byte(input: Input) -> ParseResult<u8> {
let (s, num) = number::parse(input)?;

match num.s_expression() {
SExpression::Number(n)
if n.is_exact() && n >= &Number::fixnum(0) && n <= &Number::fixnum(255) =>
{
ParseResult::Ok((s, n.to_u8().unwrap()))
}
_ => {
return ParseResult::Err(nom::Err::Error(nom::error::Error::from_error_kind(
s,
ErrorKind::Verify,
)))
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::compiler::frontend::reader::tests::*;

#[test]
fn read_byte_vector() {
assert_parse_as("#u8()", Datum::byte_vector(Vec::<u8>::new(), 0..5));

assert_parse_as(
"#u8(10 255 0)",
Datum::byte_vector(vec![10 as u8, 255 as u8, 0 as u8], 0..16),
);
}

#[test]
fn read_byte_vector_num_literals() {
assert_parse_as(
"#u8(#xff #o7)",
Datum::byte_vector(vec![255 as u8, 7 as u8], 0..13),
);
}

#[test]
fn read_byte_vector_out_of_range() {
assert_parse_error("#u8(300 355)");
}
}
5 changes: 4 additions & 1 deletion src/compiler/frontend/reader/sexp/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use nom_locate::{position, LocatedSpan};
use crate::compiler::frontend::reader::{datum::Datum, sexp::SExpression};
use crate::compiler::source::SourceId;

use super::{abbreviation, boolean, character, list, number, string, symbol, vector, whitespace};
use super::{
abbreviation, boolean, byte_vector, character, list, number, string, symbol, vector, whitespace,
};

pub(crate) type Input<'a> = LocatedSpan<&'a str, SourceId>;

Expand All @@ -30,6 +32,7 @@ fn parse_simple_datum(input: Input) -> ParseResult<Datum> {
context("boolean", boolean::parse),
context("symbol", symbol::parse),
context("string", string::parse),
context("byte-vector", byte_vector::parse),
)),
)(input)
}
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/frontend/reader/sexp/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ enum StringElement<'a> {
Continuation,
}

pub fn parse<'a>(input: Input<'a>) -> ParseResult<'a, Datum> {
pub fn parse(input: Input) -> ParseResult<Datum> {
let string_elements = fold_many0(
parse_string_element,
String::new(),
Expand All @@ -44,7 +44,7 @@ pub fn parse<'a>(input: Input<'a>) -> ParseResult<'a, Datum> {
map_datum(string_literal, SExpression::String)(input)
}

fn parse_string_element<'a>(input: Input<'a>) -> ParseResult<'a, StringElement<'a>> {
fn parse_string_element(input: Input) -> ParseResult<StringElement> {
alt((
map(parse_mnemonic_escape, StringElement::EscapedChar),
map(parse_string_escape, StringElement::EscapedChar),
Expand Down Expand Up @@ -76,7 +76,7 @@ fn parse_string_continuation<'a>(input: Input<'a>) -> ParseResult<'a, ()> {
}

#[inline]
fn parse_string_literal<'a>(input: Input<'a>) -> ParseResult<'a, &'a str> {
fn parse_string_literal(input: Input) -> ParseResult<&str> {
let (s, v) = is_not("\\\"")(input)?;

if v.fragment().is_empty() {
Expand Down
5 changes: 5 additions & 0 deletions src/vm/scheme/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ impl Writer {

format!("#({})", body.join(" "))
}
Value::ByteVector(elts) => {
let body: Vec<String> = elts.iter().map(|e| format!("{}", e)).collect();

format!("#u8({})", body.join(" "))
}
Value::ProperList(elts) => {
let body: Vec<String> = elts
.iter()
Expand Down
50 changes: 21 additions & 29 deletions src/vm/value.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#[cfg(test)]
pub mod arbitrary;
pub mod byte_vector;
pub mod closure;
pub mod equality;
pub mod error;
Expand All @@ -8,6 +9,8 @@ pub mod number;
pub mod procedure;
pub mod string;
pub mod symbol;
pub mod vector;

use self::{string::InternedString, symbol::Symbol};
use crate::compiler::frontend::reader::{datum::Datum, sexp::SExpression};
use crate::compiler::utils::string_table::StringTable;
Expand Down Expand Up @@ -65,7 +68,8 @@ pub enum Value {
Symbol(Symbol),
Char(char),
Number(number::Number),
Vector(Vec<Value>),
Vector(vector::Vector),
ByteVector(byte_vector::ByteVector),
InternedString(InternedString),
UninternedString(std::string::String),
ProperList(list::List),
Expand All @@ -84,32 +88,6 @@ impl Value {
}
}

impl SchemeEqual<Vec<Value>> for Vec<Value> {
fn is_eq(&self, other: &Vec<Value>) -> bool {
if self.len() != other.len() {
return false;
} else {
self.iter().zip(other.iter()).all(|(a, b)| a.is_eq(b))
}
}

fn is_eqv(&self, other: &Vec<Value>) -> bool {
if self.len() != other.len() {
return false;
} else {
self.iter().zip(other.iter()).all(|(a, b)| a.is_eqv(b))
}
}

fn is_equal(&self, other: &Vec<Value>) -> bool {
if self.len() != other.len() {
return false;
} else {
self.iter().zip(other.iter()).all(|(a, b)| a.is_equal(b))
}
}
}

impl SchemeEqual<Value> for Value {
fn is_eq(&self, other: &Value) -> bool {
match (self, other) {
Expand All @@ -119,6 +97,7 @@ impl SchemeEqual<Value> for Value {
(Value::UninternedString(_), Value::InternedString(_)) => false,
(Value::UninternedString(_), Value::UninternedString(_)) => false,
(Value::Vector(lhs), Value::Vector(rhs)) => lhs.is_eq(rhs),
(Value::ByteVector(lhs), Value::ByteVector(rhs)) => lhs.is_eq(rhs),
(Value::ProperList(lhs), Value::ProperList(rhs)) => lhs.is_eq(rhs),
(Value::ImproperList(lhs_head, lhs_tail), Value::ImproperList(rhs_head, rhs_tail)) => {
lhs_head.is_eq(rhs_head) && lhs_tail.is_eq(rhs_tail)
Expand All @@ -139,6 +118,7 @@ impl SchemeEqual<Value> for Value {
(Value::UninternedString(_), Value::InternedString(_)) => false,
(Value::UninternedString(_), Value::UninternedString(_)) => false,
(Value::Vector(lhs), Value::Vector(rhs)) => lhs.is_eqv(rhs),
(Value::ByteVector(lhs), Value::ByteVector(rhs)) => lhs.is_eqv(rhs),
(Value::ProperList(lhs), Value::ProperList(rhs)) => lhs.is_eqv(rhs),
(Value::ImproperList(lhs_head, lhs_tail), Value::ImproperList(rhs_head, rhs_tail)) => {
lhs_head.is_eqv(rhs_head) && lhs_tail.is_eqv(rhs_tail)
Expand All @@ -161,6 +141,7 @@ impl SchemeEqual<Value> for Value {
}
(Value::UninternedString(lhs), Value::UninternedString(rhs)) => lhs == rhs,
(Value::Vector(lhs), Value::Vector(rhs)) => lhs.is_equal(rhs),
(Value::ByteVector(lhs), Value::ByteVector(rhs)) => lhs.is_equal(rhs),
(Value::ProperList(lhs), Value::ProperList(rhs)) => lhs.is_equal(rhs),
(Value::ImproperList(lhs_head, lhs_tail), Value::ImproperList(rhs_head, rhs_tail)) => {
lhs_head.is_equal(rhs_head) && lhs_tail.is_equal(rhs_tail)
Expand Down Expand Up @@ -248,6 +229,18 @@ impl Factory {
}
}

pub fn improper_list(&self, head: Vec<Value>, tail: Value) -> Value {
Value::ImproperList(head.into(), Box::new(tail))
}

pub fn vector(&self, vals: Vec<Value>) -> Value {
Value::Vector(vals)
}

pub fn byte_vector(&self, vals: Vec<u8>) -> Value {
Value::ByteVector(vals)
}

pub fn foreign_procedure(&mut self, v: procedure::foreign::Procedure) -> Value {
Value::Procedure(procedure::Procedure::foreign(v))
}
Expand All @@ -260,7 +253,6 @@ impl Factory {
Value::Closure(closure::Closure::new(v, vec![]))
}

// TODO: decide if this should this consume datum instead
pub fn from_datum(&mut self, d: &Datum) -> Value {
match d.s_expression() {
SExpression::Bool(true) => self.bool_true().clone(),
Expand All @@ -281,7 +273,7 @@ impl Factory {
SExpression::Char(c) => self.character(*c),
SExpression::Number(num) => Value::Number(num.clone()),
SExpression::Vector(v) => Value::Vector(v.iter().map(|e| self.from_datum(e)).collect()),
_ => todo!(),
SExpression::ByteVector(v) => Value::ByteVector(v.clone()),
}
}

Expand Down
21 changes: 21 additions & 0 deletions src/vm/value/byte_vector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use crate::vm::value::equality::SchemeEqual;

pub type ByteVector = Vec<u8>;

impl SchemeEqual<ByteVector> for ByteVector {
fn is_eq(&self, other: &ByteVector) -> bool {
self.is_equal(other)
}

fn is_eqv(&self, other: &ByteVector) -> bool {
self.is_equal(other)
}

fn is_equal(&self, other: &ByteVector) -> bool {
if self.len() != other.len() {
return false;
} else {
self.iter().zip(other.iter()).all(|(a, b)| a == b)
}
}
}
18 changes: 14 additions & 4 deletions src/vm/value/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod fixnum;
pub mod flonum;
pub mod rational;
pub mod real;
use az::CheckedAs;
use std::ops::{Add, Div, Mul, Sub};

type ArithResult<T> = std::result::Result<T, RuntimeError>;
Expand Down Expand Up @@ -74,6 +75,15 @@ impl Number {
denom.into(),
))))
}

pub fn to_u8(&self) -> Option<u8> {
match self {
Self::Real(r) => r
.clone()
.checked_as::<fixnum::Fixnum>()
.and_then(|fx| fx.as_inner().to_u8()),
}
}
}

impl ToString for Number {
Expand Down Expand Up @@ -156,14 +166,14 @@ impl SchemeNumber for Number {
}
}

fn is_nan(&self) -> bool {
fn is_neg_infinite(&self) -> bool {
match self {
Self::Real(n) => n.is_nan(),
Self::Real(n) => n.is_neg_infinite(),
}
}
fn is_neg_infinite(&self) -> bool {
fn is_nan(&self) -> bool {
match self {
Self::Real(n) => n.is_neg_infinite(),
Self::Real(n) => n.is_nan(),
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/vm/value/number/fixnum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ impl SchemeNumber for Fixnum {
false
}

fn is_nan(&self) -> bool {
fn is_neg_infinite(&self) -> bool {
false
}
fn is_neg_infinite(&self) -> bool {
fn is_nan(&self) -> bool {
false
}
}
Expand Down
Loading

0 comments on commit d934f9f

Please sign in to comment.