Merge pull request #2023 from AleoHQ/feat/asserts

[Feature] Asserts
This commit is contained in:
Collin Chin 2022-08-17 16:36:39 -07:00 committed by GitHub
commit 7f37b2f3d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 214 additions and 342 deletions

View File

@ -217,24 +217,14 @@ pub trait StatementReconstructor: ExpressionReconstructor {
Statement::Console(ConsoleStatement {
function: match input.function {
ConsoleFunction::Assert(expr) => ConsoleFunction::Assert(self.reconstruct_expression(expr).0),
ConsoleFunction::Error(fmt) => ConsoleFunction::Error(ConsoleArgs {
string: fmt.string,
parameters: fmt
.parameters
.into_iter()
.map(|p| self.reconstruct_expression(p).0)
.collect(),
span: fmt.span,
}),
ConsoleFunction::Log(fmt) => ConsoleFunction::Log(ConsoleArgs {
string: fmt.string,
parameters: fmt
.parameters
.into_iter()
.map(|p| self.reconstruct_expression(p).0)
.collect(),
span: fmt.span,
}),
ConsoleFunction::AssertEq(left, right) => ConsoleFunction::AssertEq(
self.reconstruct_expression(left).0,
self.reconstruct_expression(right).0,
),
ConsoleFunction::AssertNeq(left, right) => ConsoleFunction::AssertNeq(
self.reconstruct_expression(left).0,
self.reconstruct_expression(right).0,
),
},
span: input.span,
})

View File

@ -157,10 +157,13 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> {
ConsoleFunction::Assert(expr) => {
self.visit_expression(expr, &Default::default());
}
ConsoleFunction::Error(fmt) | ConsoleFunction::Log(fmt) => {
fmt.parameters.iter().for_each(|expr| {
self.visit_expression(expr, &Default::default());
});
ConsoleFunction::AssertEq(left, right) => {
self.visit_expression(left, &Default::default());
self.visit_expression(right, &Default::default());
}
ConsoleFunction::AssertNeq(left, right) => {
self.visit_expression(left, &Default::default());
self.visit_expression(right, &Default::default());
}
};
}

View File

@ -1,49 +0,0 @@
// Copyright (C) 2019-2022 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// 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 crate::{Expression, Node, StaticString};
use leo_span::Span;
use serde::{Deserialize, Serialize};
use std::fmt;
/// The arguments `args` passed to `console.log(args)` or `console.error(args)`.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct ConsoleArgs {
/// The formatting string with `parameters` interpolated into it.
pub string: StaticString,
/// Parameters to interpolate in `string`.
pub parameters: Vec<Expression>,
/// The span from `(` to `)`.
pub span: Span,
}
impl fmt::Display for ConsoleArgs {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"\"{}\", {}",
self.string,
self.parameters
.iter()
.map(|p| p.to_string())
.collect::<Vec<_>>()
.join(",")
)
}
}
crate::simple_node_impl!(ConsoleArgs);

View File

@ -14,8 +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 crate::{ConsoleArgs, Expression, Node};
use leo_span::Span;
use crate::Expression;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -23,39 +22,20 @@ use std::fmt;
/// A console logging function to invoke.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum ConsoleFunction {
/// A `console.assert(expr)` call to invoke,
/// asserting that the expression evaluates to `true`.
/// A `console.assert(expr)` call to invoke, asserting that the expression evaluates to true.
Assert(Expression),
/// A `console.error(args)` call to invoke,
/// resulting in an error at runtime.
Error(ConsoleArgs),
/// A `console.log(args)` call to invoke,
/// resulting in a log message at runtime.
Log(ConsoleArgs),
/// A `console.assert_eq(expr1, expr2)` call to invoke, asserting that the operands are equal.
AssertEq(Expression, Expression),
/// A `console.assert_neq(expr1, expr2)` call to invoke, asserting that the operands are not equal.
AssertNeq(Expression, Expression),
}
impl fmt::Display for ConsoleFunction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ConsoleFunction::Assert(assert) => write!(f, "assert({})", assert),
ConsoleFunction::Error(error) => write!(f, "error{})", error),
ConsoleFunction::Log(log) => write!(f, "log({})", log),
}
}
}
impl Node for ConsoleFunction {
fn span(&self) -> Span {
match self {
ConsoleFunction::Assert(assert) => assert.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::Error(formatted) | ConsoleFunction::Log(formatted) => formatted.set_span(span),
ConsoleFunction::Assert(expr) => write!(f, "assert({})", expr),
ConsoleFunction::AssertEq(expr1, expr2) => write!(f, "assert_eq({}, {})", expr1, expr2),
ConsoleFunction::AssertNeq(expr1, expr2) => write!(f, "assert_neq({}, {})", expr1, expr2),
}
}
}

