Remove type checking transformation in luaify
This commit is contained in:
parent
e05e2f4cb3
commit
f6b91cde10
@ -916,7 +916,7 @@ fn generate_pat_typed(f: &mut Formatter, typed: &PatType, cx: PatContext) -> Res
|
|||||||
assert_no_attrs!(typed);
|
assert_no_attrs!(typed);
|
||||||
match *typed.ty {
|
match *typed.ty {
|
||||||
Type::Infer(_) => generate_pat(f, &typed.pat, cx),
|
Type::Infer(_) => generate_pat(f, &typed.pat, cx),
|
||||||
ref ty => syn_error!(ty, "cannot have type"),
|
ref ty => syn_error!(ty, "cannot specify type"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use crate::utils::{LuaType, expr_ident, pat_ident, syn_error, wrap_expr_block};
|
use crate::utils::syn_error;
|
||||||
use quote::format_ident;
|
|
||||||
use std::mem;
|
|
||||||
use syn::{spanned::*, visit_mut::*, *};
|
use syn::{spanned::*, visit_mut::*, *};
|
||||||
|
|
||||||
pub fn transform(expr: &mut Expr) -> Result<()> {
|
pub fn transform(expr: &mut Expr) -> Result<()> {
|
||||||
@ -27,27 +25,6 @@ impl Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VisitMut for Visitor {
|
impl VisitMut for Visitor {
|
||||||
fn visit_expr_closure_mut(&mut self, clo: &mut ExprClosure) {
|
|
||||||
match self.transform_expr_closure(clo) {
|
|
||||||
res @ Err(_) => self.result = res,
|
|
||||||
_ => visit_expr_closure_mut(self, clo),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_item_fn_mut(&mut self, func: &mut ItemFn) {
|
|
||||||
match self.transform_function(func) {
|
|
||||||
res @ Err(_) => self.result = res,
|
|
||||||
_ => visit_item_fn_mut(self, func),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_expr_mut(&mut self, expr: &mut Expr) {
|
|
||||||
match self.transform_expr(expr) {
|
|
||||||
res @ Err(_) => self.result = res,
|
|
||||||
_ => visit_expr_mut(self, expr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_expr_unary_mut(&mut self, un: &mut ExprUnary) {
|
fn visit_expr_unary_mut(&mut self, un: &mut ExprUnary) {
|
||||||
match self.transform_unary(un) {
|
match self.transform_unary(un) {
|
||||||
res @ Err(_) => self.result = res,
|
res @ Err(_) => self.result = res,
|
||||||
@ -64,147 +41,9 @@ impl VisitMut for Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Visitor {
|
impl Visitor {
|
||||||
fn transform_expr_closure(&mut self, clo: &mut ExprClosure) -> Result<()> {
|
|
||||||
//
|
|
||||||
// transforms a closure expression with input type annotations by removing the annotations
|
|
||||||
// and inserting `as` casts at the start.
|
|
||||||
//
|
|
||||||
// before:
|
|
||||||
// |a: string, b: number| { ... }
|
|
||||||
// after:
|
|
||||||
// |a, b| { a as string; b as number; ... }
|
|
||||||
//
|
|
||||||
let mut checks: Vec<Stmt> = vec![];
|
|
||||||
for input in clo.inputs.iter_mut() {
|
|
||||||
match input {
|
|
||||||
Pat::Ident(_) => {}
|
|
||||||
Pat::Type(typed) => {
|
|
||||||
let ident = pat_ident(&typed.pat)?;
|
|
||||||
let ty = mem::replace(&mut typed.ty, parse_quote!(_));
|
|
||||||
match (&*ty).try_into()? {
|
|
||||||
LuaType::Any => {}
|
|
||||||
_ => checks.push(parse_quote! { #ident as #ty; }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !checks.is_empty() {
|
|
||||||
let mut body = wrap_expr_block(&clo.body);
|
|
||||||
body.stmts.splice(..0, checks);
|
|
||||||
clo.body = Box::new(parse_quote! { #body });
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transform_function(&mut self, func: &mut ItemFn) -> Result<()> {
|
|
||||||
//
|
|
||||||
// transforms a function item with input type annotations by removing the annotations
|
|
||||||
// and inserting `as` casts at the start.
|
|
||||||
//
|
|
||||||
// before:
|
|
||||||
// fn my_func(self: table, a: string) { ... }
|
|
||||||
// after:
|
|
||||||
// fn my_func(self: _, a: _) { self as table; a as string; ... }
|
|
||||||
//
|
|
||||||
let mut checks: Vec<Stmt> = vec![];
|
|
||||||
for input in func.sig.inputs.iter_mut() {
|
|
||||||
if let Some((ident, ty)) = match input {
|
|
||||||
FnArg::Receiver(recv) if recv.colon_token.is_some() => {
|
|
||||||
let ty = mem::replace(&mut recv.ty, parse_quote!(_));
|
|
||||||
recv.colon_token = None;
|
|
||||||
Some((Ident::new("self", recv.self_token.span()), ty))
|
|
||||||
}
|
|
||||||
FnArg::Typed(typed) => {
|
|
||||||
let ident = pat_ident(&typed.pat)?;
|
|
||||||
let ty = mem::replace(&mut typed.ty, parse_quote!(_));
|
|
||||||
Some((ident, ty))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
} {
|
|
||||||
match (&*ty).try_into()? {
|
|
||||||
LuaType::Any => {}
|
|
||||||
_ => checks.push(parse_quote! { #ident as #ty; }),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
func.block.stmts.splice(..0, checks);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transform_expr(&mut self, expr: &mut Expr) -> Result<()> {
|
|
||||||
self.transform_expr_cast(expr)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transform_expr_cast(&mut self, expr: &mut Expr) -> Result<()> {
|
|
||||||
//
|
|
||||||
// transforms an `as` cast expression into a block expression containing a runtime
|
|
||||||
// lua type check.
|
|
||||||
//
|
|
||||||
// before:
|
|
||||||
// var as string
|
|
||||||
// after:
|
|
||||||
// { if type(var) != "string" { error(...) } }
|
|
||||||
//
|
|
||||||
if let Expr::Cast(cast) = expr {
|
|
||||||
let arg = (*cast.expr).clone();
|
|
||||||
let mut prelude: Option<Stmt> = None;
|
|
||||||
let ty: LuaType = (&*cast.ty).try_into()?;
|
|
||||||
let ty_str = format!("{ty}");
|
|
||||||
let (ident, msg) = match expr_ident(&arg) {
|
|
||||||
Ok(ident) => (ident.clone(), format!("{ty} expected in '{ident}', got ")),
|
|
||||||
Err(_) => {
|
|
||||||
let ident = Ident::new("_", arg.span());
|
|
||||||
prelude = Some(parse_quote! { let #ident = #arg; });
|
|
||||||
(ident, format!("{ty} expected, got "))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let tmp = format_ident!("__{ident}");
|
|
||||||
let span = cast.span();
|
|
||||||
*expr = match ty {
|
|
||||||
LuaType::Any => parse_quote_spanned!(span => {}),
|
|
||||||
LuaType::Nil => parse_quote_spanned!(span => {
|
|
||||||
#prelude
|
|
||||||
assert(#ident == (), concat!(#msg, r#type(#ident)));
|
|
||||||
}),
|
|
||||||
LuaType::Number => parse_quote_spanned!(span => {
|
|
||||||
#prelude
|
|
||||||
let #tmp = #ident;
|
|
||||||
#ident = tonumber(#ident);
|
|
||||||
assert(#ident != (), concat!(#msg, r#type(#tmp)));
|
|
||||||
}),
|
|
||||||
LuaType::Integer => parse_quote_spanned!(span => {
|
|
||||||
#prelude
|
|
||||||
let #tmp = #ident;
|
|
||||||
#ident = tonumber(#ident);
|
|
||||||
assert(#ident != () && math::floor(#ident) == #ident, concat!(#msg, r#type(#tmp)));
|
|
||||||
}),
|
|
||||||
LuaType::String => parse_quote_spanned!(span => {
|
|
||||||
#prelude
|
|
||||||
if r#type(#ident) == "number" {
|
|
||||||
#ident = tostring(#ident);
|
|
||||||
} else {
|
|
||||||
assert(r#type(#ident) == "string", concat!(#msg, r#type(#ident)));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
_ => parse_quote_spanned!(span => {
|
|
||||||
#prelude
|
|
||||||
assert(r#type(#ident) == #ty_str, concat!(#msg, r#type(#ident)));
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transform_unary(&mut self, un: &mut ExprUnary) -> Result<()> {
|
fn transform_unary(&mut self, un: &mut ExprUnary) -> Result<()> {
|
||||||
//
|
//
|
||||||
// separates a nested negation unary operator with parentheses, because double hyphen
|
// separates a nested negation unary operator with parentheses, because the double hyphen
|
||||||
// `--` indicates a comment in lua.
|
// `--` indicates a comment in lua.
|
||||||
//
|
//
|
||||||
// before:
|
// before:
|
||||||
@ -216,7 +55,7 @@ impl Visitor {
|
|||||||
&& let Expr::Unary(ref inner) = *un.expr
|
&& let Expr::Unary(ref inner) = *un.expr
|
||||||
&& let UnOp::Neg(_) = inner.op
|
&& let UnOp::Neg(_) = inner.op
|
||||||
{
|
{
|
||||||
un.expr = Box::new(parse_quote!((#inner)));
|
un.expr = Box::new(parse_quote_spanned!(inner.span() => (#inner)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use std::fmt;
|
use syn::*;
|
||||||
use syn::{ext::*, spanned::*, *};
|
|
||||||
|
|
||||||
macro_rules! syn_error {
|
macro_rules! syn_error {
|
||||||
($src:expr, $($fmt:expr),+) => {{
|
($src:expr, $($fmt:expr),+) => {{
|
||||||
@ -24,90 +23,3 @@ pub fn wrap_expr_block(expr: &Expr) -> Block {
|
|||||||
expr => parse_quote!({ #expr }),
|
expr => parse_quote!({ #expr }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr_ident(expr: &Expr) -> Result<&Ident> {
|
|
||||||
match expr {
|
|
||||||
Expr::Path(path) => path.path.require_ident(),
|
|
||||||
_ => syn_error!(expr, "expected ident"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pat_ident(pat: &Pat) -> Result<Ident> {
|
|
||||||
Ok(match pat {
|
|
||||||
Pat::Ident(ident) => match ident.subpat {
|
|
||||||
Some((_, ref subpat)) => syn_error!(subpat, "unexpected subpattern"),
|
|
||||||
None => ident.ident.clone(),
|
|
||||||
},
|
|
||||||
Pat::Wild(wild) => Ident::new("_", wild.span()),
|
|
||||||
_ => syn_error!(pat, "expected ident"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum LuaType {
|
|
||||||
Any,
|
|
||||||
Nil,
|
|
||||||
Boolean,
|
|
||||||
Lightuserdata,
|
|
||||||
Number,
|
|
||||||
Integer,
|
|
||||||
String,
|
|
||||||
Table,
|
|
||||||
Function,
|
|
||||||
Userdata,
|
|
||||||
Thread,
|
|
||||||
Cdata,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for LuaType {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
LuaType::Any => write!(f, "any"),
|
|
||||||
LuaType::Nil => write!(f, "nil"),
|
|
||||||
LuaType::Boolean => write!(f, "boolean"),
|
|
||||||
LuaType::Lightuserdata => write!(f, "lightuserdata"),
|
|
||||||
LuaType::Number => write!(f, "number"),
|
|
||||||
LuaType::Integer => write!(f, "integer"),
|
|
||||||
LuaType::String => write!(f, "string"),
|
|
||||||
LuaType::Table => write!(f, "table"),
|
|
||||||
LuaType::Function => write!(f, "function"),
|
|
||||||
LuaType::Userdata => write!(f, "userdata"),
|
|
||||||
LuaType::Thread => write!(f, "thread"),
|
|
||||||
LuaType::Cdata => write!(f, "cdata"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&Ident> for LuaType {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: &Ident) -> Result<Self> {
|
|
||||||
Ok(match format!("{}", value.unraw()).as_str() {
|
|
||||||
"any" => Self::Any,
|
|
||||||
"nil" => Self::Nil,
|
|
||||||
"boolean" => Self::Boolean,
|
|
||||||
"lightuserdata" => Self::Lightuserdata,
|
|
||||||
"number" => Self::Number,
|
|
||||||
"integer" => Self::Integer,
|
|
||||||
"string" => Self::String,
|
|
||||||
"table" => Self::Table,
|
|
||||||
"function" => Self::Function,
|
|
||||||
"userdata" => Self::Userdata,
|
|
||||||
"thread" => Self::Thread,
|
|
||||||
"cdata" => Self::Cdata,
|
|
||||||
_ => syn_error!(value, "invalid lua type"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&Type> for LuaType {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: &Type) -> Result<Self> {
|
|
||||||
match value {
|
|
||||||
Type::Infer(_) => Ok(Self::Any),
|
|
||||||
Type::Path(path) if path.qself.is_none() => path.path.require_ident()?.try_into(),
|
|
||||||
_ => syn_error!(value, "invalid lua type"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -77,13 +77,6 @@ fn local_fn() {
|
|||||||
}),
|
}),
|
||||||
r#"function(a,b)local function inner(c,d)end;return inner;end"#
|
r#"function(a,b)local function inner(c,d)end;return inner;end"#
|
||||||
);
|
);
|
||||||
assert_eq!(
|
|
||||||
luaify!(|| {
|
|
||||||
fn check(self: string, arg: number) {}
|
|
||||||
inner
|
|
||||||
}),
|
|
||||||
r#"function()local function check(self,arg)do if type(self)=="number"then self=tostring(self);else assert(type(self)=="string","string expected in \'self\', got "..type(self));end;end;do local __arg=arg;arg=tonumber(arg);assert(arg~=nil,"number expected in \'arg\', got "..type(__arg));end;end;return inner;end"#
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -208,37 +201,6 @@ fn loops() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn type_checks() {
|
|
||||||
assert_eq!(luaify!(|s| {}), r#"function(s)end"#);
|
|
||||||
assert_eq!(
|
|
||||||
luaify!(|s: table| {}),
|
|
||||||
r#"function(s)do assert(type(s)=="table","table expected in \'s\', got "..type(s));end;end"#
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
luaify!(|s| { s as string }),
|
|
||||||
r#"function(s)do if type(s)=="number"then s=tostring(s);else assert(type(s)=="string","string expected in \'s\', got "..type(s));end;end;end"#
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
luaify!(|s| { s as number }),
|
|
||||||
r#"function(s)do local __s=s;s=tonumber(s);assert(s~=nil,"number expected in \'s\', got "..type(__s));end;end"#
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
luaify!(|s| { s as nil }),
|
|
||||||
r#"function(s)do assert(s==nil,"nil expected in \'s\', got "..type(s));end;end"#
|
|
||||||
);
|
|
||||||
assert_eq!(luaify!(|s| { s as any }), r#"function(s)do end;end"#);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
luaify!(|s| {
|
|
||||||
let (ok, res) = coroutine::r#yield(thread);
|
|
||||||
ok as boolean;
|
|
||||||
res as nil;
|
|
||||||
}),
|
|
||||||
r#"function(s)local ok,res=coroutine.yield(thread);do assert(type(ok)=="boolean","boolean expected in \'ok\', got "..type(ok));end;do assert(res==nil,"nil expected in \'res\', got "..type(res));end;end"#
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn concat() {
|
fn concat() {
|
||||||
assert_eq!(luaify!(concat!(a)), r#"a"#);
|
assert_eq!(luaify!(concat!(a)), r#"a"#);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user