Merge pull request #149 from tildeio/tuples

Add coercions for tuples
This commit is contained in:
Godfrey Chan 2018-05-11 16:27:23 -07:00 committed by GitHub
commit 32d6a67b11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 259 additions and 5 deletions

View File

@ -3,7 +3,7 @@ cache:
- '%HOMEDRIVE%%HOMEPATH%\.multirust -> .appveyor.yml'
environment:
EXAMPLES: duration calculator console membership text_transform turbo_blank json_builder
EXAMPLES: duration calculator console geometry membership text_transform turbo_blank json_builder
VERBOSE: true
RUST_BACKTRACE: 1
matrix:

View File

@ -21,7 +21,7 @@ cache:
env:
global:
- EXAMPLES="duration calculator console membership text_transform turbo_blank json_builder"
- EXAMPLES="duration calculator console geometry membership text_transform turbo_blank json_builder"
- VERBOSE=true
- RUST_BACKTRACE=1
- RUST_VERSION=stable

View File

@ -21,7 +21,7 @@ appveyor = { repository = "tildeio/helix", branch = "master", service = "github"
[workspace]
members = ["examples/calculator", "examples/console", "examples/duration", "examples/membership", "examples/text_transform", "examples/turbo_blank", "examples/json_builder"]
members = ["examples/calculator", "examples/console", "examples/geometry", "examples/duration", "examples/membership", "examples/text_transform", "examples/turbo_blank", "examples/json_builder"]
[dependencies]
libc = "0.2.0"

View File

@ -4,7 +4,7 @@ task :test do
sh "bundle exec rake"
end
examples = ENV["EXAMPLES"] || "duration calculator console membership text_transform turbo_blank json_builder"
examples = ENV["EXAMPLES"] || "duration calculator console geometry membership text_transform turbo_blank json_builder"
sh "bash ./examples/runner default #{examples}"
end
@ -15,7 +15,7 @@ task :install do
sh "bundle"
end
examples = ENV["EXAMPLES"] || "duration calculator console membership text_transform turbo_blank json_builder"
examples = ENV["EXAMPLES"] || "duration calculator console geometry membership text_transform turbo_blank json_builder"
sh "bash ./examples/runner install #{examples}"
end

3
examples/geometry/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
target
*.bundle
*.gem

View File

@ -0,0 +1,11 @@
[package]
name = "geometry"
version = "0.1.0"
authors = ["Godfrey Chan <godfrey@tilde.io>"]
[lib]
crate-type = ["cdylib"]
[dependencies.helix]
path = "../.."

View File

@ -0,0 +1,5 @@
source 'https://rubygems.org'
gem 'helix_runtime', path: '../../ruby'
gem 'rake', '~> 10.0'
gem 'rspec', '~> 3.4'

20
examples/geometry/Rakefile Executable file
View File

@ -0,0 +1,20 @@
require 'bundler/setup'
require 'helix_runtime/build_task'
require 'rspec/core/rake_task'
require_relative '../shared.rb'
# For Windows
$stdout.sync = true
HelixRuntime::BuildTask.new do |t|
t.build_root = File.expand_path("../..", __dir__)
t.helix_lib_dir = File.expand_path("../../ruby/windows_build", __dir__)
t.pre_build = HelixRuntime::Tests.pre_build
end
RSpec::Core::RakeTask.new(:spec) do |t|
t.verbose = false
end
task :spec => :build
task :default => :spec

View File

@ -0,0 +1,2 @@
require 'helix_runtime'
require 'geometry/native'

View File

@ -0,0 +1,35 @@
require "spec_helper"
describe Point do
let(:origin) { Point.new(0,0) }
let(:north) { Point.new(0,1) }
let(:east) { Point.new(1,0) }
let(:south) { Point.new(0,-1) }
let(:west) { Point.new(-1,0) }
it "has the right coordinates" do
expect(origin.x).to eq(0)
expect(origin.y).to eq(0)
expect(north.x).to eq(0)
expect(north.y).to eq(1)
expect(east.x).to eq(1)
expect(east.y).to eq(0)
expect(south.x).to eq(0)
expect(south.y).to eq(-1)
expect(west.x).to eq(-1)
expect(west.y).to eq(0)
end
it "can be turned into an array" do
expect(origin.to_a).to eq([0,0])
expect(north.to_a).to eq([0,1])
expect(east.to_a).to eq([1,0])
expect(south.to_a).to eq([0,-1])
expect(west.to_a).to eq([-1,0])
end
end

View File

@ -0,0 +1,2 @@
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
require 'geometry'

View File

@ -0,0 +1,29 @@
#![recursion_limit="1024"]
#[macro_use]
extern crate helix;
ruby! {
class Point {
struct {
x: f64,
y: f64
}
def initialize(helix, x: f64, y: f64) {
Point { helix, x, y }
}
def x(&self) -> f64 {
self.x
}
def y(&self) -> f64 {
self.y
}
def to_a(&self) -> (f64, f64) {
(self.x, self.y)
}
}
}

View File

@ -5,6 +5,7 @@ mod integers;
mod float;
mod symbol;
mod string;
mod tuples;
mod option;
mod result;
mod slice;

121
src/coercions/tuples.rs Normal file
View File

@ -0,0 +1,121 @@
use sys::{self, VALUE};
use super::{CheckResult, FromRuby, ToRuby, ToRubyResult};
use super::super::{inspect};
#[doc(hidden)]
macro_rules! impl_tuple_coercions {
($($name:ident),*) => {
impl_tuple_from_ruby!(count_items!($($name),*), $($name),*);
impl_tuple_to_ruby!(count_items!($($name),*), $($name),*);
};
($($any:tt)*) => {
compile_error!(stringify!("impl_tuple_coercions" $($any)*));
};
}
#[doc(hidden)]
macro_rules! impl_tuple_from_ruby {
($count:expr, $($name:ident),*) => {
impl<$($name: FromRuby,)*> FromRuby for ($($name,)*) {
type Checked = ($($name::Checked,)*);
fn from_ruby(value: VALUE) -> CheckResult<Self::Checked> {
if unsafe { sys::RB_TYPE_P(value, sys::T_ARRAY) } {
// Make sure we can actually do the conversions for the values.
let len = unsafe { sys::RARRAY_LEN(value) };
if len != $count {
type_error!(value, format!("an array with {} {}", $count, { if $count == 1 { "element" } else { "elements" } }))
}
extract_tuple_elements_from_ruby!(value, (0), $($name),*);
Ok(($($name,)*))
} else {
type_error!(value, "an array")
}
}
fn from_checked(checked: Self::Checked) -> Self {
#[allow(non_snake_case)]
let ($($name,)*) = checked;
($($name::from_checked($name),)*)
}
}
};
($($any:tt)*) => {
compile_error!(stringify!("impl_tuple_from_ruby" $($any)*));
};
}
#[doc(hidden)]
macro_rules! extract_tuple_elements_from_ruby {
($value:ident, $offset:tt) => {};
($value:ident, $offset:tt, $name:ident $($rest:tt)*) => {
#[allow(non_snake_case)]
let $name = {
let val = unsafe { sys::rb_ary_entry($value, $offset as isize) };
match $name::from_ruby(val) {
Ok(v) => v,
Err(e) => type_error!(format!("Failed to convert {}, element {} has the wrong type: {}", inspect($value), $offset, e)),
}
};
extract_tuple_elements_from_ruby!($value, ($offset + 1) $($rest)*);
};
($($any:tt)*) => {
compile_error!(stringify!("extract_tuple_elements_from_ruby" $($any)*));
};
}
#[doc(hidden)]
macro_rules! impl_tuple_to_ruby {
($count:expr, $($name:ident),*) => {
impl<$($name: ToRuby,)*> ToRuby for ($($name,)*) {
fn to_ruby(self) -> ToRubyResult {
let ary = unsafe { sys::rb_ary_new_capa($count as isize) };
#[allow(non_snake_case)]
let ($($name,)*) = self;
$(
unsafe { sys::rb_ary_push(ary, $name.to_ruby()?); }
)*;
Ok(ary)
}
}
};
($($any:tt)*) => {
compile_error!(stringify!("impl_tuple_to_ruby" $($any)*));
};
}
#[doc(hidden)]
macro_rules! count_items {
() => { 0 };
($item:tt $(, $rest:tt)*) => { 1 + count_items!($($rest),*) };
($($any:tt)*) => {
compile_error!(stringify!("count_items" $($any)*));
};
}
impl_tuple_coercions!(A);
impl_tuple_coercions!(A, B);
impl_tuple_coercions!(A, B, C);
impl_tuple_coercions!(A, B, C, D);
impl_tuple_coercions!(A, B, C, D, E);
impl_tuple_coercions!(A, B, C, D, E, F);
impl_tuple_coercions!(A, B, C, D, E, F, G);
impl_tuple_coercions!(A, B, C, D, E, F, G, H);
impl_tuple_coercions!(A, B, C, D, E, F, G, H, I);
impl_tuple_coercions!(A, B, C, D, E, F, G, H, I, J);
impl_tuple_coercions!(A, B, C, D, E, F, G, H, I, J, K);
impl_tuple_coercions!(A, B, C, D, E, F, G, H, I, J, K, L);

View File

@ -303,6 +303,8 @@ macro_rules! parse {
ast: [ $($ast:tt)* ]
}
} => {
assert_has_initialize!($class, "Classes defining a struct must implement `initialize`");
parse! {
state: top_level,
buffer: $program,
@ -863,6 +865,29 @@ macro_rules! assert_not_reopen {
{ { reopen: false }, $($message:expr),* } => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! assert_has_initialize {
{
{
type: class,
rust_name: $rust_name:tt,
ruby_name: $ruby_name:tt,
meta: $meta:tt,
struct: $struct:tt,
methods: $methods:tt
},
$($message:expr),*
} => { assert_has_initialize!({ struct: $struct }, $methods, $($message),*); };
{ { struct: () }, $methods:tt, $($message:expr),* } => {};
{ { struct: $struct:tt }, [ ], $($message:expr),* } => { parse_error!($($message),*); };
{ { struct: $struct:tt }, [ { type: initializer, $($rest:tt)* } $($methods:tt)* ], $($message:expr),* } => {};
{ { struct: $struct:tt }, [ $method:tt $($methods:tt)* ], $($message:expr),* } => {
assert_has_initialize!({ struct: $struct }, [ $($methods)* ], $($message),*);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! assert_has_struct {