Skip to content

Commit

Permalink
Merge pull request #24 from ratel-rust/template-string
Browse files Browse the repository at this point in the history
Template literals 2
  • Loading branch information
maciejhirsz authored Oct 19, 2016
2 parents 60435e8 + 3ce888b commit f87c8bf
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 9 deletions.
2 changes: 1 addition & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ratel"
version = "0.4.1"
version = "0.5.0"
authors = ["Maciej Hirsz <maciej.hirsz@gmail.com>"]
license = "MIT/Apache-2.0"
description = "JavaScript transpiler in Rust"
Expand Down
45 changes: 38 additions & 7 deletions core/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,17 +213,48 @@ impl Code for OperatorType {
}
}

fn write_quasi(gen: &mut Generator, quasi: OwnedSlice) {
gen.write_byte(b'"');

let mut iter = quasi.as_str().bytes();

while let Some(byte) = iter.next() {
match byte {
b'\r' => {},
b'\n' => gen.write_bytes(b"\\n"),
b'"' => gen.write_bytes(b"\\\""),
b'\\' => {
if let Some(follow) = iter.next() {
match follow {
b'`' => gen.write_byte(b'`'),
b'\n' => {},
b'\r' => {},
_ => {
gen.write_byte(b'\\');
gen.write_byte(follow);
}
}
}
},
_ => gen.write_byte(byte),
}
}

gen.write_byte(b'"');
}

impl Code for Value {
#[inline]
fn to_code(&self, gen: &mut Generator) {
match *self {
Value::Undefined => gen.write_min(b"undefined", b"void 0"),
Value::Null => gen.write_bytes(b"null"),
Value::True => gen.write_min(b"true", b"!0",),
Value::False => gen.write_min(b"false", b"!1"),
Value::Integer(ref num) => gen.write(num),
Value::Number(ref num) => gen.write(num),
Value::String(ref string) => gen.write(string),
Value::Undefined => gen.write_min(b"undefined", b"void 0"),
Value::Null => gen.write_bytes(b"null"),
Value::True => gen.write_min(b"true", b"!0",),
Value::False => gen.write_min(b"false", b"!1"),
Value::Integer(ref num) => gen.write(num),
Value::Number(ref num) => gen.write(num),
Value::String(ref string) => gen.write(string),
Value::RawQuasi(ref quasi) => write_quasi(gen, *quasi),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions core/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub enum Value {
Integer(u64),
Number(OwnedSlice),
String(OwnedSlice),
RawQuasi(OwnedSlice),
}

#[derive(Debug, PartialEq, Clone, Copy)]
Expand Down
5 changes: 5 additions & 0 deletions core/src/owned_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ impl OwnedSlice {
slice::from_raw_parts(self.ptr, self.len)
}
}

#[inline]
pub fn len(&self) -> usize {
self.len
}
}

impl From<&'static str> for OwnedSlice {
Expand Down
69 changes: 68 additions & 1 deletion core/src/transformer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct Settings {
pub transform_exponentation: bool,
pub transform_class_properties: bool,
pub transform_class: bool,
pub transform_template_strings: bool,
}

trait Take {
Expand Down Expand Up @@ -44,6 +45,7 @@ impl Settings {
settings.transform_arrow = true;
settings.transform_object = true;
settings.transform_class = true;
settings.transform_template_strings = true;

settings
}
Expand All @@ -65,6 +67,7 @@ impl Settings {
transform_exponentation: false,
transform_class_properties: false,
transform_class: false,
transform_template_strings: false,
}
}
}
Expand Down Expand Up @@ -256,7 +259,71 @@ impl Transformable for Expression {

_ => return,
}
}
},

Expression::Template {
ref mut tag,
ref mut expressions,
ref mut quasis,
} => {
tag.transform(settings);
expressions.transform(settings);

if !settings.transform_template_strings {
return;
}

if let Some(tag) = tag.take() {
// Tagged template

let mut arguments = Vec::with_capacity(expressions.len() + 1);

arguments.push(Expression::Array(
quasis.drain(..)
.map(|quasi| Expression::Literal(Value::RawQuasi(quasi)))
.collect()
));

arguments.extend(expressions.drain(..));

Expression::Call {
callee: tag,
arguments: arguments,
}
} else {
// Not tagged template

let mut quasis = quasis.drain(..);

let mut left = Expression::Literal(
Value::RawQuasi(quasis.next().expect("Must have first quasi"))
);

let iter = quasis.zip(expressions.drain(..));

for (quasi, expression) in iter {
left = Expression::binary(
left,
Addition,
expression
);

if quasi.len() == 0 {
continue;
}

left = Expression::binary(
left,
Addition,
Expression::Literal(
Value::RawQuasi(quasi)
)
);
}

left
}
},

_ => return,
}
Expand Down
22 changes: 22 additions & 0 deletions core/tests/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,28 @@ fn dont_touch_var_in_global_scope() {
assert_compile!("var pi = 3.14;", "var pi=3.14;");
}

#[test]
fn template_strings_plain() {
assert_compile!("`foobar`;", r#""foobar";"#);
assert_compile!("`foo\\`bar`;", r#""foo`bar";"#);
assert_compile!("`foo\nbar`;", "\"foo\\nbar\";");
}

#[test]
fn template_strings_interpolation() {
assert_compile!("`foo${1}bar`;", r#""foo"+1+"bar";"#);
assert_compile!("`foo${1}${2**2}bar`;", r#""foo"+1+Math.pow(2,2)+"bar";"#);
assert_compile!("`foo${1}bar${2**2}`;", r#""foo"+1+"bar"+Math.pow(2,2);"#);
}

#[test]
fn template_strings_tagged() {
assert_compile!("foo`bar`;", r#"foo(["bar"]);"#);
assert_compile!("foo`bar${1}baz`;", r#"foo(["bar","baz"],1);"#);
assert_compile!("foo`bar${1}${2}baz`;", r#"foo(["bar","","baz"],1,2);"#);
assert_compile!("foo`bar${1}baz${2}`;", r#"foo(["bar","baz",""],1,2);"#);
}

#[test]
fn convert_let_to_var_in_block() {
let program = "if(true) {
Expand Down

0 comments on commit f87c8bf

Please sign in to comment.