Skip to content

Commit

Permalink
Implement js methods, Property{T}, and other odds-and-ends (#912)
Browse files Browse the repository at this point in the history
* Implement js methods, Property{T}, and other odds-and-ends

* Apparently parse doesn't understand numbers with underscores, even though Rust's own parser does...
  • Loading branch information
dfellis authored Sep 21, 2024
1 parent 54374e2 commit 0ec8ff2
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 46 deletions.
33 changes: 17 additions & 16 deletions alan/src/compile/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,13 @@ test_full!(passing_ints_to_function => r#"
stdout "I got a number! 5\n";
status 0;
);
test!(underscores_in_numbers => r#"
test_full!(underscores_in_numbers => r#"
export fn main = print(1_000_000 * 2);
"#;
stdout "2000000\n";
status 0;
);
test!(other_integer_syntaxes => r#"
test_full!(other_integer_syntaxes => r#"
export fn main {
print(0b10 == 2);
print(0o10 == 8);
Expand All @@ -283,7 +283,7 @@ test!(other_integer_syntaxes => r#"
"#;
stdout "true\ntrue\ntrue\ntrue\n";
);
test!(scientific_notation => r#"
test_full!(scientific_notation => r#"
export fn main {
print(15.0 == 1.5e1);
print(-5.0 == -5e0);
Expand Down Expand Up @@ -783,21 +783,21 @@ test_full!(f64_neg => r#"
stdout "-3\n";
);

test!(grouping => r#"
test_full!(grouping => r#"
export fn main {
print(2 / (3));
print(3 / (1 + 2));
}"#;
stdout "0\n1\n";
);

test!(string_min => r#"
test_full!(string_min => r#"
export fn main {
min(3.string, 5.string).print;
}"#;
stdout "3\n";
);
test!(string_max => r#"
test_full!(string_max => r#"
export fn main {
max(3.string, 5.string).print;
}"#;
Expand Down Expand Up @@ -1084,7 +1084,7 @@ true

// String Manipulation

test!(string_ops => r#"
test_full!(string_ops => r#"
export fn main {
concat('Hello, ', 'World!').print;
Expand Down Expand Up @@ -1116,14 +1116,14 @@ Hello
Hello
"#;
);
test!(string_const_vs_computed_equality => r#"
test_full!(string_const_vs_computed_equality => r#"
export fn main {
const foo = 'foo';
print(foo.trim == foo);
}"#;
stdout "true\n";
);
test!(string_chars_direct => r#"
test_full!(string_chars_direct => r#"
export fn main {
const foo = 'foo';
print(#foo);
Expand Down Expand Up @@ -1600,7 +1600,7 @@ test!(bitshifting => r#"

// Functions and Custom Operators

test!(basic_function_usage => r#"
test_full!(basic_function_usage => r#"
fn foo() = print('foo');
fn bar(s: string) = s.concat("bar");
Expand All @@ -1614,7 +1614,7 @@ foobar
"#;
);

test!(functions_and_custom_operators => r#"
test_full!(functions_and_custom_operators => r#"
fn foo() {
print('foo');
}
Expand Down Expand Up @@ -1654,6 +1654,7 @@ to barto bar3
"#;
);

// TODO: Need to figure out how to get this working in JS-land
test!(mutable_functions => r#"
fn addeq (a: Mut{i64}, b: i64) {
a = a.clone() + b;
Expand All @@ -1672,7 +1673,7 @@ test!(mutable_functions => r#"

// Conditionals

test!(if_fn => r#"
test_full!(if_fn => r#"
export fn main {
if(1 == 0, fn = print('What!?'), fn = print('Math is sane...'));
if(1 == 2, fn = 'Uhh...').print;
Expand Down Expand Up @@ -1778,7 +1779,7 @@ test_ignore!(conditional_let_assignment => r#"
stdout "1\n";
);

test!(conditional_compilation => r#"
test_full!(conditional_compilation => r#"
type{true} foo = string;
type{false} foo = i64;
Expand All @@ -1805,7 +1806,7 @@ test!(conditional_compilation => r#"
}"#;
stdout "Hello, World!\n9\n9\ntrue\n";
);
test!(library_testing => r#"
test_full!(library_testing => r#"
export fn add1(a: i64) -> i64 = a + 1;
export postfix add1 as ++ precedence 5;
Expand All @@ -1828,7 +1829,7 @@ test_compile_error!(object_constructor_compiler_checks => r#"
}"#;
error "Could not find a function with a call signature of Foo(f64)";
);
test!(array_literals => r#"
test_full!(array_literals => r#"
export fn main {
const test3 = [ 1, 2, 4, 8, 16, 32, 64 ];
print(test3[0]);
Expand All @@ -1837,7 +1838,7 @@ test!(array_literals => r#"
}"#;
stdout "1\n2\n4\n";
);
test!(object_literals => r#"
test_full!(object_literals => r#"
type MyType =
foo: string,
bar: bool;
Expand Down
53 changes: 38 additions & 15 deletions alan/src/lntojs/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,10 @@ pub fn from_microstatement(
name: _,
kind,
typen: _,
} => {
if let ArgKind::Ref = kind {
Ok(("".to_string(), out, deps))
} else {
Err("Targeting JS does not allow for Mut{T}, Own{T}, or Deref{T}".into())
}
}
} => match kind {
ArgKind::Ref | ArgKind::Mut => Ok(("".to_string(), out, deps)),
_ => Err("Targeting JS does not allow for Own{T} or Deref{T}".into()),
},
Microstatement::Assignment {
name,
value,
Expand All @@ -36,10 +33,15 @@ pub fn from_microstatement(
let (val, o, d) = from_microstatement(value, scope, out, deps)?;
out = o;
deps = d;
let n = if name.as_str() == "var" {
"__var__"
} else {
name.as_str()
};
if *mutable {
Ok((format!("let {} = {}", name, val), out, deps))
Ok((format!("let {} = {}", n, val), out, deps))
} else {
Ok((format!("const {} = {}", name, val), out, deps))
Ok((format!("const {} = {}", n, val), out, deps))
}
}
Microstatement::Closure { function } => {
Expand Down Expand Up @@ -70,7 +72,18 @@ pub fn from_microstatement(
representation,
} => match &typen {
CType::Type(n, _) if n == "string" => {
Ok((representation.replace("\n", "\\n"), out, deps))
if representation.starts_with("\"") {
Ok((representation.replace("\n", "\\n"), out, deps))
} else {
Ok((representation.clone(), out, deps))
}
}
CType::Type(n, _) if n == "i64" || n == "u64" => {
if representation.replace("_", "").parse::<i128>().is_ok() {
Ok((format!("{}n", representation), out, deps))
} else {
Ok((representation.clone(), out, deps))
}
}
CType::Binds(n, _) => match &**n {
CType::TString(_) => Ok((representation.clone(), out, deps)),
Expand Down Expand Up @@ -116,7 +129,7 @@ pub fn from_microstatement(
Ok((representation.clone(), out, deps))
}
otherwise => CType::fail(&format!(
"1. Bound types must be strings or node.js imports: {:?}",
"Bound types must be strings or node.js imports: {:?}",
otherwise
)),
},
Expand Down Expand Up @@ -228,7 +241,13 @@ pub fn from_microstatement(
},
}
}
_ => Ok((representation.clone(), out, deps)),
_ => {
if representation.as_str() == "var" {
Ok(("__var__".to_string(), out, deps))
} else {
Ok((representation.clone(), out, deps))
}
}
},
Microstatement::Array { vals, .. } => {
let mut val_representations = Vec::new();
Expand Down Expand Up @@ -278,7 +297,11 @@ pub fn from_microstatement(
let (a, o, d) = from_microstatement(arg, scope, out, deps)?;
out = o;
deps = d;
argstrs.push(a);
if a.as_str() == "var" {
argstrs.push("__var__".to_string());
} else {
argstrs.push(a);
}
}
if let FnKind::External(d) = &function.kind {
match &*d {
Expand Down Expand Up @@ -543,7 +566,7 @@ pub fn from_microstatement(
if function.name == "Error" {
return Ok((
format!(
"({} instanceof AlanError ? {} : null)",
"({} instanceof alan_std.AlanError ? {} : null)",
argstrs[0], argstrs[0]
),
out,
Expand All @@ -552,7 +575,7 @@ pub fn from_microstatement(
} else {
return Ok((
format!(
"({} instanceof AlanError ? null : {})",
"(!({} instanceof alan_std.AlanError) ? {} : null)",
argstrs[0], argstrs[0]
),
out,
Expand Down
54 changes: 54 additions & 0 deletions alan/src/program/ctype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub enum CType {
Infix(Box<CType>),
Prefix(Box<CType>),
Method(Box<CType>),
Property(Box<CType>),
Cast(Box<CType>),
Own(Box<CType>),
Deref(Box<CType>),
Expand Down Expand Up @@ -207,6 +208,7 @@ impl CType {
CType::Infix(o) => format!("Infix{{{}}}", o.to_strict_string(strict)),
CType::Prefix(o) => format!("Prefix{{{}}}", o.to_strict_string(strict)),
CType::Method(f) => format!("Method{{{}}}", f.to_strict_string(strict)),
CType::Property(p) => format!("Property{{{}}}", p.to_strict_string(strict)),
CType::Cast(t) => format!("Cast{{{}}}", t.to_strict_string(strict)),
CType::Own(t) => {
if strict {
Expand Down Expand Up @@ -471,6 +473,7 @@ impl CType {
CType::Infix(o) => format!("Infix{{{}}}", o.to_functional_string()),
CType::Prefix(o) => format!("Prefix{{{}}}", o.to_functional_string()),
CType::Method(f) => format!("Method{{{}}}", f.to_functional_string()),
CType::Property(p) => format!("Property{{{}}}", p.to_functional_string()),
CType::Cast(t) => format!("Cast{{{}}}", t.to_functional_string()),
CType::Own(t) => format!("Own{{{}}}", t.to_functional_string()),
CType::Deref(t) => format!("Deref{{{}}}", t.to_functional_string()),
Expand Down Expand Up @@ -733,6 +736,7 @@ impl CType {
CType::Infix(o) => CType::Infix(Box::new((*o).degroup())),
CType::Prefix(o) => CType::Prefix(Box::new((*o).degroup())),
CType::Method(f) => CType::Method(Box::new((*f).degroup())),
CType::Property(p) => CType::Property(Box::new((*p).degroup())),
CType::Cast(t) => CType::Cast(Box::new((*t).degroup())),
CType::Own(t) => CType::Own(Box::new((*t).degroup())),
CType::Deref(t) => CType::Deref(Box::new((*t).degroup())),
Expand Down Expand Up @@ -947,6 +951,10 @@ impl CType {
arg.push(f1);
input.push(f2);
}
(Some(CType::Property(p1)), Some(CType::Property(p2))) => {
arg.push(p1);
input.push(p2);
}
(Some(CType::Cast(t1)), Some(CType::Cast(t2))) => {
arg.push(t1);
input.push(t2);
Expand Down Expand Up @@ -1936,6 +1944,50 @@ impl CType {
otherwise
)),
},
CType::Property(p) => match &**p {
CType::TString(s) => {
if args.len() > 1 {
CType::fail(&format!("Property bindings may only have one argument, the value the property is accessed from. Not {:?}", args))
} else {
let arg_car = args[0].clone();
microstatements.push(Microstatement::Return {
value: Some(Box::new(Microstatement::Value {
typen: rettype.clone(),
representation: format!(
"{}.{}",
match &arg_car.2 {
CType::Int(i) => {
trimmed_args = true;
format!("{}", i)
}
CType::Float(f) => {
trimmed_args = true;
format!("{}", f)
}
CType::Bool(b) => {
trimmed_args = true;
match b {
true => "true".to_string(),
false => "false".to_string(),
}
}
CType::TString(s) => {
trimmed_args = true;
format!("\"{}\"", s.replace("\"", "\\\""))
}
_ => arg_car.0.clone(),
},
s,
),
})),
});
}
}
otherwise => CType::fail(&format!(
"Unsupported native method declaration {:?}",
otherwise
)),
},
CType::Cast(t) => match &**t {
CType::TString(s) => {
if args.len() != 1 {
Expand Down Expand Up @@ -2657,6 +2709,7 @@ impl CType {
CType::Infix(o) => CType::Infix(Box::new(o.swap_subtype(old_type, new_type))),
CType::Prefix(o) => CType::Prefix(Box::new(o.swap_subtype(old_type, new_type))),
CType::Method(f) => CType::Method(Box::new(f.swap_subtype(old_type, new_type))),
CType::Property(p) => CType::Property(Box::new(p.swap_subtype(old_type, new_type))),
CType::Cast(t) => CType::Cast(Box::new(t.swap_subtype(old_type, new_type))),
CType::Own(t) => CType::Own(Box::new(t.swap_subtype(old_type, new_type))),
CType::Deref(t) => CType::Deref(Box::new(t.swap_subtype(old_type, new_type))),
Expand Down Expand Up @@ -3962,6 +4015,7 @@ pub fn typebaselist_to_ctype(
"Infix" => CType::Infix(Box::new(args[0].clone())),
"Prefix" => CType::Prefix(Box::new(args[0].clone())),
"Method" => CType::Method(Box::new(args[0].clone())),
"Property" => CType::Property(Box::new(args[0].clone())),
"Cast" => CType::Cast(Box::new(args[0].clone())),
"Own" => CType::Own(Box::new(args[0].clone())),
"Deref" => CType::Deref(Box::new(args[0].clone())),
Expand Down
6 changes: 3 additions & 3 deletions alan/src/program/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ impl<'a> Scope<'a> {
"Type" | "Generic" | "Int" | "Float" | "Bool" | "String" => {
/* Do nothing for the 'structural' types */
}
g @ ("Group" | "Infix" | "Prefix" | "Method" | "Cast" | "Own"
| "Deref" | "Mut" | "Rust" | "Node" | "From" | "Array" | "Fail"
| "Neg" | "Len" | "Size" | "FileStr" | "Env" | "EnvExists"
g @ ("Group" | "Infix" | "Prefix" | "Method" | "Property" | "Cast"
| "Own" | "Deref" | "Mut" | "Rust" | "Node" | "From" | "Array"
| "Fail" | "Neg" | "Len" | "Size" | "FileStr" | "Env" | "EnvExists"
| "Not") => s = CType::from_generic(s, g, 1),
g @ ("Function" | "Call" | "Dependency" | "Import" | "Field"
| "Prop" | "Buffer" | "Add" | "Sub" | "Mul" | "Div" | "Mod"
Expand Down
Loading

0 comments on commit 0ec8ff2

Please sign in to comment.