Resolve functions (#550)
Modifies parsing to return strongly-typed `Thunk`s, which contain both the function implementation, as well as the correct number of arguments. This moves unknown function and function argument count mismatch errors to parse time.
This commit is contained in:
parent
ba93c5e6af
commit
d2decbfdb8
47
Cargo.lock
generated
47
Cargo.lock
generated
@ -113,6 +113,16 @@ dependencies = [
|
|||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derivative"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "difference"
|
name = "difference"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@ -198,6 +208,7 @@ dependencies = [
|
|||||||
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"edit-distance 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"edit-distance 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -274,6 +285,14 @@ dependencies = [
|
|||||||
"output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "0.4.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@ -287,6 +306,14 @@ name = "quick-error"
|
|||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "0.6.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@ -390,6 +417,16 @@ name = "strsim"
|
|||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "0.15.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
@ -454,6 +491,11 @@ name = "unicode-width"
|
|||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -534,6 +576,7 @@ dependencies = [
|
|||||||
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||||
"checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc"
|
"checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc"
|
||||||
"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f"
|
"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f"
|
||||||
|
"checksum derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "942ca430eef7a3806595a6737bc388bf51adb888d3fc0dd1b50f1c170167ee3a"
|
||||||
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
|
||||||
"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97"
|
"checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97"
|
||||||
"checksum dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
"checksum dotenv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||||
@ -553,8 +596,10 @@ dependencies = [
|
|||||||
"checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
|
"checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
|
||||||
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
|
||||||
"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
|
"checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
|
||||||
|
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||||
"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
|
"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
|
||||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||||
|
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||||
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
|
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
|
||||||
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
|
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
|
||||||
@ -568,6 +613,7 @@ dependencies = [
|
|||||||
"checksum snafu 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41207ca11f96a62cd34e6b7fdf73d322b25ae3848eb9d38302169724bb32cf27"
|
"checksum snafu 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41207ca11f96a62cd34e6b7fdf73d322b25ae3848eb9d38302169724bb32cf27"
|
||||||
"checksum snafu-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c5e338c8b0577457c9dda8e794b6ad7231c96e25b1b0dd5842d52249020c1c0"
|
"checksum snafu-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c5e338c8b0577457c9dda8e794b6ad7231c96e25b1b0dd5842d52249020c1c0"
|
||||||
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||||
"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92"
|
"checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92"
|
||||||
"checksum target 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "10000465bb0cc031c87a44668991b284fd84c0e6bd945f62d4af04e9e52a222a"
|
"checksum target 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "10000465bb0cc031c87a44668991b284fd84c0e6bd945f62d4af04e9e52a222a"
|
||||||
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||||
@ -575,6 +621,7 @@ dependencies = [
|
|||||||
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||||
"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
|
"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
|
||||||
|
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
@ -19,6 +19,7 @@ ansi_term = "0.12"
|
|||||||
assert_matches = "1"
|
assert_matches = "1"
|
||||||
atty = "0.2"
|
atty = "0.2"
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
|
derivative = "1"
|
||||||
dotenv = "0.15"
|
dotenv = "0.15"
|
||||||
edit-distance = "2"
|
edit-distance = "2"
|
||||||
env_logger = "0.7"
|
env_logger = "0.7"
|
||||||
|
@ -101,20 +101,47 @@ impl<'a, 'b> AssignmentEvaluator<'a, 'b> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Call {
|
Expression::Call { thunk } => {
|
||||||
function,
|
|
||||||
arguments: call_arguments,
|
|
||||||
} => {
|
|
||||||
let call_arguments = call_arguments
|
|
||||||
.iter()
|
|
||||||
.map(|argument| self.evaluate_expression(argument, arguments))
|
|
||||||
.collect::<Result<Vec<String>, RuntimeError>>()?;
|
|
||||||
let context = FunctionContext {
|
let context = FunctionContext {
|
||||||
invocation_directory: &self.config.invocation_directory,
|
invocation_directory: &self.config.invocation_directory,
|
||||||
working_directory: &self.working_directory,
|
working_directory: &self.working_directory,
|
||||||
dotenv: self.dotenv,
|
dotenv: self.dotenv,
|
||||||
};
|
};
|
||||||
Function::evaluate(*function, &context, &call_arguments)
|
|
||||||
|
use Thunk::*;
|
||||||
|
match thunk {
|
||||||
|
Nullary { name, function, .. } => {
|
||||||
|
function(&context).map_err(|message| RuntimeError::FunctionCall {
|
||||||
|
function: *name,
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Unary {
|
||||||
|
name,
|
||||||
|
function,
|
||||||
|
arg,
|
||||||
|
..
|
||||||
|
} => function(&context, &self.evaluate_expression(arg, arguments)?).map_err(|message| {
|
||||||
|
RuntimeError::FunctionCall {
|
||||||
|
function: *name,
|
||||||
|
message,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Binary {
|
||||||
|
name,
|
||||||
|
function,
|
||||||
|
args: [a, b],
|
||||||
|
..
|
||||||
|
} => function(
|
||||||
|
&context,
|
||||||
|
&self.evaluate_expression(a, arguments)?,
|
||||||
|
&self.evaluate_expression(b, arguments)?,
|
||||||
|
)
|
||||||
|
.map_err(|message| RuntimeError::FunctionCall {
|
||||||
|
function: *name,
|
||||||
|
message,
|
||||||
|
}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Expression::StringLiteral { string_literal } => Ok(string_literal.cooked.to_string()),
|
Expression::StringLiteral { string_literal } => Ok(string_literal.cooked.to_string()),
|
||||||
Expression::Backtick { contents, token } => {
|
Expression::Backtick { contents, token } => {
|
||||||
|
@ -61,32 +61,35 @@ impl<'a: 'b, 'b> AssignmentResolver<'a, 'b> {
|
|||||||
Expression::Variable { name } => {
|
Expression::Variable { name } => {
|
||||||
let variable = name.lexeme();
|
let variable = name.lexeme();
|
||||||
if self.evaluated.contains(variable) {
|
if self.evaluated.contains(variable) {
|
||||||
return Ok(());
|
Ok(())
|
||||||
} else if self.seen.contains(variable) {
|
} else if self.seen.contains(variable) {
|
||||||
let token = self.assignments[variable].name.token();
|
let token = self.assignments[variable].name.token();
|
||||||
self.stack.push(variable);
|
self.stack.push(variable);
|
||||||
return Err(token.error(CircularVariableDependency {
|
Err(token.error(CircularVariableDependency {
|
||||||
variable,
|
variable,
|
||||||
circle: self.stack.clone(),
|
circle: self.stack.clone(),
|
||||||
}));
|
}))
|
||||||
} else if self.assignments.contains_key(variable) {
|
} else if self.assignments.contains_key(variable) {
|
||||||
self.resolve_assignment(variable)?;
|
self.resolve_assignment(variable)
|
||||||
} else {
|
} else {
|
||||||
return Err(name.token().error(UndefinedVariable { variable }));
|
Err(name.token().error(UndefinedVariable { variable }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Call {
|
Expression::Call { thunk } => match thunk {
|
||||||
function,
|
Thunk::Nullary { .. } => Ok(()),
|
||||||
arguments,
|
Thunk::Unary { arg, .. } => self.resolve_expression(arg),
|
||||||
} => Function::resolve(&function.token(), arguments.len())?,
|
Thunk::Binary { args: [a, b], .. } => {
|
||||||
|
self.resolve_expression(a)?;
|
||||||
|
self.resolve_expression(b)
|
||||||
|
}
|
||||||
|
},
|
||||||
Expression::Concatination { lhs, rhs } => {
|
Expression::Concatination { lhs, rhs } => {
|
||||||
self.resolve_expression(lhs)?;
|
self.resolve_expression(lhs)?;
|
||||||
self.resolve_expression(rhs)?;
|
self.resolve_expression(rhs)
|
||||||
}
|
}
|
||||||
Expression::StringLiteral { .. } | Expression::Backtick { .. } => {}
|
Expression::StringLiteral { .. } | Expression::Backtick { .. } => Ok(()),
|
||||||
Expression::Group { contents } => self.resolve_expression(contents)?,
|
Expression::Group { contents } => self.resolve_expression(contents),
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,12 +128,32 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
analysis_error! {
|
analysis_error! {
|
||||||
name: unknown_function,
|
name: unknown_function_parameter,
|
||||||
input: "a = foo()",
|
input: "x := env_var(yy)",
|
||||||
offset: 4,
|
offset: 13,
|
||||||
line: 0,
|
line: 0,
|
||||||
column: 4,
|
column: 13,
|
||||||
width: 3,
|
width: 2,
|
||||||
kind: UnknownFunction{function: "foo"},
|
kind: UndefinedVariable{variable: "yy"},
|
||||||
|
}
|
||||||
|
|
||||||
|
analysis_error! {
|
||||||
|
name: unknown_function_parameter_binary_first,
|
||||||
|
input: "x := env_var_or_default(yy, 'foo')",
|
||||||
|
offset: 24,
|
||||||
|
line: 0,
|
||||||
|
column: 24,
|
||||||
|
width: 2,
|
||||||
|
kind: UndefinedVariable{variable: "yy"},
|
||||||
|
}
|
||||||
|
|
||||||
|
analysis_error! {
|
||||||
|
name: unknown_function_parameter_binary_second,
|
||||||
|
input: "x := env_var_or_default('foo', yy)",
|
||||||
|
offset: 31,
|
||||||
|
line: 0,
|
||||||
|
column: 31,
|
||||||
|
width: 2,
|
||||||
|
kind: UndefinedVariable{variable: "yy"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ pub(crate) use std::{
|
|||||||
cmp,
|
cmp,
|
||||||
collections::{BTreeMap, BTreeSet},
|
collections::{BTreeMap, BTreeSet},
|
||||||
env,
|
env,
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Debug, Display, Formatter},
|
||||||
fs,
|
fs,
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
iter::{self, FromIterator},
|
iter::{self, FromIterator},
|
||||||
@ -26,6 +26,7 @@ pub(crate) use crate::testing;
|
|||||||
pub(crate) use crate::{node::Node, tree::Tree};
|
pub(crate) use crate::{node::Node, tree::Tree};
|
||||||
|
|
||||||
// dependencies
|
// dependencies
|
||||||
|
pub(crate) use derivative::Derivative;
|
||||||
pub(crate) use edit_distance::edit_distance;
|
pub(crate) use edit_distance::edit_distance;
|
||||||
pub(crate) use libc::EXIT_FAILURE;
|
pub(crate) use libc::EXIT_FAILURE;
|
||||||
pub(crate) use log::warn;
|
pub(crate) use log::warn;
|
||||||
@ -52,15 +53,15 @@ pub(crate) use crate::{
|
|||||||
compilation_error::CompilationError, compilation_error_kind::CompilationErrorKind,
|
compilation_error::CompilationError, compilation_error_kind::CompilationErrorKind,
|
||||||
compiler::Compiler, config::Config, config_error::ConfigError, count::Count,
|
compiler::Compiler, config::Config, config_error::ConfigError, count::Count,
|
||||||
dependency::Dependency, enclosure::Enclosure, expression::Expression, fragment::Fragment,
|
dependency::Dependency, enclosure::Enclosure, expression::Expression, fragment::Fragment,
|
||||||
function::Function, function_context::FunctionContext, functions::Functions,
|
function::Function, function_context::FunctionContext, interrupt_guard::InterruptGuard,
|
||||||
interrupt_guard::InterruptGuard, interrupt_handler::InterruptHandler, item::Item,
|
interrupt_handler::InterruptHandler, item::Item, justfile::Justfile, lexer::Lexer, line::Line,
|
||||||
justfile::Justfile, lexer::Lexer, line::Line, list::List, load_error::LoadError, module::Module,
|
list::List, load_error::LoadError, module::Module, name::Name, output_error::OutputError,
|
||||||
name::Name, output_error::OutputError, parameter::Parameter, parser::Parser, platform::Platform,
|
parameter::Parameter, parser::Parser, platform::Platform, position::Position,
|
||||||
position::Position, positional::Positional, recipe::Recipe, recipe_context::RecipeContext,
|
positional::Positional, recipe::Recipe, recipe_context::RecipeContext,
|
||||||
recipe_resolver::RecipeResolver, runtime_error::RuntimeError, search::Search,
|
recipe_resolver::RecipeResolver, runtime_error::RuntimeError, search::Search,
|
||||||
search_config::SearchConfig, search_error::SearchError, set::Set, setting::Setting,
|
search_config::SearchConfig, search_error::SearchError, set::Set, setting::Setting,
|
||||||
settings::Settings, shebang::Shebang, show_whitespace::ShowWhitespace, state::State,
|
settings::Settings, shebang::Shebang, show_whitespace::ShowWhitespace, state::State,
|
||||||
string_literal::StringLiteral, subcommand::Subcommand, table::Table, token::Token,
|
string_literal::StringLiteral, subcommand::Subcommand, table::Table, thunk::Thunk, token::Token,
|
||||||
token_kind::TokenKind, use_color::UseColor, variables::Variables, verbosity::Verbosity,
|
token_kind::TokenKind, use_color::UseColor, variables::Variables, verbosity::Verbosity,
|
||||||
warning::Warning,
|
warning::Warning,
|
||||||
};
|
};
|
||||||
|
@ -14,10 +14,7 @@ pub(crate) enum Expression<'src> {
|
|||||||
token: Token<'src>,
|
token: Token<'src>,
|
||||||
},
|
},
|
||||||
/// `name(arguments)`
|
/// `name(arguments)`
|
||||||
Call {
|
Call { thunk: Thunk<'src> },
|
||||||
function: Name<'src>,
|
|
||||||
arguments: Vec<Expression<'src>>,
|
|
||||||
},
|
|
||||||
/// `lhs + rhs`
|
/// `lhs + rhs`
|
||||||
Concatination {
|
Concatination {
|
||||||
lhs: Box<Expression<'src>>,
|
lhs: Box<Expression<'src>>,
|
||||||
@ -35,35 +32,17 @@ impl<'src> Expression<'src> {
|
|||||||
pub(crate) fn variables<'expression>(&'expression self) -> Variables<'expression, 'src> {
|
pub(crate) fn variables<'expression>(&'expression self) -> Variables<'expression, 'src> {
|
||||||
Variables::new(self)
|
Variables::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn functions<'expression>(&'expression self) -> Functions<'expression, 'src> {
|
|
||||||
Functions::new(self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Display for Expression<'src> {
|
impl<'src> Display for Expression<'src> {
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
|
||||||
match self {
|
match self {
|
||||||
Expression::Backtick { contents, .. } => write!(f, "`{}`", contents)?,
|
Expression::Backtick { contents, .. } => write!(f, "`{}`", contents),
|
||||||
Expression::Concatination { lhs, rhs } => write!(f, "{} + {}", lhs, rhs)?,
|
Expression::Concatination { lhs, rhs } => write!(f, "{} + {}", lhs, rhs),
|
||||||
Expression::StringLiteral { string_literal } => write!(f, "{}", string_literal)?,
|
Expression::StringLiteral { string_literal } => write!(f, "{}", string_literal),
|
||||||
Expression::Variable { name } => write!(f, "{}", name.lexeme())?,
|
Expression::Variable { name } => write!(f, "{}", name.lexeme()),
|
||||||
Expression::Call {
|
Expression::Call { thunk } => write!(f, "{}", thunk),
|
||||||
function,
|
Expression::Group { contents } => write!(f, "({})", contents),
|
||||||
arguments,
|
|
||||||
} => {
|
|
||||||
write!(f, "{}(", function.lexeme())?;
|
|
||||||
for (i, argument) in arguments.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
write!(f, ", {}", argument)?;
|
|
||||||
} else {
|
|
||||||
write!(f, "{}", argument)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write!(f, ")")?;
|
|
||||||
}
|
|
||||||
Expression::Group { contents } => write!(f, "({})", contents)?,
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,14 @@ use crate::common::*;
|
|||||||
|
|
||||||
use target;
|
use target;
|
||||||
|
|
||||||
|
pub(crate) enum Function {
|
||||||
|
Nullary(fn(&FunctionContext) -> Result<String, String>),
|
||||||
|
Unary(fn(&FunctionContext, &str) -> Result<String, String>),
|
||||||
|
Binary(fn(&FunctionContext, &str, &str) -> Result<String, String>),
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref FUNCTIONS: BTreeMap<&'static str, Function> = vec![
|
pub(crate) static ref TABLE: BTreeMap<&'static str, Function> = vec![
|
||||||
("arch", Function::Nullary(arch)),
|
("arch", Function::Nullary(arch)),
|
||||||
("os", Function::Nullary(os)),
|
("os", Function::Nullary(os)),
|
||||||
("os_family", Function::Nullary(os_family)),
|
("os_family", Function::Nullary(os_family)),
|
||||||
@ -18,14 +24,8 @@ lazy_static! {
|
|||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum Function {
|
|
||||||
Nullary(fn(&FunctionContext) -> Result<String, String>),
|
|
||||||
Unary(fn(&FunctionContext, &str) -> Result<String, String>),
|
|
||||||
Binary(fn(&FunctionContext, &str, &str) -> Result<String, String>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
fn argc(&self) -> usize {
|
pub(crate) fn argc(&self) -> usize {
|
||||||
use self::Function::*;
|
use self::Function::*;
|
||||||
match *self {
|
match *self {
|
||||||
Nullary(_) => 0,
|
Nullary(_) => 0,
|
||||||
@ -33,85 +33,26 @@ impl Function {
|
|||||||
Binary(_) => 2,
|
Binary(_) => 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve<'a>(token: &Token<'a>, argc: usize) -> CompilationResult<'a, ()> {
|
|
||||||
let name = token.lexeme();
|
|
||||||
if let Some(function) = FUNCTIONS.get(&name) {
|
|
||||||
use self::Function::*;
|
|
||||||
match (function, argc) {
|
|
||||||
(&Nullary(_), 0) | (&Unary(_), 1) | (&Binary(_), 2) => Ok(()),
|
|
||||||
_ => Err(
|
|
||||||
token.error(CompilationErrorKind::FunctionArgumentCountMismatch {
|
|
||||||
function: name,
|
|
||||||
found: argc,
|
|
||||||
expected: function.argc(),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(token.error(CompilationErrorKind::UnknownFunction {
|
|
||||||
function: token.lexeme(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn evaluate<'a>(
|
|
||||||
function_name: Name<'a>,
|
|
||||||
context: &FunctionContext,
|
|
||||||
arguments: &[String],
|
|
||||||
) -> RunResult<'a, String> {
|
|
||||||
let name = function_name.lexeme();
|
|
||||||
if let Some(function) = FUNCTIONS.get(name) {
|
|
||||||
use self::Function::*;
|
|
||||||
let argc = arguments.len();
|
|
||||||
match (function, argc) {
|
|
||||||
(&Nullary(f), 0) => f(context).map_err(|message| RuntimeError::FunctionCall {
|
|
||||||
function: function_name,
|
|
||||||
message,
|
|
||||||
}),
|
|
||||||
(&Unary(f), 1) => f(context, &arguments[0]).map_err(|message| RuntimeError::FunctionCall {
|
|
||||||
function: function_name,
|
|
||||||
message,
|
|
||||||
}),
|
|
||||||
(&Binary(f), 2) => {
|
|
||||||
f(context, &arguments[0], &arguments[1]).map_err(|message| RuntimeError::FunctionCall {
|
|
||||||
function: function_name,
|
|
||||||
message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => Err(RuntimeError::Internal {
|
|
||||||
message: format!(
|
|
||||||
"attempted to evaluate function `{}` with {} arguments",
|
|
||||||
name, argc
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::Internal {
|
|
||||||
message: format!("attempted to evaluate unknown function: `{}`", name),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn arch(_context: &FunctionContext) -> Result<String, String> {
|
fn arch(_context: &FunctionContext) -> Result<String, String> {
|
||||||
Ok(target::arch().to_string())
|
Ok(target::arch().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn os(_context: &FunctionContext) -> Result<String, String> {
|
fn os(_context: &FunctionContext) -> Result<String, String> {
|
||||||
Ok(target::os().to_string())
|
Ok(target::os().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn os_family(_context: &FunctionContext) -> Result<String, String> {
|
fn os_family(_context: &FunctionContext) -> Result<String, String> {
|
||||||
Ok(target::os_family().to_string())
|
Ok(target::os_family().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn invocation_directory(context: &FunctionContext) -> Result<String, String> {
|
fn invocation_directory(context: &FunctionContext) -> Result<String, String> {
|
||||||
Platform::to_shell_path(context.working_directory, context.invocation_directory)
|
Platform::to_shell_path(context.working_directory, context.invocation_directory)
|
||||||
.map_err(|e| format!("Error getting shell path: {}", e))
|
.map_err(|e| format!("Error getting shell path: {}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn env_var(context: &FunctionContext, key: &str) -> Result<String, String> {
|
fn env_var(context: &FunctionContext, key: &str) -> Result<String, String> {
|
||||||
use std::env::VarError::*;
|
use std::env::VarError::*;
|
||||||
|
|
||||||
if let Some(value) = context.dotenv.get(key) {
|
if let Some(value) = context.dotenv.get(key) {
|
||||||
@ -128,7 +69,7 @@ pub(crate) fn env_var(context: &FunctionContext, key: &str) -> Result<String, St
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn env_var_or_default(
|
fn env_var_or_default(
|
||||||
context: &FunctionContext,
|
context: &FunctionContext,
|
||||||
key: &str,
|
key: &str,
|
||||||
default: &str,
|
default: &str,
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
use crate::common::*;
|
|
||||||
|
|
||||||
pub(crate) struct Functions<'expression, 'src> {
|
|
||||||
stack: Vec<&'expression Expression<'src>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'expression, 'src> Functions<'expression, 'src> {
|
|
||||||
pub(crate) fn new(root: &'expression Expression<'src>) -> Functions<'expression, 'src> {
|
|
||||||
Functions { stack: vec![root] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'expression, 'src> Iterator for Functions<'expression, 'src> {
|
|
||||||
type Item = (Token<'src>, usize);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
match self.stack.pop() {
|
|
||||||
None
|
|
||||||
| Some(Expression::StringLiteral { .. })
|
|
||||||
| Some(Expression::Backtick { .. })
|
|
||||||
| Some(Expression::Variable { .. }) => None,
|
|
||||||
Some(Expression::Call {
|
|
||||||
function,
|
|
||||||
arguments,
|
|
||||||
..
|
|
||||||
}) => Some((function.token(), arguments.len())),
|
|
||||||
Some(Expression::Concatination { lhs, rhs }) => {
|
|
||||||
self.stack.push(lhs);
|
|
||||||
self.stack.push(rhs);
|
|
||||||
self.next()
|
|
||||||
}
|
|
||||||
Some(Expression::Group { contents }) => {
|
|
||||||
self.stack.push(contents);
|
|
||||||
self.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -40,7 +40,6 @@ mod expression;
|
|||||||
mod fragment;
|
mod fragment;
|
||||||
mod function;
|
mod function;
|
||||||
mod function_context;
|
mod function_context;
|
||||||
mod functions;
|
|
||||||
mod interrupt_guard;
|
mod interrupt_guard;
|
||||||
mod interrupt_handler;
|
mod interrupt_handler;
|
||||||
mod item;
|
mod item;
|
||||||
@ -81,6 +80,7 @@ mod state;
|
|||||||
mod string_literal;
|
mod string_literal;
|
||||||
mod subcommand;
|
mod subcommand;
|
||||||
mod table;
|
mod table;
|
||||||
|
mod thunk;
|
||||||
mod token;
|
mod token;
|
||||||
mod token_kind;
|
mod token_kind;
|
||||||
mod use_color;
|
mod use_color;
|
||||||
|
27
src/node.rs
27
src/node.rs
@ -50,12 +50,27 @@ impl<'src> Node<'src> for Expression<'src> {
|
|||||||
fn tree(&self) -> Tree<'src> {
|
fn tree(&self) -> Tree<'src> {
|
||||||
match self {
|
match self {
|
||||||
Expression::Concatination { lhs, rhs } => Tree::atom("+").push(lhs.tree()).push(rhs.tree()),
|
Expression::Concatination { lhs, rhs } => Tree::atom("+").push(lhs.tree()).push(rhs.tree()),
|
||||||
Expression::Call {
|
Expression::Call { thunk } => {
|
||||||
function,
|
let mut tree = Tree::atom("call");
|
||||||
arguments,
|
|
||||||
} => Tree::atom("call")
|
use Thunk::*;
|
||||||
.push(function.lexeme())
|
match thunk {
|
||||||
.extend(arguments.iter().map(|argument| argument.tree())),
|
Nullary { name, .. } => tree.push_mut(name.lexeme()),
|
||||||
|
Unary { name, arg, .. } => {
|
||||||
|
tree.push_mut(name.lexeme());
|
||||||
|
tree.push_mut(arg.tree());
|
||||||
|
}
|
||||||
|
Binary {
|
||||||
|
name, args: [a, b], ..
|
||||||
|
} => {
|
||||||
|
tree.push_mut(name.lexeme());
|
||||||
|
tree.push_mut(a.tree());
|
||||||
|
tree.push_mut(b.tree());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree
|
||||||
|
}
|
||||||
Expression::Variable { name } => Tree::atom(name.lexeme()),
|
Expression::Variable { name } => Tree::atom(name.lexeme()),
|
||||||
Expression::StringLiteral {
|
Expression::StringLiteral {
|
||||||
string_literal: StringLiteral { cooked, .. },
|
string_literal: StringLiteral { cooked, .. },
|
||||||
|
@ -379,8 +379,7 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
|
|||||||
if self.next_is(ParenL) {
|
if self.next_is(ParenL) {
|
||||||
let arguments = self.parse_sequence()?;
|
let arguments = self.parse_sequence()?;
|
||||||
Ok(Expression::Call {
|
Ok(Expression::Call {
|
||||||
function: name,
|
thunk: Thunk::resolve(name, arguments)?,
|
||||||
arguments,
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Ok(Expression::Variable { name })
|
Ok(Expression::Variable { name })
|
||||||
@ -837,20 +836,20 @@ mod tests {
|
|||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: call_one_arg,
|
name: call_one_arg,
|
||||||
text: "x := foo(y)",
|
text: "x := env_var(y)",
|
||||||
tree: (justfile (assignment x (call foo y))),
|
tree: (justfile (assignment x (call env_var y))),
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: call_multiple_args,
|
name: call_multiple_args,
|
||||||
text: "x := foo(y, z)",
|
text: "x := env_var_or_default(y, z)",
|
||||||
tree: (justfile (assignment x (call foo y z))),
|
tree: (justfile (assignment x (call env_var_or_default y z))),
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: call_trailing_comma,
|
name: call_trailing_comma,
|
||||||
text: "x := foo(y,)",
|
text: "x := env_var(y,)",
|
||||||
tree: (justfile (assignment x (call foo y))),
|
tree: (justfile (assignment x (call env_var y))),
|
||||||
}
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
@ -1718,4 +1717,76 @@ mod tests {
|
|||||||
setting: "shall",
|
setting: "shall",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error! {
|
||||||
|
name: unknown_function,
|
||||||
|
input: "a = foo()",
|
||||||
|
offset: 4,
|
||||||
|
line: 0,
|
||||||
|
column: 4,
|
||||||
|
width: 3,
|
||||||
|
kind: UnknownFunction{function: "foo"},
|
||||||
|
}
|
||||||
|
|
||||||
|
error! {
|
||||||
|
name: unknown_function_in_interpolation,
|
||||||
|
input: "a:\n echo {{bar()}}",
|
||||||
|
offset: 11,
|
||||||
|
line: 1,
|
||||||
|
column: 8,
|
||||||
|
width: 3,
|
||||||
|
kind: UnknownFunction{function: "bar"},
|
||||||
|
}
|
||||||
|
|
||||||
|
error! {
|
||||||
|
name: unknown_function_in_default,
|
||||||
|
input: "a f=baz():",
|
||||||
|
offset: 4,
|
||||||
|
line: 0,
|
||||||
|
column: 4,
|
||||||
|
width: 3,
|
||||||
|
kind: UnknownFunction{function: "baz"},
|
||||||
|
}
|
||||||
|
|
||||||
|
error! {
|
||||||
|
name: function_argument_count_nullary,
|
||||||
|
input: "x := arch('foo')",
|
||||||
|
offset: 5,
|
||||||
|
line: 0,
|
||||||
|
column: 5,
|
||||||
|
width: 4,
|
||||||
|
kind: FunctionArgumentCountMismatch {
|
||||||
|
function: "arch",
|
||||||
|
found: 1,
|
||||||
|
expected: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
error! {
|
||||||
|
name: function_argument_count_unary,
|
||||||
|
input: "x := env_var()",
|
||||||
|
offset: 5,
|
||||||
|
line: 0,
|
||||||
|
column: 5,
|
||||||
|
width: 7,
|
||||||
|
kind: FunctionArgumentCountMismatch {
|
||||||
|
function: "env_var",
|
||||||
|
found: 0,
|
||||||
|
expected: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
error! {
|
||||||
|
name: function_argument_count_binary,
|
||||||
|
input: "x := env_var_or_default('foo')",
|
||||||
|
offset: 5,
|
||||||
|
line: 0,
|
||||||
|
column: 5,
|
||||||
|
width: 18,
|
||||||
|
kind: FunctionArgumentCountMismatch {
|
||||||
|
function: "env_var_or_default",
|
||||||
|
found: 1,
|
||||||
|
expected: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,6 @@ impl<'a, 'b> RecipeResolver<'a, 'b> {
|
|||||||
for recipe in resolver.resolved_recipes.values() {
|
for recipe in resolver.resolved_recipes.values() {
|
||||||
for parameter in &recipe.parameters {
|
for parameter in &recipe.parameters {
|
||||||
if let Some(expression) = ¶meter.default {
|
if let Some(expression) = ¶meter.default {
|
||||||
for (function, argc) in expression.functions() {
|
|
||||||
Function::resolve(&function, argc)?;
|
|
||||||
}
|
|
||||||
for variable in expression.variables() {
|
for variable in expression.variables() {
|
||||||
resolver.resolve_variable(&variable, &[])?;
|
resolver.resolve_variable(&variable, &[])?;
|
||||||
}
|
}
|
||||||
@ -38,9 +35,6 @@ impl<'a, 'b> RecipeResolver<'a, 'b> {
|
|||||||
for line in &recipe.body {
|
for line in &recipe.body {
|
||||||
for fragment in &line.fragments {
|
for fragment in &line.fragments {
|
||||||
if let Fragment::Interpolation { expression, .. } = fragment {
|
if let Fragment::Interpolation { expression, .. } = fragment {
|
||||||
for (function, argc) in expression.functions() {
|
|
||||||
Function::resolve(&function, argc)?;
|
|
||||||
}
|
|
||||||
for variable in expression.variables() {
|
for variable in expression.variables() {
|
||||||
resolver.resolve_variable(&variable, &recipe.parameters)?;
|
resolver.resolve_variable(&variable, &recipe.parameters)?;
|
||||||
}
|
}
|
||||||
@ -186,26 +180,6 @@ mod tests {
|
|||||||
kind: UndefinedVariable{variable: "lol"},
|
kind: UndefinedVariable{variable: "lol"},
|
||||||
}
|
}
|
||||||
|
|
||||||
analysis_error! {
|
|
||||||
name: unknown_function_in_interpolation,
|
|
||||||
input: "a:\n echo {{bar()}}",
|
|
||||||
offset: 11,
|
|
||||||
line: 1,
|
|
||||||
column: 8,
|
|
||||||
width: 3,
|
|
||||||
kind: UnknownFunction{function: "bar"},
|
|
||||||
}
|
|
||||||
|
|
||||||
analysis_error! {
|
|
||||||
name: unknown_function_in_default,
|
|
||||||
input: "a f=baz():",
|
|
||||||
offset: 4,
|
|
||||||
line: 0,
|
|
||||||
column: 4,
|
|
||||||
width: 3,
|
|
||||||
kind: UnknownFunction{function: "baz"},
|
|
||||||
}
|
|
||||||
|
|
||||||
analysis_error! {
|
analysis_error! {
|
||||||
name: unknown_variable_in_default,
|
name: unknown_variable_in_default,
|
||||||
input: "a f=foo:",
|
input: "a f=foo:",
|
||||||
|
77
src/thunk.rs
Normal file
77
src/thunk.rs
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
#[derive(Derivative)]
|
||||||
|
#[derivative(Debug, PartialEq = "feature_allow_slow_enum")]
|
||||||
|
pub(crate) enum Thunk<'src> {
|
||||||
|
Nullary {
|
||||||
|
name: Name<'src>,
|
||||||
|
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||||
|
function: fn(&FunctionContext) -> Result<String, String>,
|
||||||
|
},
|
||||||
|
Unary {
|
||||||
|
name: Name<'src>,
|
||||||
|
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||||
|
function: fn(&FunctionContext, &str) -> Result<String, String>,
|
||||||
|
arg: Box<Expression<'src>>,
|
||||||
|
},
|
||||||
|
Binary {
|
||||||
|
name: Name<'src>,
|
||||||
|
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||||
|
function: fn(&FunctionContext, &str, &str) -> Result<String, String>,
|
||||||
|
args: [Box<Expression<'src>>; 2],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'src> Thunk<'src> {
|
||||||
|
pub(crate) fn resolve(
|
||||||
|
name: Name<'src>,
|
||||||
|
mut arguments: Vec<Expression<'src>>,
|
||||||
|
) -> CompilationResult<'src, Thunk<'src>> {
|
||||||
|
if let Some(function) = crate::function::TABLE.get(&name.lexeme()) {
|
||||||
|
match (function, arguments.len()) {
|
||||||
|
(Function::Nullary(function), 0) => Ok(Thunk::Nullary {
|
||||||
|
function: *function,
|
||||||
|
name,
|
||||||
|
}),
|
||||||
|
(Function::Unary(function), 1) => Ok(Thunk::Unary {
|
||||||
|
function: *function,
|
||||||
|
arg: Box::new(arguments.pop().unwrap()),
|
||||||
|
name,
|
||||||
|
}),
|
||||||
|
(Function::Binary(function), 2) => {
|
||||||
|
let b = Box::new(arguments.pop().unwrap());
|
||||||
|
let a = Box::new(arguments.pop().unwrap());
|
||||||
|
Ok(Thunk::Binary {
|
||||||
|
function: *function,
|
||||||
|
args: [a, b],
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(
|
||||||
|
name.error(CompilationErrorKind::FunctionArgumentCountMismatch {
|
||||||
|
function: name.lexeme(),
|
||||||
|
found: arguments.len(),
|
||||||
|
expected: function.argc(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(name.error(CompilationErrorKind::UnknownFunction {
|
||||||
|
function: name.lexeme(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Thunk<'_> {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
use Thunk::*;
|
||||||
|
match self {
|
||||||
|
Nullary { name, .. } => write!(f, "{}()", name.lexeme()),
|
||||||
|
Unary { name, arg, .. } => write!(f, "{}({})", name.lexeme(), arg),
|
||||||
|
Binary {
|
||||||
|
name, args: [a, b], ..
|
||||||
|
} => write!(f, "{}({}, {})", name.lexeme(), a, b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user