Support mixed constructors/bindings in types (#3870)

Libraries: Revert changes that were necessitated by a new rule we have decided not to introduce.

Parser:
- Support mixed constructors/bindings in types.
- Disallow zero-length hex sequences in character escapes: `\x`, `\u`, `\u{}`, `\U`, `\U{}` are no longer legal synonyms for `\0` (matches old parser behavior).
This commit is contained in:
Kaz Wesley 2022-11-14 12:24:07 -08:00 committed by GitHub
parent 85d4337f26
commit a1db36b57c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 248 additions and 247 deletions

View File

@ -19,6 +19,18 @@ from project.Data.Statistics.Statistic import all
from project.Data.Statistics.Statistic export all
type Statistic
## PRIVATE
Convert the Enso Statistic into Java equivalent.
to_moment_statistic : SingleValue
to_moment_statistic self = case self of
Sum -> Moments.SUM
Mean -> Moments.MEAN
Variance p -> if p then Moments.VARIANCE_POPULATION else Moments.VARIANCE
Standard_Deviation p -> if p then Moments.STANDARD_DEVIATION_POPULATION else Moments.STANDARD_DEVIATION
Skew p -> if p then Moments.SKEW_POPULATION else Moments.SKEW
Kurtosis -> Moments.KURTOSIS
_ -> Nothing
## Count the number of non-Nothing and non-NaN values.
Count
@ -77,18 +89,6 @@ type Statistic
- predicted: the series to compute the r_squared with.
R_Squared (predicted:Vector)
## PRIVATE
Convert the Enso Statistic into Java equivalent.
to_moment_statistic : SingleValue
to_moment_statistic self = case self of
Sum -> Moments.SUM
Mean -> Moments.MEAN
Variance p -> if p then Moments.VARIANCE_POPULATION else Moments.VARIANCE
Standard_Deviation p -> if p then Moments.STANDARD_DEVIATION_POPULATION else Moments.STANDARD_DEVIATION
Skew p -> if p then Moments.SKEW_POPULATION else Moments.SKEW
Kurtosis -> Moments.KURTOSIS
_ -> Nothing
## Compute a single statistic on a vector like object.

View File

@ -9,30 +9,6 @@ import project.Internal.IR.Order_Descriptor.Order_Descriptor
A context associated with an SQL query.
type Context
## PRIVATE
A context associated with an SQL query.
The expressions can only be computed in a context which specifies from where
their columns come and set filters and other settings for processing the
query.
Arguments:
- from_spec: the sources for the query, see `From_Spec` for more
details.
- where_filters: a list of expressions for filtering - only the rows
for which these expressions evaluate to true are included in the
result.
- orders: a list of ordering expressions, for each entry an ORDER BY
clause is added.
- groups: a list of grouping expressions, for each entry a GROUP BY is
added, the resulting query can then directly include only the
grouped-by columns or aggregate expressions.
- meta_index: a list of internal columns to use for joining or grouping.
- limit: an optional maximum number of elements that the equery should
return.
Value (from_spec : From_Spec) (where_filters : Vector SQL_Expression) (orders : Vector Order_Descriptor) (groups : Vector SQL_Expression) (meta_index : Vector Internal_Column) (limit : Nothing | Integer)
## PRIVATE
Creates a query context that just fetches data from a table, without any
@ -66,6 +42,30 @@ type Context
for_subquery subquery =
Context.Value subquery [] [] [] [] Nothing
## PRIVATE
A context associated with an SQL query.
The expressions can only be computed in a context which specifies from where
their columns come and set filters and other settings for processing the
query.
Arguments:
- from_spec: the sources for the query, see `From_Spec` for more
details.
- where_filters: a list of expressions for filtering - only the rows
for which these expressions evaluate to true are included in the
result.
- orders: a list of ordering expressions, for each entry an ORDER BY
clause is added.
- groups: a list of grouping expressions, for each entry a GROUP BY is
added, the resulting query can then directly include only the
grouped-by columns or aggregate expressions.
- meta_index: a list of internal columns to use for joining or grouping.
- limit: an optional maximum number of elements that the equery should
return.
Value (from_spec : From_Spec) (where_filters : Vector SQL_Expression) (orders : Vector Order_Descriptor) (groups : Vector SQL_Expression) (meta_index : Vector Internal_Column) (limit : Nothing | Integer)
## PRIVATE
Returns a copy of the context with changed `meta_index`.

View File

@ -13,18 +13,6 @@ polyglot java import org.opencv.core.Scalar
## UNSTABLE
type Image
## UNSTABLE
The image data type.
Arguments:
- opencv_mat: The underlying matrix that stores the image data.
The image is represented with a matrix of rows x columns. Each
pixel is represented with a vector of 1 to 4 values (channels).
Pixel values are normalized in a range [0.0 .. 1.0].
Value opencv_mat
## UNSTABLE
Create an image from the array of values.
@ -114,6 +102,18 @@ type Image
Panic.catch_java Any (Java_Codecs.write path self.opencv_mat int_flags) _->
Error.throw (File.IO_Error (File.new path) 'Failed to write to the file')
## UNSTABLE
The image data type.
Arguments:
- opencv_mat: The underlying matrix that stores the image data.
The image is represented with a matrix of rows x columns. Each
pixel is represented with a vector of 1 to 4 values (channels).
Pixel values are normalized in a range [0.0 .. 1.0].
Value opencv_mat
## UNSTABLE
Return the number of image rows.

View File

@ -15,14 +15,6 @@ polyglot java import org.enso.table.operations.OrderBuilder
from project.Data.Column.Column import Column_Data
type Column
## PRIVATE
A representation of a column in a Table.
Arguments:
- java_column: The internal representation of the column.
Column_Data java_column
## Creates a new column given a name and a vector of elements.
Arguments:
@ -39,6 +31,14 @@ type Column
from_vector : Text -> Vector -> Column
from_vector name items = Column_Data (Java_Column.fromItems name items.to_array)
## PRIVATE
A representation of a column in a Table.
Arguments:
- java_column: The internal representation of the column.
Column_Data java_column
## Returns a text containing an ASCII-art table displaying this data.
Arguments:

View File

@ -44,14 +44,6 @@ polyglot java import java.util.UUID
## Represents a column-oriented table data structure.
type Table
## PRIVATE
A table.
Arguments:
- java_table: The internal java representation of the table.
Table_Data java_table
## Creates a new table from a vector of `[name, items]` pairs.
Arguments:
@ -99,6 +91,14 @@ type Table
columns = header.map_with_index i-> name-> [name, rows.map (_.at i)]
Table.new columns
## PRIVATE
A table.
Arguments:
- java_table: The internal java representation of the table.
Table_Data java_table
## Returns a text containing an ASCII-art table displaying this data.
Arguments:

View File

@ -5,8 +5,6 @@ polyglot java import org.enso.base.Text_Utils
## Object to generate (deterministic) random value for testing
type Faker
Value generator
upper_case_letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".char_vector
lower_case_letters = "abcdefghijklmnopqrstuvwxyz".char_vector
@ -22,6 +20,8 @@ type Faker
generator = if seed == 0 then Random.new else Random.new seed
Faker.Value generator
Value generator
## Creates a random Text based on a template of character sets.
Arguments:

View File

@ -29,9 +29,6 @@ find_caller_script stack =
## Holds configuration for a Test_Suite
type Suite_Config
## PRIVATE - construct a configuration
Value only_group_regexp print_only_failures output_path
## Creates an Suite_Config based off environment and caller location
from_environment : Suite_Config
from_environment =
@ -50,6 +47,9 @@ type Suite_Config
Suite_Config.Value only_group_regexp print_only_failures results_path
## PRIVATE - construct a configuration
Value only_group_regexp print_only_failures output_path
should_run_group self name =
regexp = self.only_group_regexp
case regexp of

View File

@ -66,6 +66,7 @@ import org.enso.syntax2.Line;
import org.enso.syntax2.TextElement;
import org.enso.syntax2.Token;
import org.enso.syntax2.Tree;
import org.enso.syntax2.TypeDefStatement;
import scala.Option;
import scala.collection.immutable.LinearSeq;
@ -131,28 +132,31 @@ final class TreeToIr {
return switch (inputAst) {
case null -> appendTo;
case Tree.TypeDef def -> {
List<IR> irBody = nil();
var typeName = buildName(def.getName(), true);
var translatedBody = translateTypeBody(def.getBlock());
var irConstructors = new java.util.ArrayList<IR>();
for (var constructorLine : def.getConstructors()) {
var definition = constructorLine.getExpression();
if (definition == null) {
continue;
for (var line : def.getBody()) {
var definition = line.getStatement();
switch (definition) {
case null -> {}
case TypeDefStatement.Binding bind -> irBody = translateTypeBodyExpression(bind.getStatement(), irBody);
case TypeDefStatement.TypeConstructorDef cons -> {
if (cons.getDocumentation() != null) {
irBody = cons(translateComment(def, cons.getDocumentation()), irBody);
}
var constructorName = buildName(inputAst, cons.getConstructor());
List<IR.DefinitionArgument> args = translateArgumentsDefinition(cons.getArguments());
var cAt = getIdentifiedLocation(inputAst);
var ir = new IR$Module$Scope$Definition$Data(constructorName, args, cAt, meta(), diag());
irBody = cons(ir, irBody);
}
default -> {}
}
if (definition.getDocumentation() != null) {
irConstructors.add(translateComment(def, definition.getDocumentation()));
}
var constructorName = buildName(inputAst, definition.getConstructor());
List<IR.DefinitionArgument> args = translateArgumentsDefinition(definition.getArguments());
var cAt = getIdentifiedLocation(inputAst);
irConstructors.add(new IR$Module$Scope$Definition$Data(constructorName, args, cAt, meta(), diag()));
}
var translatedConstructors = CollectionConverters.asScala(irConstructors.iterator()).toList();
List<IR.DefinitionArgument> args = translateArgumentsDefinition(def.getParams());
var type = new IR$Module$Scope$Definition$SugaredType(
typeName,
args,
translatedConstructors.appendedAll(translatedBody),
irBody.reverse(),
getIdentifiedLocation(inputAst),
meta(), diag()
);
@ -237,19 +241,6 @@ final class TreeToIr {
return CollectionConverters.asScala(args.stream().map(p -> translateArgumentDefinition(p)).iterator()).toList();
}
/** Translates the body of a type expression.
*
* @param body the body to be translated
* @return the [[IR]] representation of `body`
*/
private List<IR> translateTypeBody(java.util.List<Line> block) {
List<IR> res = nil();
for (var line : block) {
res = translateTypeBodyExpression(line.getExpression(), res);
}
return res.reverse();
}
/** Translates any expression that can be found in the body of a type
* declaration from [[AST]] into [[IR]].
*

View File

@ -192,7 +192,13 @@ impl<'g> ToSExpr<'g> {
match primitive {
Primitive::U32 => Value::Number(read_u32(data).into()),
Primitive::I32 => Value::Number((read_u32(data) as i32).into()),
Primitive::Char => Value::Char(char::try_from(read_u32(data)).unwrap()),
Primitive::Char => {
let n = read_u32(data);
match char::try_from(n) {
Ok(c) => Value::Char(c),
Err(_) => Value::Null,
}
}
Primitive::U64 => Value::Number(read_u64(data).into()),
Primitive::I64 => Value::Number((read_u64(data) as i64).into()),
Primitive::Bool => {

View File

@ -144,17 +144,14 @@ fn doc_comments() {
#[test]
fn type_definition_no_body() {
test("type Bool", block![(TypeDef type Bool #() #() #())]);
test("type Option a", block![(TypeDef type Option #((() (Ident a) () ())) #() #())]);
test("type Option (a)", block![
(TypeDef type Option #((() (Ident a) () ())) #() #())]);
test("type Foo (a : Int)", block![
(TypeDef type Foo #((() (Ident a) (":" (Ident Int)) ())) #() #())]);
test("type A a=0", block![
(TypeDef type A #((() (Ident a) () ("=" (Number () "0" ())))) #() #())]);
test("type Existing_Headers (column_names : Vector Text)", block![
test!("type Bool", (TypeDef type Bool #() #()));
test!("type Option a", (TypeDef type Option #((() (Ident a) () ())) #()));
test!("type Option (a)", (TypeDef type Option #((() (Ident a) () ())) #()));
test!("type Foo (a : Int)", (TypeDef type Foo #((() (Ident a) (":" (Ident Int)) ())) #()));
test!("type A a=0", (TypeDef type A #((() (Ident a) () ("=" (Number () "0" ())))) #()));
test!("type Existing_Headers (column_names : Vector Text)",
(TypeDef type Existing_Headers #(
(() (Ident column_names) (":" (App (Ident Vector) (Ident Text))) ())) #() #())]);
(() (Ident column_names) (":" (App (Ident Vector) (Ident Text))) ())) #()));
}
#[test]
@ -170,26 +167,29 @@ fn type_constructors() {
#[rustfmt::skip]
let expected = block![
(TypeDef type Geo #()
#(((() Circle #() #(((() (Ident radius) () ())) ((() (Ident x) () ())))))
((() Rectangle #((() (Ident width) () ()) (() (Ident height) () ())) #()))
((() Point #() #())))
#())
];
#(((TypeConstructorDef
() Circle #() #(((() (Ident radius) () ())) ((() (Ident x) () ())))))
((TypeConstructorDef
() Rectangle #((() (Ident width) () ()) (() (Ident height) () ())) #()))
((TypeConstructorDef () Point #() #()))))];
test(&code.join("\n"), expected);
let code = "type Foo\n Bar (a : B = C.D)";
#[rustfmt::skip]
let expected = block![
(TypeDef type Foo #()
#(((() Bar #((() (Ident a) (":" (Ident B)) ("=" (OprApp (Ident C) (Ok ".") (Ident D))))) #())))
#())];
(TypeDef type Foo #() #(((TypeConstructorDef
()
Bar
#((() (Ident a) (":" (Ident B)) ("=" (OprApp (Ident C) (Ok ".") (Ident D)))))
#()))))];
test(code, expected);
let code = "type Foo\n ## Bar\n Baz";
let expected =
block![(TypeDef type Foo #() #((((#((Section " Bar")) #(())) Baz #() #()))) #())];
let expected = block![(TypeDef type Foo #() #((
(TypeConstructorDef (#((Section " Bar")) #(())) Baz #() #()))))];
test(code, expected);
let code = ["type A", " Foo (a : Integer, b : Integer)"];
#[rustfmt::skip]
let expected = block![(TypeDef type A #() #(((() Foo #((() (Invalid) () ())) #()))) #())];
let expected = block![(TypeDef type A #() #((
(TypeConstructorDef () Foo #((() (Invalid) () ())) #()))))];
test(&code.join("\n"), expected);
}
@ -198,11 +198,10 @@ fn type_methods() {
let code = ["type Geo", " number =", " x", " area self = x + x"];
#[rustfmt::skip]
let expected = block![
(TypeDef type Geo #() #()
#((Function (Ident number) #() "=" (BodyBlock #((Ident x))))
(Function (Ident area) #((() (Ident self) () ())) "="
(OprApp (Ident x) (Ok "+") (Ident x)))))
];
(TypeDef type Geo #()
#(((Binding (Function (Ident number) #() "=" (BodyBlock #((Ident x))))))
((Binding (Function (Ident area) #((() (Ident self) () ())) "="
(OprApp (Ident x) (Ok "+") (Ident x)))))))];
test(&code.join("\n"), expected);
}
@ -218,13 +217,14 @@ fn type_operator_methods() {
];
#[rustfmt::skip]
let expected = block![
(TypeDef type Foo #() #()
#((TypeSignature (Ident #"+") ":"
(OprApp (Ident Foo) (Ok "->") (OprApp (Ident Foo) (Ok "->") (Ident Foo))))
(Function (Ident #"+") #((() (Ident self) () ()) (() (Ident b) () ())) "=" (Ident b))
(TypeSignature (OprApp (Ident Foo) (Ok ".") (Ident #"+")) ":" (Ident Foo))
(Function (OprApp (Ident Foo) (Ok ".") (Ident #"+"))
#((() (Ident self) () ()) (() (Ident b) () ())) "=" (Ident b))))];
(TypeDef type Foo #()
#(((Binding (TypeSignature (Ident #"+") ":"
(OprApp (Ident Foo) (Ok "->") (OprApp (Ident Foo) (Ok "->") (Ident Foo))))))
((Binding
(Function (Ident #"+") #((() (Ident self) () ()) (() (Ident b) () ())) "=" (Ident b))))
((Binding (TypeSignature (OprApp (Ident Foo) (Ok ".") (Ident #"+")) ":" (Ident Foo))))
((Binding (Function (OprApp (Ident Foo) (Ok ".") (Ident #"+"))
#((() (Ident self) () ()) (() (Ident b) () ())) "=" (Ident b))))))];
test(&code.join("\n"), expected);
}
@ -245,15 +245,16 @@ fn type_def_full() {
#[rustfmt::skip]
let expected = block![
(TypeDef type Geo #()
#(((() Circle #() #(
#(((TypeConstructorDef () Circle #() #(
((() (Ident radius) (":" (Ident float)) ()))
((() (Ident x) () ())))))
((() Rectangle #((() (Ident width) () ()) (() (Ident height) () ())) #()))
((() Point #() #()))
(()))
#((Function (Ident number) #() "=" (BodyBlock #((Ident x))))
(Function (Ident area) #((() (Ident self) () ())) "=" (OprApp (Ident x) (Ok "+") (Ident x)))))
];
((TypeConstructorDef
() Rectangle #((() (Ident width) () ()) (() (Ident height) () ())) #()))
((TypeConstructorDef () Point #() #()))
(())
((Binding (Function (Ident number) #() "=" (BodyBlock #((Ident x))))))
((Binding (Function (Ident area) #((() (Ident self) () ())) "="
(OprApp (Ident x) (Ok "+") (Ident x)))))))];
test(&code.join("\n"), expected);
}
@ -264,9 +265,8 @@ fn type_def_defaults() {
let expected = block![
(TypeDef type Result #((() (Ident error) () ())
(() (Ident ok) () ("=" (Ident Nothing))))
#(((() Ok #((() (Ident value) (":" (Ident ok)) ("=" (Ident Nothing)))) #())))
#())
];
#(((TypeConstructorDef () Ok
#((() (Ident value) (":" (Ident ok)) ("=" (Ident Nothing)))) #()))))];
test(&code.join("\n"), expected);
}
@ -280,9 +280,9 @@ fn type_def_nested() {
];
#[rustfmt::skip]
let expected = block![
(TypeDef type Foo #() #()
#((TypeDef type Bar #() #() #())
(TypeDef type Baz #() #() #())))
(TypeDef type Foo #()
#(((Binding (TypeDef type Bar #() #())))
((Binding (TypeDef type Baz #() #())))))
];
test(&code.join("\n"), expected);
}
@ -883,29 +883,24 @@ fn type_annotations() {
#[test]
fn inline_text_literals() {
#[rustfmt::skip]
let cases = [
(r#""I'm an inline raw text!""#, block![
(TextLiteral #((Section "I'm an inline raw text!")))]),
(r#"zero_length = """#, block![
(Assignment (Ident zero_length) "=" (TextLiteral #()))]),
(r#""type""#, block![(TextLiteral #((Section "type")))]),
(r#"unclosed = ""#, block![(Assignment (Ident unclosed) "=" (TextLiteral #()))]),
(r#"unclosed = "a"#, block![
(Assignment (Ident unclosed) "=" (TextLiteral #((Section "a"))))]),
(r#"'Other quote type'"#, block![(TextLiteral #((Section "Other quote type")))]),
(r#""Non-escape: \n""#, block![(TextLiteral #((Section "Non-escape: \\n")))]),
(r#""Non-escape: \""#, block![(TextLiteral #((Section "Non-escape: \\")))]),
(r#"'String with \' escape'"#, block![
(TextLiteral
#((Section "String with ") (Escape '\'') (Section " escape")))]),
(r#"'\u0915\u094D\u0937\u093F'"#, block![(TextLiteral #(
(Escape '\u{0915}') (Escape '\u{094D}') (Escape '\u{0937}') (Escape '\u{093F}')))]),
(r#"('\n')"#, block![(Group (TextLiteral #((Escape '\n'))))]),
(r#"`"#, block![(Invalid)]),
(r#"(")")"#, block![(Group (TextLiteral #((Section ")"))))]),
];
cases.into_iter().for_each(|(code, expected)| test(code, expected));
test!(r#""I'm an inline raw text!""#, (TextLiteral #((Section "I'm an inline raw text!"))));
test!(r#"zero_length = """#, (Assignment (Ident zero_length) "=" (TextLiteral #())));
test!(r#""type""#, (TextLiteral #((Section "type"))));
test!(r#"unclosed = ""#, (Assignment (Ident unclosed) "=" (TextLiteral #())));
test!(r#"unclosed = "a"#, (Assignment (Ident unclosed) "=" (TextLiteral #((Section "a")))));
test!(r#"'Other quote type'"#, (TextLiteral #((Section "Other quote type"))));
test!(r#""Non-escape: \n""#, (TextLiteral #((Section "Non-escape: \\n"))));
test!(r#""Non-escape: \""#, (TextLiteral #((Section "Non-escape: \\"))));
test!(r#"'String with \' escape'"#,
(TextLiteral #((Section "String with ") (Escape '\'') (Section " escape"))));
test!(r#"'\u0915\u094D\u0937\u093F'"#, (TextLiteral
#((Escape '\u{0915}') (Escape '\u{094D}') (Escape '\u{0937}') (Escape '\u{093F}'))));
test!(r#"('\n')"#, (Group (TextLiteral #((Escape '\n')))));
test!(r#"`"#, (Invalid));
test!(r#"(")")"#, (Group (TextLiteral #((Section ")")))));
test!(r#"'\x'"#, (TextLiteral #((Escape ()))));
test!(r#"'\u'"#, (TextLiteral #((Escape ()))));
test!(r#"'\U'"#, (TextLiteral #((Escape ()))));
}
#[test]
@ -1238,12 +1233,8 @@ fn inline_annotations() {
#[test]
fn multiline_annotations() {
#[rustfmt::skip]
let cases = [
("@Builtin_Type\ntype Date", block![
(Annotated "@" Builtin_Type #(()) (TypeDef type Date #() #() #()))]),
];
cases.into_iter().for_each(|(code, expected)| test(code, expected));
test!("@Builtin_Type\ntype Date",
(Annotated "@" Builtin_Type #(()) (TypeDef type Date #() #())));
}

View File

@ -1091,16 +1091,15 @@ impl<'s> Lexer<'s> {
self.take_next();
expect_len = 6;
}
let mut value: u32 = 0;
let mut value: Option<u32> = None;
for _ in 0..expect_len {
if let Some(c) = self.current_char && let Some(x) = decode_hexadecimal_digit(c) {
value = 16 * value + x as u32;
value = Some(16 * value.unwrap_or_default() + x as u32);
self.take_next();
} else {
break;
}
}
let value = char::from_u32(value);
if delimited && self.current_char == Some('}') {
self.take_next();
}
@ -1108,7 +1107,7 @@ impl<'s> Lexer<'s> {
let token = self.make_token(
backslash_start,
sequence_end.clone(),
token::Variant::text_escape(value),
token::Variant::text_escape(value.and_then(char::from_u32)),
);
self.output.push(token);
sequence_end

View File

@ -313,14 +313,13 @@ fn type_def_body(matched_segments: NonEmptyVec<MatchedSegment>) -> syntax::Tree
for block::Line { newline, expression } in block::lines(block) {
builder.line(newline, expression);
}
let (constructors, body) = builder.finish();
Tree::type_def(header, name, params, constructors, body)
let body = builder.finish();
Tree::type_def(header, name, params, body)
}
#[derive(Default)]
struct TypeDefBodyBuilder<'s> {
constructors: Vec<syntax::tree::TypeConstructorLine<'s>>,
body: Vec<syntax::tree::block::Line<'s>>,
body: Vec<syntax::tree::TypeDefLine<'s>>,
documentation: Option<(syntax::token::Newline<'s>, syntax::tree::DocComment<'s>)>,
}
@ -342,58 +341,42 @@ impl<'s> TypeDefBodyBuilder<'s> {
doc.newlines.push(newline);
return;
}
if self.body.is_empty() {
if let Some(expression) = expression {
match Self::to_constructor_line(expression) {
Ok(mut expression) => {
if let Some((nl, mut doc)) = self.documentation.take() {
let nl = mem::replace(&mut newline, nl);
doc.newlines.push(nl);
expression.documentation = doc.into();
}
let expression = Some(expression);
let line = syntax::tree::TypeConstructorLine { newline, expression };
self.constructors.push(line);
let statement = expression.map(|expression| {
let mut statement = Self::to_body_statement(expression);
match &mut statement {
syntax::tree::TypeDefStatement::Constructor { constructor } => {
if let Some((nl, mut doc)) = self.documentation.take() {
let nl = mem::replace(&mut newline, nl);
doc.newlines.push(nl);
constructor.documentation = doc.into();
}
}
syntax::tree::TypeDefStatement::Binding { statement } => {
if let Some((nl, mut doc)) = self.documentation.take() {
let nl = mem::replace(&mut newline, nl);
doc.newlines.push(nl);
*statement = syntax::Tree::documented(doc, statement.clone().into());
}
Err(expression) => self.push_body(newline, expression.into()),
}
} else {
self.constructors.push(newline.into());
}
} else {
self.push_body(newline, expression);
}
statement
});
let line = syntax::tree::TypeDefLine { newline, statement };
self.body.push(line);
}
fn push_body(
&mut self,
mut newline: syntax::token::Newline<'s>,
expression: Option<syntax::Tree<'s>>,
) {
let mut expression = expression.map(crate::expression_to_statement);
if let Some((nl, mut doc)) = self.documentation.take() {
let nl = mem::replace(&mut newline, nl);
doc.newlines.push(nl);
expression = syntax::Tree::documented(doc, expression.take()).into();
}
self.body.push(syntax::tree::block::Line { newline, expression });
}
/// Return the constructor/body sequences.
pub fn finish(
mut self,
) -> (Vec<syntax::tree::TypeConstructorLine<'s>>, Vec<syntax::tree::block::Line<'s>>) {
/// Return the type body statements.
pub fn finish(self) -> Vec<syntax::tree::TypeDefLine<'s>> {
let mut body = self.body;
if let Some((newline, doc)) = self.documentation {
let expression = syntax::Tree::documented(doc, default()).into();
self.body.push(syntax::tree::block::Line { newline, expression });
let statement = syntax::Tree::documented(doc, default());
let statement = Some(syntax::tree::TypeDefStatement::Binding { statement });
body.push(syntax::tree::TypeDefLine { newline, statement });
}
(self.constructors, self.body)
body
}
/// Interpret the given expression as a `TypeConstructorDef`, if its syntax is compatible.
fn to_constructor_line(
expression: syntax::Tree<'_>,
) -> Result<syntax::tree::TypeConstructorDef<'_>, syntax::Tree<'_>> {
fn to_body_statement(expression: syntax::Tree<'_>) -> syntax::tree::TypeDefStatement<'_> {
use syntax::tree::*;
let mut last_argument_default = default();
let mut left_offset = crate::source::Offset::default();
@ -428,7 +411,9 @@ impl<'s> TypeDefBodyBuilder<'s> {
})
.collect();
let arguments = default();
return Ok(TypeConstructorDef { documentation, constructor, arguments, block });
let constructor =
TypeConstructorDef { documentation, constructor, arguments, block };
return TypeDefStatement::Constructor { constructor };
}
_ => &expression,
};
@ -443,9 +428,12 @@ impl<'s> TypeDefBodyBuilder<'s> {
*default = Some(ArgumentDefault { equals, expression });
}
let block = default();
return Ok(TypeConstructorDef{ documentation, constructor, arguments, block });
let constructor =
TypeConstructorDef { documentation, constructor, arguments, block };
return TypeDefStatement::Constructor { constructor };
}
Err(expression)
let statement = crate::expression_to_statement(expression);
TypeDefStatement::Binding { statement }
}
}

View File

@ -201,14 +201,13 @@ macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args)
/// A type definition; introduced by a line consisting of the keyword `type`, an identifier
/// to be used as the name of the type, and zero or more specifications of type parameters.
/// The following indented block contains two types of lines:
/// - First zero or more type constructors, and their subordinate blocks.
/// - Then a block of statements, which may define methods or type methods.
/// - Type constructors definitions.
/// - Bindings, defining either methods or type methods.
TypeDef {
pub keyword: token::Ident<'s>,
pub name: token::Ident<'s>,
pub params: Vec<ArgumentDefinition<'s>>,
pub constructors: Vec<TypeConstructorLine<'s>>,
pub block: Vec<block::Line<'s>>,
pub keyword: token::Ident<'s>,
pub name: token::Ident<'s>,
pub params: Vec<ArgumentDefinition<'s>>,
pub body: Vec<TypeDefLine<'s>>,
},
/// A variable assignment, like `foo = bar 23`.
Assignment {
@ -403,24 +402,49 @@ impl<'s> span::Builder<'s> for Error {
// === Type Definitions ===
/// A line within a type definition, containing a type constructor definition.
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
pub struct TypeConstructorLine<'s> {
/// The token beginning the line.
pub newline: token::Newline<'s>,
/// The type constructor definition, unless this is an empty line.
pub expression: Option<TypeConstructorDef<'s>>,
/// A line in a type definition's body block.
#[derive(Debug, Clone, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
pub struct TypeDefLine<'s> {
/// Token ending the previous line.
pub newline: token::Newline<'s>,
/// Type definition body statement, if any.
pub statement: Option<TypeDefStatement<'s>>,
}
impl<'s> span::Builder<'s> for TypeConstructorLine<'s> {
impl<'s> span::Builder<'s> for TypeDefLine<'s> {
fn add_to_span(&mut self, span: Span<'s>) -> Span<'s> {
span.add(&mut self.newline).add(&mut self.expression)
span.add(&mut self.newline).add(&mut self.statement)
}
}
impl<'s> From<token::Newline<'s>> for TypeConstructorLine<'s> {
impl<'s> From<token::Newline<'s>> for TypeDefLine<'s> {
fn from(newline: token::Newline<'s>) -> Self {
Self { newline, expression: None }
Self { newline, statement: None }
}
}
/// A statement in a type-definition body.
#[derive(Debug, Clone, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
pub enum TypeDefStatement<'s> {
/// A binding in a type-definition body.
Binding {
/// The binding statement.
statement: Tree<'s>,
},
/// A constructor definition within a type-definition body.
#[reflect(inline)]
Constructor {
/// The constructor.
constructor: TypeConstructorDef<'s>,
},
}
impl<'s> span::Builder<'s> for TypeDefStatement<'s> {
fn add_to_span(&mut self, span: Span<'s>) -> Span<'s> {
match self {
TypeDefStatement::Binding { statement } => span.add(statement),
TypeDefStatement::Constructor { constructor } => span.add(constructor),
}
}
}
@ -446,6 +470,9 @@ impl<'s> span::Builder<'s> for TypeConstructorDef<'s> {
}
}
// === Argument blocks ===
/// An argument specification on its own line.
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
pub struct ArgumentDefinitionLine<'s> {
@ -867,8 +894,7 @@ pub fn apply_operator<'s>(
},
(lhs, rhs) => {
let invalid = Tree::opr_app(lhs, opr, rhs);
let err = Error::new("`:` operator must be applied to two operands.");
Tree::invalid(err, invalid)
invalid.with_error("`:` operator must be applied to two operands.")
}
};
}