View File

@ -17,8 +17,5 @@
pub mod console_function;
pub use console_function::*;
pub mod console_args;
pub use console_args::*;
pub mod console_statement;
pub use console_statement::*;

View File

@ -78,7 +78,7 @@ impl fmt::Display for Type {
Type::Boolean => write!(f, "boolean"),
Type::Field => write!(f, "field"),
Type::Group => write!(f, "group"),
Type::Identifier(ref variable) => write!(f, "circuit {}", variable),
Type::Identifier(ref variable) => write!(f, "{}", variable),
Type::Integer(ref integer_type) => write!(f, "{}", integer_type),
Type::Scalar => write!(f, "scalar"),
Type::String => write!(f, "string"),

View File

@ -172,62 +172,53 @@ impl ParserContext<'_> {
})
}
/// Returns a [`ConsoleArgs`] AST node if the next tokens represent a formatted string.
fn parse_console_args(&mut self) -> Result<ConsoleArgs> {
let mut static_string = None;
let (parameters, _, span) = self.parse_paren_comma_list(|p| {
if static_string.is_none() {
p.bump();
let SpannedToken { token, span } = p.prev_token.clone();
match token {
Token::StaticString(string) => {
static_string = Some(StaticString::new(string));
}
_ => {
p.emit_err(ParserError::unexpected_str(token, "formatted static_string", span));
}
};
Ok(None)
} else {
p.parse_expression().map(Some)
}
})?;
Ok(ConsoleArgs {
string: static_string.unwrap_or_default(),
span,
parameters,
})
}
/// Returns a [`ConsoleStatement`] AST node if the next tokens represent a console statement.
fn parse_console_statement(&mut self) -> Result<ConsoleStatement> {
let keyword = self.expect(&Token::Console)?;
self.expect(&Token::Dot)?;
let function = self.expect_identifier()?;
let function = match function.name {
let identifier = self.expect_identifier()?;
let (span, function) = match identifier.name {
sym::assert => {
self.expect(&Token::LeftParen)?;
let expr = self.parse_expression()?;
self.expect(&Token::RightParen)?;
ConsoleFunction::Assert(expr)
(keyword + expr.span(), ConsoleFunction::Assert(expr))
}
sym::error => ConsoleFunction::Error(self.parse_console_args()?),
sym::log => ConsoleFunction::Log(self.parse_console_args()?),
x => {
sym::assert_eq => {
self.expect(&Token::LeftParen)?;
let left = self.parse_expression()?;
self.expect(&Token::Comma)?;
let right = self.parse_expression()?;
self.expect(&Token::RightParen)?;
(left.span() + right.span(), ConsoleFunction::AssertEq(left, right))
}
sym::assert_neq => {
self.expect(&Token::LeftParen)?;
let left = self.parse_expression()?;
self.expect(&Token::Comma)?;
let right = self.parse_expression()?;
self.expect(&Token::RightParen)?;
(left.span() + right.span(), ConsoleFunction::AssertNeq(left, right))
}
symbol => {
// Not sure what it is, assume it's `log`.
self.emit_err(ParserError::unexpected_ident(
x,
&["assert", "error", "log"],
function.span,
symbol,
&["assert", "assert_eq", "assert_neq"],
identifier.span,
));
ConsoleFunction::Log(self.parse_console_args()?)
(
Default::default(),
ConsoleFunction::Assert(Expression::Err(ErrExpression {
span: Default::default(),
})),
)
}
};
self.expect(&Token::Semicolon)?;
Ok(ConsoleStatement {
span: keyword + function.span(),
span: keyword + span,
function,
})
}

View File

@ -17,7 +17,7 @@
use crate::CodeGenerator;
use leo_ast::{
AssignStatement, Block, ConditionalStatement, ConsoleStatement, DefinitionStatement, Expression,
AssignStatement, Block, ConditionalStatement, ConsoleFunction, ConsoleStatement, DefinitionStatement, Expression,
IterationStatement, ParamMode, ReturnStatement, Statement,
};
@ -85,9 +85,30 @@ impl<'a> CodeGenerator<'a> {
unreachable!("`IterationStatement`s should not be in the AST at this phase of compilation.");
}
fn visit_console(&mut self, _input: &'a ConsoleStatement) -> String {
// `ConsoleStatement`s do not need to be included in the bytecode.
String::new()
fn visit_console(&mut self, input: &'a ConsoleStatement) -> String {
let mut generate_assert_instruction = |name: &str, left: &'a Expression, right: &'a Expression| {
let (left_operand, left_instructions) = self.visit_expression(left);
let (right_operand, right_instructions) = self.visit_expression(right);
let assert_instruction = format!(" {} {} {};", name, left_operand, right_operand);
// Concatenate the instructions.
let mut instructions = left_instructions;
instructions.push_str(&right_instructions);
instructions.push_str(&assert_instruction);
instructions
};
match &input.function {
ConsoleFunction::Assert(expr) => {
let (operand, mut instructions) = self.visit_expression(expr);
let assert_instruction = format!(" assert.eq {} true;", operand);
instructions.push_str(&assert_instruction);
instructions
}
ConsoleFunction::AssertEq(left, right) => generate_assert_instruction("assert.eq", left, right),
ConsoleFunction::AssertNeq(left, right) => generate_assert_instruction("assert.neq", left, right),
}
}
pub(crate) fn visit_block(&mut self, input: &'a Block) -> String {

View File

@ -193,10 +193,15 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
fn visit_console(&mut self, input: &'a ConsoleStatement) {
match &input.function {
ConsoleFunction::Assert(expr) => {
self.visit_expression(expr, &Some(Type::Boolean));
let type_ = self.visit_expression(expr, &Some(Type::Boolean));
self.assert_bool_type(&type_, expr.span());
}
ConsoleFunction::Error(_) | ConsoleFunction::Log(_) => {
// TODO: undetermined
ConsoleFunction::AssertEq(left, right) | ConsoleFunction::AssertNeq(left, right) => {
let t1 = self.visit_expression(left, &None);
let t2 = self.visit_expression(right, &None);
// Check that the types are equal.
self.check_eq_types(&t1, &t2, input.span());
}
}
}

View File

@ -106,7 +106,9 @@ impl<'a> TypeChecker<'a> {
/// Emits an error if the two given types are not equal.
pub(crate) fn check_eq_types(&self, t1: &Option<Type>, t2: &Option<Type>, span: Span) {
match (t1, t2) {
(Some(t1), Some(t2)) if t1 != t2 => self.emit_err(TypeCheckerError::type_should_be(t1, t2, span)),
(Some(t1), Some(t2)) if !Type::eq_flat(t1, t2) => {
self.emit_err(TypeCheckerError::type_should_be(t1, t2, span))
}
(Some(type_), None) | (None, Some(type_)) => {
self.emit_err(TypeCheckerError::type_should_be("no type", type_, span))
}

View File

@ -187,7 +187,6 @@ symbols! {
CoreFunction,
console,
Else: "else",
error,
For: "for",
function,
If: "if",
@ -196,7 +195,8 @@ symbols! {
input,
Let: "let",
leo,
log,
assert_eq,
assert_neq,
main,
Mut: "mut",
prelude,

View File

@ -4,8 +4,32 @@ expectation: Pass
input_file: inputs/true.in
*/
circuit Foo {
a: u8;
}
record Token {
// The token owner.
owner: address,
// The Aleo balance (in gates).
gates: u64,
// The token amount.
amount: u64,
}
@program
function main(a: bool) -> bool {
console.assert(a == true);
function main(a: bool, foo: Foo, token: Token) -> bool {
console.assert_eq(a, true);
console.assert_neq(a, false);
console.assert(a);
console.assert_eq(foo, Foo { a: 0u8 });
console.assert_neq(token, Token {
owner: aleo1lfapwg53y5enqpt0d8cnef4g8lj7l6g9uhkkma23qyv6jm4ppyfq50regr,
gates: 0u64,
amount: 0u64,
});
return a == true;
}

View File

@ -0,0 +1,14 @@
/*
namespace: Compile
expectation: Fail
input_file: inputs/true.in
*/
@program
function main(a: bool) -> bool {
console.assert_eq(a == 1u8);
console.assert(1u8);
return a == true;
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: inputs/dummy.in
*/
@program
function main(y: bool) -> bool {
console.error("hello error");
return y == true;
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: inputs/dummy.in
*/
@program
function main(y: bool) -> bool {
console.log("hello world");
return y == true;
}

View File

@ -1,17 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file:
- inputs/input_unequal.in
- inputs/input_equal.in
*/
// Conditionally add two u32 integers and log the result to the console.
@program
function main(a: u32, b: u32) -> bool {
if a == b {
console.log("{}=={}", a, b); // This line should not fail.
}
return a == b;
}

View File

@ -1,8 +0,0 @@
/*
namespace: Compile
expectation: Fail
*/
function main() {
console.log( hello );
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: inputs/dummy.in
*/
@program
function main(y: bool) -> bool {
console.log("a = {}", y);
return y == true;
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: inputs/dummy.in
*/
@program
function main(y: bool) -> bool {
console.log("{}", 1u32);
return y == true;
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Pass
input_file: inputs/dummy.in
*/
@program
function main(y: bool) -> bool {
console.log("{} {}", 1u32, true);
return y == true;
}

View File

@ -1,8 +0,0 @@
/*
namespace: Compile
expectation: Fail
*/
function main() {
console.log("{}", a);
}

View File

@ -1,11 +0,0 @@
/*
namespace: Compile
expectation: Fail
input_file: inputs/dummy.in
*/
function main(y: bool) -> bool {
let hello: string = "hello world";
console.log(hello);
return y == true;
}

View File

@ -1,6 +1,6 @@
/*
namespace: Compile
expectation: Fail
expectation: Pass
*/
circuit Amount {

View File

@ -3,7 +3,7 @@ namespace: Compile
expectation: Pass
outputs:
- output:
- initial_input_ast: 222defe193f82972f049ae44ae527ea3c1404b7eb75fca2d1eea23b28e792a8d
initial_ast: 6c65fcbd8d4c78b14b44e6c5a8a5aa207ddaaec906b6dbe4f3767bb268fcd244
unrolled_ast: 6c65fcbd8d4c78b14b44e6c5a8a5aa207ddaaec906b6dbe4f3767bb268fcd244
ssa_ast: c346c03c0dbd339428c528740ed1e8b9ed8592e9abe59c995c83089fffb9c707
- initial_input_ast: 00ae278f2e47685455a873498a580f06abfcb7bae93cc5844c2616a7da7d03db
initial_ast: c9e573f04eefae7c4769e23ef75e1b330fc728126ddfa82458f96e4677ee50a1
unrolled_ast: c9e573f04eefae7c4769e23ef75e1b330fc728126ddfa82458f96e4677ee50a1
ssa_ast: 4ee779d08beb04336cddb64d1b0e4a662d90b178754f6ffece96bd94e751686d

View File

@ -0,0 +1,5 @@
---
namespace: Compile
expectation: Fail
outputs:
- "Error [EPAR0370005]: expected , -- found ')'\n --> compiler-test:6:31\n |\n 6 | console.assert_eq(a == 1u8);\n | ^"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [EAST0372008]: function `main` shadowed by\n --> compiler-test:8:1\n |\n 8 | function main(y: bool) -> bool {\n 9 | console.log(\"{}\", 2u8);\n 10 | return y; \n 11 | }\n | ^\n"
- "Error [EPAR0370007]: unexpected identifier: expected 'assert', 'assert_eq', 'assert_neq' -- found 'log'\n --> compiler-test:4:13\n |\n 4 | console.log(\"{}\", 1u8);\n | ^^^\nError [EPAR0370005]: expected ; -- found '('\n --> compiler-test:4:16\n |\n 4 | console.log(\"{}\", 1u8);\n | ^"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [EAST0372011]: variable `a` shadowed by\n --> compiler-test:3:23\n |\n 3 | function main(a: u32, a: u32) -> u32 {\n | ^\n"
- "Error [EPAR0370007]: unexpected identifier: expected 'assert', 'assert_eq', 'assert_neq' -- found 'log'\n --> compiler-test:4:13\n |\n 4 | console.log(\"{}\", 1u8);\n | ^^^\nError [EPAR0370005]: expected ; -- found '('\n --> compiler-test:4:16\n |\n 4 | console.log(\"{}\", 1u8);\n | ^"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:4:22\n |\n 4 | function main(a: u8, foo: Foo) -> u8 {\n | ^^^\nError [ETYC0372003]: Expected type `circuit Foo` but type `u8` was found\n --> compiler-test:9:22\n |\n 9 | function returns_foo(a: u8) -> Foo {\n | ^\nError [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:9:1\n |\n 9 | function returns_foo(a: u8) -> Foo {\n 10 | return a;\n 11 | }\n | ^\n"
- "Error [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:4:22\n |\n 4 | function main(a: u8, foo: Foo) -> u8 {\n | ^^^\nError [ETYC0372003]: Expected type `Foo` but type `u8` was found\n --> compiler-test:9:22\n |\n 9 | function returns_foo(a: u8) -> Foo {\n | ^\nError [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:9:1\n |\n 9 | function returns_foo(a: u8) -> Foo {\n 10 | return a;\n 11 | }\n | ^\n"

View File

@ -1,5 +1,9 @@
---
namespace: Compile
expectation: Fail
expectation: Pass
outputs:
- "Failed to parse string. Remaining invalid string is: \"amount as circuit amount.private;\n\n\nfunction mint:\n input r0 as address.private;\n input r1 as u64.private;\n cast r1 r1 into r2 as amount;\n cast r0 0u64 r2 into r3 as token.record;\n output r3 as token.record;\n\nfunction main:\n input r0 as address.private;\n cast 1u64 1u64 into r1 as amount;\n cast r0 0u64 r1 into r2 as token.record;\n output r2.gates as u64.private;\n\n\""
- output:
- initial_input_ast: no input
initial_ast: 4c9190de88fefd0cd576a5567f42bc1ac4b4db466cbf26703d0226928ba3b593
unrolled_ast: 4c9190de88fefd0cd576a5567f42bc1ac4b4db466cbf26703d0226928ba3b593
ssa_ast: bbf5abf43a38845ceaf00571964f6313382ba7af97a42a272720003e8ed8430d

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:5:2\n |\n 5 | \tlet b: Foo = 1u8;\n | ^^^^^^^^^^^^^^^^\nError [ETYC0372003]: Expected type `circuit Foo` but type `u8` was found\n --> compiler-test:5:15\n |\n 5 | \tlet b: Foo = 1u8;\n | ^^^\n"
- "Error [ETYC0372017]: The type `Foo` is not found in the current scope.\n --> compiler-test:5:2\n |\n 5 | \tlet b: Foo = 1u8;\n | ^^^^^^^^^^^^^^^^\nError [ETYC0372003]: Expected type `Foo` but type `u8` was found\n --> compiler-test:5:15\n |\n 5 | \tlet b: Foo = 1u8;\n | ^^^\n"

View File

@ -4,80 +4,53 @@ expectation: Pass
outputs:
- Console:
function:
Assert:
Identifier: "{\"name\":\"x\",\"span\":\"{\\\"lo\\\":15,\\\"hi\\\":16}\"}"
span:
lo: 0
hi: 16
- Console:
function:
Error:
string: "{}"
parameters:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"lo\\\":20,\\\"hi\\\":21}\"}"
span:
lo: 13
hi: 22
AssertEq:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"lo\\\":18,\\\"hi\\\":19}\"}"
- Identifier: "{\"name\":\"y\",\"span\":\"{\\\"lo\\\":21,\\\"hi\\\":22}\"}"
span:
lo: 0
hi: 22
- Console:
function:
Error:
string: "{}{}"
parameters:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"lo\\\":22,\\\"hi\\\":23}\"}"
- Identifier: "{\"name\":\"y\",\"span\":\"{\\\"lo\\\":25,\\\"hi\\\":26}\"}"
span:
lo: 13
hi: 27
AssertEq:
- Circuit:
name: "{\"name\":\"Foo\",\"span\":\"{\\\"lo\\\":18,\\\"hi\\\":21}\"}"
members:
- identifier: "{\"name\":\"x\",\"span\":\"{\\\"lo\\\":24,\\\"hi\\\":25}\"}"
expression:
Identifier: "{\"name\":\"x\",\"span\":\"{\\\"lo\\\":27,\\\"hi\\\":28}\"}"
span:
lo: 18
hi: 30
- Circuit:
name: "{\"name\":\"Foo\",\"span\":\"{\\\"lo\\\":32,\\\"hi\\\":35}\"}"
members:
- identifier: "{\"name\":\"x\",\"span\":\"{\\\"lo\\\":38,\\\"hi\\\":39}\"}"
expression:
Identifier: "{\"name\":\"y\",\"span\":\"{\\\"lo\\\":41,\\\"hi\\\":42}\"}"
span:
lo: 32
hi: 44
span:
lo: 0
hi: 27
hi: 44
- Console:
function:
Error:
string: x
parameters: []
span:
lo: 13
hi: 18
AssertNeq:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"lo\\\":19,\\\"hi\\\":20}\"}"
- Identifier: "{\"name\":\"y\",\"span\":\"{\\\"lo\\\":22,\\\"hi\\\":23}\"}"
span:
lo: 0
hi: 18
hi: 23
- Console:
function:
Log:
string: "{}"
parameters:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"lo\\\":18,\\\"hi\\\":19}\"}"
span:
lo: 11
hi: 20
Assert:
Literal:
Boolean:
- false
- span:
lo: 15
hi: 20
span:
lo: 0
hi: 20
- Console:
function:
Log:
string: "{}{}"
parameters:
- Identifier: "{\"name\":\"x\",\"span\":\"{\\\"lo\\\":20,\\\"hi\\\":21}\"}"
- Identifier: "{\"name\":\"y\",\"span\":\"{\\\"lo\\\":23,\\\"hi\\\":24}\"}"
span:
lo: 11
hi: 25
span:
lo: 0
hi: 25
- Console:
function:
Log:
string: x
parameters: []
span:
lo: 11
hi: 16
span:
lo: 0
hi: 16

View File

@ -3,5 +3,11 @@ namespace: ParseStatement
expectation: Fail
outputs:
- "Error [EPAR0370021]: Unicode bidi override code point encountered."
- "Error [EPAR0370009]: unexpected string: expected 'formatted static_string', found '1'\n --> test:1:13\n |\n 1 | console.log(1);\n | ^"
- "Error [EPAR0370007]: unexpected identifier: expected 'assert', 'error', 'log' -- found 'test'\n --> test:1:9\n |\n 1 | console.test();\n | ^^^^"
- "Error [EPAR0370007]: unexpected identifier: expected 'assert', 'assert_eq', 'assert_neq' -- found 'log'\n --> test:1:9\n |\n 1 | console.log(1);\n | ^^^\nError [EPAR0370005]: expected ; -- found '('\n --> test:1:12\n |\n 1 | console.log(1);\n | ^"
- "Error [EPAR0370007]: unexpected identifier: expected 'assert', 'assert_eq', 'assert_neq' -- found 'test'\n --> test:1:9\n |\n 1 | console.test();\n | ^^^^\nError [EPAR0370005]: expected ; -- found '('\n --> test:1:13\n |\n 1 | console.test();\n | ^"
- "Error [EPAR0370007]: unexpected identifier: expected 'assert', 'assert_eq', 'assert_neq' -- found 'error'\n --> test:1:9\n |\n 1 | console.error(\"{}\", x);\n | ^^^^^\nError [EPAR0370005]: expected ; -- found '('\n --> test:1:14\n |\n 1 | console.error(\"{}\", x);\n | ^"
- "Error [EPAR0370007]: unexpected identifier: expected 'assert', 'assert_eq', 'assert_neq' -- found 'error'\n --> test:1:9\n |\n 1 | console.error(\"{}{}\", x, y);\n | ^^^^^\nError [EPAR0370005]: expected ; -- found '('\n --> test:1:14\n |\n 1 | console.error(\"{}{}\", x, y);\n | ^"
- "Error [EPAR0370007]: unexpected identifier: expected 'assert', 'assert_eq', 'assert_neq' -- found 'error'\n --> test:1:9\n |\n 1 | console.error(\"x\");\n | ^^^^^\nError [EPAR0370005]: expected ; -- found '('\n --> test:1:14\n |\n 1 | console.error(\"x\");\n | ^"
- "Error [EPAR0370007]: unexpected identifier: expected 'assert', 'assert_eq', 'assert_neq' -- found 'log'\n --> test:1:9\n |\n 1 | console.log(\"{}\", x);\n | ^^^\nError [EPAR0370005]: expected ; -- found '('\n --> test:1:12\n |\n 1 | console.log(\"{}\", x);\n | ^"
- "Error [EPAR0370007]: unexpected identifier: expected 'assert', 'assert_eq', 'assert_neq' -- found 'log'\n --> test:1:9\n |\n 1 | console.log(\"{}{}\", x, y);\n | ^^^\nError [EPAR0370005]: expected ; -- found '('\n --> test:1:12\n |\n 1 | console.log(\"{}{}\", x, y);\n | ^"
- "Error [EPAR0370007]: unexpected identifier: expected 'assert', 'assert_eq', 'assert_neq' -- found 'log'\n --> test:1:9\n |\n 1 | console.log(\"x\");\n | ^^^\nError [EPAR0370005]: expected ; -- found '('\n --> test:1:12\n |\n 1 | console.log(\"x\");\n | ^"

View File

@ -3,18 +3,10 @@ namespace: ParseStatement
expectation: Pass
*/
console.assert(x);
console.assert_eq(x, y);
console.assert_eq(Foo { x: x }, Foo { x: y });
console.error("{}", x);
console.assert_neq(x, y);
console.error("{}{}", x, y);
console.error("x");
console.log("{}", x);
console.log("{}{}", x, y);
console.log("x");
console.assert(false);

View File

@ -8,3 +8,16 @@ console.error(""); // bidi override
console.log(1);
console.test();
console.error("{}", x);
console.error("{}{}", x, y);
console.error("x");
console.log("{}", x);
console.log("{}{}", x, y);
console.log("x");