mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-23 18:21:38 +03:00
merge master, color only at cli as it breaks tests, remove debug
This commit is contained in:
parent
780a2744ec
commit
20d64970ab
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1202,7 +1202,6 @@ name = "leo-ast"
|
||||
version = "1.5.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"colored",
|
||||
"criterion",
|
||||
"indexmap",
|
||||
"leo-input",
|
||||
@ -1339,6 +1338,7 @@ dependencies = [
|
||||
name = "leo-parser"
|
||||
version = "1.5.2"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"criterion",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
|
@ -238,9 +238,7 @@ impl<'a, T: Monoid, R: MonoidalReducerStatement<'a, T>> MonoidalDirector<'a, T,
|
||||
pub fn reduce_console(&mut self, input: &ConsoleStatement<'a>) -> T {
|
||||
let argument = match &input.function {
|
||||
ConsoleFunction::Assert(e) => self.reduce_expression(e.get()),
|
||||
ConsoleFunction::Debug(f) | ConsoleFunction::Error(f) | ConsoleFunction::Log(f) => {
|
||||
self.reduce_formatted_string(f)
|
||||
}
|
||||
ConsoleFunction::Error(f) | ConsoleFunction::Log(f) => self.reduce_formatted_string(f),
|
||||
};
|
||||
|
||||
self.reducer.reduce_console(input, argument)
|
||||
|
@ -259,7 +259,7 @@ impl<'a, R: ReconstructingReducerStatement<'a>> ReconstructingDirector<'a, R> {
|
||||
let argument = self.reduce_expression(argument.get());
|
||||
self.reducer.reduce_console_assert(input, argument)
|
||||
}
|
||||
ConsoleFunction::Debug(f) | ConsoleFunction::Error(f) | ConsoleFunction::Log(f) => {
|
||||
ConsoleFunction::Error(f) | ConsoleFunction::Log(f) => {
|
||||
let formatted = self.reduce_formatted_string(f.clone());
|
||||
self.reducer.reduce_console_log(input, formatted)
|
||||
}
|
||||
|
@ -300,7 +300,6 @@ pub trait ReconstructingReducerStatement<'a>: ReconstructingReducerExpression<'a
|
||||
span: input.span,
|
||||
function: match input.function {
|
||||
ConsoleFunction::Assert(_) => unimplemented!(),
|
||||
ConsoleFunction::Debug(_) => ConsoleFunction::Debug(argument),
|
||||
ConsoleFunction::Error(_) => ConsoleFunction::Error(argument),
|
||||
ConsoleFunction::Log(_) => ConsoleFunction::Log(argument),
|
||||
},
|
||||
|
@ -336,9 +336,7 @@ impl<'a, R: StatementVisitor<'a>> VisitorDirector<'a, R> {
|
||||
VisitResult::VisitChildren => {
|
||||
match &input.function {
|
||||
ConsoleFunction::Assert(e) => self.visit_expression(e)?,
|
||||
ConsoleFunction::Debug(f) | ConsoleFunction::Error(f) | ConsoleFunction::Log(f) => {
|
||||
self.visit_formatted_string(f)?
|
||||
}
|
||||
ConsoleFunction::Error(f) | ConsoleFunction::Log(f) => self.visit_formatted_string(f)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ pub struct ConsoleArgs<'a> {
|
||||
#[derive(Clone)]
|
||||
pub enum ConsoleFunction<'a> {
|
||||
Assert(Cell<&'a Expression<'a>>),
|
||||
Debug(ConsoleArgs<'a>),
|
||||
Error(ConsoleArgs<'a>),
|
||||
Log(ConsoleArgs<'a>),
|
||||
}
|
||||
@ -89,7 +88,6 @@ impl<'a> FromAst<'a, leo_ast::ConsoleStatement> for ConsoleStatement<'a> {
|
||||
AstConsoleFunction::Assert(expression) => ConsoleFunction::Assert(Cell::new(
|
||||
<&Expression<'a>>::from_ast(scope, expression, Some(Type::Boolean.into()))?,
|
||||
)),
|
||||
AstConsoleFunction::Debug(args) => ConsoleFunction::Debug(ConsoleArgs::from_ast(scope, args, None)?),
|
||||
AstConsoleFunction::Error(args) => ConsoleFunction::Error(ConsoleArgs::from_ast(scope, args, None)?),
|
||||
AstConsoleFunction::Log(args) => ConsoleFunction::Log(ConsoleArgs::from_ast(scope, args, None)?),
|
||||
},
|
||||
@ -103,7 +101,6 @@ impl<'a> Into<leo_ast::ConsoleStatement> for &ConsoleStatement<'a> {
|
||||
leo_ast::ConsoleStatement {
|
||||
function: match &self.function {
|
||||
Assert(e) => AstConsoleFunction::Assert(e.get().into()),
|
||||
Debug(args) => AstConsoleFunction::Debug(args.into()),
|
||||
Error(args) => AstConsoleFunction::Error(args.into()),
|
||||
Log(args) => AstConsoleFunction::Log(args.into()),
|
||||
},
|
||||
|
@ -1,3 +0,0 @@
|
||||
function main() {
|
||||
console.debug("hello debug");
|
||||
}
|
@ -40,14 +40,6 @@ fn test_log_input() {
|
||||
load_asg(program_string).unwrap();
|
||||
}
|
||||
|
||||
// Debug
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let program_string = include_str!("debug.leo");
|
||||
load_asg(program_string).unwrap();
|
||||
}
|
||||
|
||||
// Error
|
||||
|
||||
#[test]
|
||||
|
@ -47,9 +47,6 @@ version = "0.4"
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.colored]
|
||||
version = "2.0"
|
||||
|
||||
[features]
|
||||
default = [ ]
|
||||
ci_skip = [ ]
|
||||
|
@ -17,8 +17,6 @@
|
||||
use crate::{LeoError, Span};
|
||||
use std::{fmt, sync::Arc};
|
||||
|
||||
use colored::Colorize;
|
||||
|
||||
pub const INDENT: &str = " ";
|
||||
|
||||
/// Formatted compiler error type
|
||||
@ -85,9 +83,7 @@ impl fmt::Display for FormattedError {
|
||||
path = &*self.path,
|
||||
line_start = self.line_start,
|
||||
start = self.col_start,
|
||||
)
|
||||
.bold()
|
||||
.red();
|
||||
);
|
||||
|
||||
write!(f, "{}", error_message)?;
|
||||
|
||||
|
@ -392,7 +392,7 @@ impl Canonicalizer {
|
||||
ConsoleFunction::Assert(expression) => {
|
||||
ConsoleFunction::Assert(self.canonicalize_expression(expression))
|
||||
}
|
||||
ConsoleFunction::Debug(args) | ConsoleFunction::Error(args) | ConsoleFunction::Log(args) => {
|
||||
ConsoleFunction::Error(args) | ConsoleFunction::Log(args) => {
|
||||
let parameters = args
|
||||
.parameters
|
||||
.iter()
|
||||
@ -406,7 +406,6 @@ impl Canonicalizer {
|
||||
};
|
||||
|
||||
match &console_function_call.function {
|
||||
ConsoleFunction::Debug(_) => ConsoleFunction::Debug(console_args),
|
||||
ConsoleFunction::Error(_) => ConsoleFunction::Error(console_args),
|
||||
ConsoleFunction::Log(_) => ConsoleFunction::Log(console_args),
|
||||
_ => unimplemented!(), // impossible
|
||||
|
@ -390,7 +390,7 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
|
||||
) -> Result<ConsoleStatement, ReducerError> {
|
||||
let function = match &console_function_call.function {
|
||||
ConsoleFunction::Assert(expression) => ConsoleFunction::Assert(self.reduce_expression(expression)?),
|
||||
ConsoleFunction::Debug(args) | ConsoleFunction::Error(args) | ConsoleFunction::Log(args) => {
|
||||
ConsoleFunction::Error(args) | ConsoleFunction::Log(args) => {
|
||||
let mut parameters = vec![];
|
||||
for parameter in args.parameters.iter() {
|
||||
parameters.push(self.reduce_expression(parameter)?);
|
||||
@ -403,7 +403,6 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
|
||||
};
|
||||
|
||||
match &console_function_call.function {
|
||||
ConsoleFunction::Debug(_) => ConsoleFunction::Debug(formatted),
|
||||
ConsoleFunction::Error(_) => ConsoleFunction::Error(formatted),
|
||||
ConsoleFunction::Log(_) => ConsoleFunction::Log(formatted),
|
||||
_ => return Err(ReducerError::impossible_console_assert_call(&args.span)),
|
||||
|
@ -22,7 +22,6 @@ use std::fmt;
|
||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
|
||||
pub enum ConsoleFunction {
|
||||
Assert(Expression),
|
||||
Debug(ConsoleArgs),
|
||||
Error(ConsoleArgs),
|
||||
Log(ConsoleArgs),
|
||||
}
|
||||
@ -31,7 +30,6 @@ impl fmt::Display for ConsoleFunction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ConsoleFunction::Assert(assert) => write!(f, "assert({})", assert),
|
||||
ConsoleFunction::Debug(debug) => write!(f, "debug({})", debug),
|
||||
ConsoleFunction::Error(error) => write!(f, "error{})", error),
|
||||
ConsoleFunction::Log(log) => write!(f, "log({})", log),
|
||||
}
|
||||
@ -42,18 +40,14 @@ impl Node for ConsoleFunction {
|
||||
fn span(&self) -> &Span {
|
||||
match self {
|
||||
ConsoleFunction::Assert(assert) => assert.span(),
|
||||
ConsoleFunction::Debug(formatted) | ConsoleFunction::Error(formatted) | ConsoleFunction::Log(formatted) => {
|
||||
&formatted.span
|
||||
}
|
||||
ConsoleFunction::Error(formatted) | ConsoleFunction::Log(formatted) => &formatted.span,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_span(&mut self, span: Span) {
|
||||
match self {
|
||||
ConsoleFunction::Assert(assert) => assert.set_span(span),
|
||||
ConsoleFunction::Debug(formatted) | ConsoleFunction::Error(formatted) | ConsoleFunction::Log(formatted) => {
|
||||
formatted.set_span(span)
|
||||
}
|
||||
ConsoleFunction::Error(formatted) | ConsoleFunction::Log(formatted) => formatted.set_span(span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,13 +39,6 @@ impl<'a, F: PrimeField, G: GroupType<F>> ConstrainedProgram<'a, F, G> {
|
||||
&console.span.clone().unwrap_or_default(),
|
||||
)?;
|
||||
}
|
||||
ConsoleFunction::Debug(string) => {
|
||||
let string = self.format(cs, string)?;
|
||||
|
||||
if get_indicator_value(indicator) {
|
||||
tracing::debug!("{}", string);
|
||||
}
|
||||
}
|
||||
ConsoleFunction::Error(string) => {
|
||||
let string = self.format(cs, string)?;
|
||||
|
||||
|
@ -597,8 +597,7 @@ impl<R: ReconstructingReducer, O: CombinerOptions> CombineAstAsgDirector<R, O> {
|
||||
(AstConsoleFunction::Assert(ast_expression), AsgConsoleFunction::Assert(asg_expression)) => {
|
||||
AstConsoleFunction::Assert(self.reduce_expression(&ast_expression, asg_expression.get())?)
|
||||
}
|
||||
(AstConsoleFunction::Debug(ast_console_args), AsgConsoleFunction::Debug(asg_format))
|
||||
| (AstConsoleFunction::Error(ast_console_args), AsgConsoleFunction::Error(asg_format))
|
||||
(AstConsoleFunction::Error(ast_console_args), AsgConsoleFunction::Error(asg_format))
|
||||
| (AstConsoleFunction::Log(ast_console_args), AsgConsoleFunction::Log(asg_format)) => {
|
||||
let mut parameters = vec![];
|
||||
for (ast_parameter, asg_parameter) in
|
||||
@ -614,7 +613,6 @@ impl<R: ReconstructingReducer, O: CombinerOptions> CombineAstAsgDirector<R, O> {
|
||||
};
|
||||
|
||||
match &ast.function {
|
||||
AstConsoleFunction::Debug(_) => AstConsoleFunction::Debug(args),
|
||||
AstConsoleFunction::Error(_) => AstConsoleFunction::Error(args),
|
||||
AstConsoleFunction::Log(_) => AstConsoleFunction::Log(args),
|
||||
_ => return Err(ReducerError::impossible_console_assert_call(&ast_console_args.span)),
|
||||
|
15
leo/main.rs
15
leo/main.rs
@ -230,7 +230,20 @@ fn handle_error<T>(res: Result<T>) -> T {
|
||||
match res {
|
||||
Ok(t) => t,
|
||||
Err(err) => {
|
||||
eprintln!("{} {}", "Error:".bold().red(), err);
|
||||
eprintln!(
|
||||
"{} {}",
|
||||
"Error:".bold().red(),
|
||||
err.to_string()
|
||||
.lines()
|
||||
.enumerate()
|
||||
.map(|(i, l)| if i == 0 {
|
||||
l.bold().red().to_string()
|
||||
} else {
|
||||
l.to_string()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,9 @@ version = "1.7"
|
||||
[dependencies.tendril]
|
||||
version = "0.4"
|
||||
|
||||
[dev-dependencies.colored]
|
||||
version = "2.0"
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.3"
|
||||
|
||||
|
@ -277,13 +277,12 @@ impl ParserContext {
|
||||
let expr = self.parse_expression()?;
|
||||
ConsoleFunction::Assert(expr)
|
||||
}
|
||||
"debug" => ConsoleFunction::Debug(self.parse_console_args()?),
|
||||
"error" => ConsoleFunction::Error(self.parse_console_args()?),
|
||||
"log" => ConsoleFunction::Log(self.parse_console_args()?),
|
||||
x => {
|
||||
return Err(SyntaxError::unexpected_ident(
|
||||
&x,
|
||||
&["assert", "debug", "error", "log"],
|
||||
&["assert", "error", "log"],
|
||||
&function.span,
|
||||
));
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use colored::Colorize;
|
||||
use leo_ast::{Expression, ExpressionStatement, Span, Statement, ValueExpression};
|
||||
use leo_test_framework::{
|
||||
runner::{Namespace, ParseType, Runner},
|
||||
@ -95,7 +96,11 @@ impl Namespace for ParseStatementNamespace {
|
||||
}
|
||||
|
||||
fn run_test(&self, test: Test) -> Result<Value, String> {
|
||||
let tokenizer = tokenizer::tokenize("test", test.content.into()).map_err(|x| x.to_string())?;
|
||||
let tokenizer = tokenizer::tokenize("test", test.content.into()).map_err(|x| {
|
||||
let s = x.to_string();
|
||||
println!("s is {}", s);
|
||||
s
|
||||
})?;
|
||||
if tokenizer
|
||||
.iter()
|
||||
.all(|x| matches!(x.token, Token::CommentLine(_) | Token::CommentBlock(_)))
|
||||
@ -108,7 +113,11 @@ impl Namespace for ParseStatementNamespace {
|
||||
}
|
||||
let mut tokens = ParserContext::new(tokenizer);
|
||||
|
||||
let parsed = tokens.parse_statement().map_err(|x| x.to_string())?;
|
||||
let parsed = tokens.parse_statement().map_err(|x| {
|
||||
let s = x.to_string();
|
||||
println!("bruh {}", s);
|
||||
s
|
||||
})?;
|
||||
not_fully_consumed(&mut tokens)?;
|
||||
|
||||
Ok(serde_yaml::to_value(&parsed).expect("serialization failed"))
|
||||
|
@ -1,10 +0,0 @@
|
||||
/*
|
||||
namespace: Compile
|
||||
expectation: Pass
|
||||
input_file: input/dummy.in
|
||||
*/
|
||||
|
||||
function main(y: bool) -> bool {
|
||||
console.debug("hello debug");
|
||||
return y == true;
|
||||
}
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- " --> compiler-test:6:19\n |\n 6 | const x = a[i..10];\n | ^^^^^^^^\n |\n = array size cannot be inferred, add explicit types"
|
||||
- " --> compiler-test:7:17\n |\n 7 | console.debug(\"{}\", x);\n | ^^^^^\n |\n = expected identifier 'assert', 'error', 'log' -- got 'debug'"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- " --> :0:0\n |\n |\n |\n = failed to resolve import: 'core'"
|
||||
- " --> :0:0\n |\n |\n = failed to resolve import: 'core'"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- " --> :0:0\n |\n |\n |\n = failed to resolve import: 'core'"
|
||||
- " --> :0:0\n |\n |\n = failed to resolve import: 'core'"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- " --> :0:0\n |\n |\n |\n = failed to resolve import: 'core.unstable'"
|
||||
- " --> :0:0\n |\n |\n = failed to resolve import: 'core.unstable'"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- " --> compiler-test:8:1\n |\n 8 | function main() {\n 9 | ...\n 10 | }\n | ^\n |\n = a function named \"main\" already exists in this scope"
|
||||
- " --> compiler-test:8:1\n |\n 8 | function main() {\n|\n 9 | ...\n|\n 10 | }\n | ^\n |\n = a function named \"main\" already exists in this scope"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- " --> compiler-test:4:5\n |\n 4 | if true {\n 5 | ...\n 6 | }\n | ^\n |\n = function 'main' failed to validate return path: 'cannot have asymmetrical return in if statement'"
|
||||
- " --> compiler-test:4:5\n |\n 4 | if true {\n|\n 5 | ...\n|\n 6 | }\n | ^\n |\n = function 'main' failed to validate return path: 'cannot have asymmetrical return in if statement'"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- " --> compiler-test:3:1\n |\n 3 | function main() -> bool {\n 4 | ...\n 5 | }\n 6 | \n 7 | \n 8 | \n 9 | \n 10 | \n | ^\n |\n = function 'main' missing return for all paths"
|
||||
- " --> compiler-test:3:1\n |\n 3 | function main() -> bool {\n|\n 4 | ...\n|\n 5 | }\n|\n 6 | \n|\n 7 | \n|\n 8 | \n|\n 9 | \n|\n 10 | \n | ^\n |\n = function 'main' missing return for all paths"
|
||||
|
@ -2,4 +2,4 @@
|
||||
namespace: Compile
|
||||
expectation: Fail
|
||||
outputs:
|
||||
- " --> compiler-test:3:1\n |\n 3 | function main() -> bool {\n 4 | ...\n 5 | }\n | ^\n |\n = Mismatched types. Expected register output type `u8`, found type `bool`."
|
||||
- " --> compiler-test:3:1\n |\n 3 | function main() -> bool {\n|\n 4 | ...\n|\n 5 | }\n | ^\n |\n = Mismatched types. Expected register output type `u8`, found type `bool`."
|
||||
|
@ -80,73 +80,6 @@ outputs:
|
||||
col_stop: 18
|
||||
path: test
|
||||
content: "console.error(\"x\");"
|
||||
- Console:
|
||||
function:
|
||||
Debug:
|
||||
string:
|
||||
- Scalar: 123
|
||||
- Scalar: 125
|
||||
parameters:
|
||||
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":21,\\\"col_stop\\\":22,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.debug(\\\\\\\"{}\\\\\\\", x);\\\"}\"}"
|
||||
span:
|
||||
line_start: 1
|
||||
line_stop: 1
|
||||
col_start: 15
|
||||
col_stop: 22
|
||||
path: test
|
||||
content: "console.debug(\"{}\", x);"
|
||||
span:
|
||||
line_start: 1
|
||||
line_stop: 1
|
||||
col_start: 1
|
||||
col_stop: 22
|
||||
path: test
|
||||
content: "console.debug(\"{}\", x);"
|
||||
- Console:
|
||||
function:
|
||||
Debug:
|
||||
string:
|
||||
- Scalar: 123
|
||||
- Scalar: 125
|
||||
- Scalar: 123
|
||||
- Scalar: 125
|
||||
parameters:
|
||||
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":23,\\\"col_stop\\\":24,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.debug(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}"
|
||||
- Identifier: "{\"name\":\"y\",\"span\":\"{\\\"line_start\\\":1,\\\"line_stop\\\":1,\\\"col_start\\\":26,\\\"col_stop\\\":27,\\\"path\\\":\\\"test\\\",\\\"content\\\":\\\"console.debug(\\\\\\\"{}{}\\\\\\\", x, y);\\\"}\"}"
|
||||
span:
|
||||
line_start: 1
|
||||
line_stop: 1
|
||||
col_start: 15
|
||||
col_stop: 27
|
||||
path: test
|
||||
content: "console.debug(\"{}{}\", x, y);"
|
||||
span:
|
||||
line_start: 1
|
||||
line_stop: 1
|
||||
col_start: 1
|
||||
col_stop: 27
|
||||
path: test
|
||||
content: "console.debug(\"{}{}\", x, y);"
|
||||
- Console:
|
||||
function:
|
||||
Debug:
|
||||
string:
|
||||
- Scalar: 120
|
||||
parameters: []
|
||||
span:
|
||||
line_start: 1
|
||||
line_stop: 1
|
||||
col_start: 15
|
||||
col_stop: 18
|
||||
path: test
|
||||
content: "console.debug(\"x\");"
|
||||
span:
|
||||
line_start: 1
|
||||
line_stop: 1
|
||||
col_start: 1
|
||||
col_stop: 18
|
||||
path: test
|
||||
content: "console.debug(\"x\");"
|
||||
- Console:
|
||||
function:
|
||||
Log:
|
||||
|
@ -13,13 +13,6 @@ console.error("{}{}", x, y);
|
||||
console.error("x");
|
||||
|
||||
|
||||
console.debug("{}", x);
|
||||
|
||||
console.debug("{}{}", x, y);
|
||||
|
||||
console.debug("x");
|
||||
|
||||
|
||||
console.log("{}", x);
|
||||
|
||||
console.log("{}{}", x, y);
|
||||
|
Loading…
Reference in New Issue
Block a user