diff --git a/.github/workflows/lines-of-code.yml b/.github/workflows/lines-of-code.yml index 3f5829c0..a13760bf 100644 --- a/.github/workflows/lines-of-code.yml +++ b/.github/workflows/lines-of-code.yml @@ -23,14 +23,19 @@ jobs: - name: Run update script run: | cargo metadata --offline --format-version 1 --no-deps | jq -r ".workspace_members[]" | while read -r name version pathInfo ; do - linesOfRustCode=$(scc -c --no-cocomo -f json -i rs ${pathInfo:13:-1} | jq '.[] | .Code'); - + path=${pathInfo:13:-1} + if [[ -d "$path/src" ]] ; then + linesOfRustCode=$(scc -c --no-cocomo -f json -i rs "$path/src" | jq '.[] | .Code'); + else + linesOfRustCode=$(scc -c --no-cocomo -f json -i rs $path | jq '.[] | .Code'); + fi + curl \ --header "Content-Type: application/json" \ --header "X-POST-ACCESS-KEY: ${{ secrets.PROJECTS_POST_ACCESS_KEY }}" \ --data "{\"project\":\"$name\",\"language\":\"rust\",\"loc\":$linesOfRustCode}" \ -w "\nUpdated-project: \n" \ https://projects.kaleidawave.workers.dev/update-project; - - echo "\`$name\` has $linesOfRustCode lines of code" >> $GITHUB_STEP_SUMMARY; + + echo "\`$name\` has $linesOfRustCode lines of code"Y; done \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index baee86df..a4c7bb81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ package = "ezno-checker" [dependencies.parser] path = "./parser" -version = "0.0.8" +version = "0.1.0" features = ["extras"] package = "ezno-parser" diff --git a/checker/Cargo.toml b/checker/Cargo.toml index d2cdb7f6..7adfb64b 100644 --- a/checker/Cargo.toml +++ b/checker/Cargo.toml @@ -13,7 +13,6 @@ categories = ["compilers"] [features] default = [] -# declaration-synthesis = [] ezno-parser = ["parser"] [dependencies] @@ -45,6 +44,6 @@ erased-serde = "0.3" [dependencies.parser] path = "../parser" optional = true -version = "0.0.8" +version = "0.1.0" features = ["extras"] package = "ezno-parser" diff --git a/checker/src/synthesis/classes.rs b/checker/src/synthesis/classes.rs index c8819636..600cb1a5 100644 --- a/checker/src/synthesis/classes.rs +++ b/checker/src/synthesis/classes.rs @@ -96,14 +96,11 @@ pub(super) fn synthesise_class_declaration< // TODO abstract let (getter_setter, is_async, is_generator) = match &method.header { - Some(MethodHeader::Get(_)) => (GetterSetter::Getter, false, false), - Some(MethodHeader::Set(_)) => (GetterSetter::Setter, false, false), - None => (GetterSetter::None, false, false), - Some( - MethodHeader::Generator(is_async, _) - | MethodHeader::GeneratorStar(is_async, _), - ) => (GetterSetter::None, is_async.is_some(), true), - Some(MethodHeader::Async(_)) => (GetterSetter::None, true, false), + MethodHeader::Get(_) => (GetterSetter::Getter, false, false), + MethodHeader::Set(_) => (GetterSetter::Setter, false, false), + MethodHeader::Regular { r#async, generator } => { + (GetterSetter::None, r#async.is_some(), generator.is_some()) + } }; let method_ty = environment.new_function( @@ -187,27 +184,17 @@ pub(super) fn synthesise_class_declaration< ParserPropertyKey::Ident(_, _, true) => Publicity::Private, _ => Publicity::Public, }; - let (is_async, is_generator) = match &method.header { - None | Some(MethodHeader::Set(_)) | Some(MethodHeader::Get(_)) => { - (false, false) - } - Some( - MethodHeader::Generator(is_async, _) - | MethodHeader::GeneratorStar(is_async, _), - ) => (is_async.is_some(), true), - Some(MethodHeader::Async(_)) => (true, false), - }; let behavior = FunctionRegisterBehavior::ClassMethod { - is_async, - is_generator, + is_async: method.header.is_async(), + is_generator: method.header.is_generator(), // TODO super_type: None, }; let function = environment.new_function(checking_data, method, behavior); let value = match method.header { - Some(MethodHeader::Get(_)) => PropertyValue::Getter(Box::new(function)), - Some(MethodHeader::Set(_)) => PropertyValue::Setter(Box::new(function)), + MethodHeader::Get(_) => PropertyValue::Getter(Box::new(function)), + MethodHeader::Set(_) => PropertyValue::Setter(Box::new(function)), _ => PropertyValue::Value(checking_data.types.new_function_type(function)), }; diff --git a/checker/src/synthesis/expressions.rs b/checker/src/synthesis/expressions.rs index c9656c84..1cfa9d5d 100644 --- a/checker/src/synthesis/expressions.rs +++ b/checker/src/synthesis/expressions.rs @@ -910,7 +910,7 @@ pub(super) fn synthesise_object_literal( for member in members.iter() { let member_position = member.get_position().clone().with_source(environment.get_source()); match member { - ObjectLiteralMember::SpreadExpression(spread, pos) => { + ObjectLiteralMember::Spread(spread, pos) => { checking_data.raise_unimplemented_error( "spread in object literal", pos.clone().with_source(environment.get_source()), @@ -992,19 +992,15 @@ pub(super) fn synthesise_object_literal( ); let behavior = crate::behavior::functions::FunctionRegisterBehavior::ObjectMethod { - is_async: method.header.as_ref().map(|h| h.is_async()).unwrap_or_default(), - is_generator: method - .header - .as_ref() - .map(|h| h.is_generator()) - .unwrap_or_default(), + is_async: method.header.is_async(), + is_generator: method.header.is_generator(), }; let function = environment.new_function(checking_data, method, behavior); let property = match &method.header { - Some(MethodHeader::Get(_)) => crate::PropertyValue::Getter(Box::new(function)), - Some(MethodHeader::Set(_)) => crate::PropertyValue::Setter(Box::new(function)), + MethodHeader::Get(_) => crate::PropertyValue::Getter(Box::new(function)), + MethodHeader::Set(_) => crate::PropertyValue::Setter(Box::new(function)), _ => { crate::PropertyValue::Value(checking_data.types.new_function_type(function)) } diff --git a/checker/src/synthesis/interfaces.rs b/checker/src/synthesis/interfaces.rs index 9f813954..b6a1b1bc 100644 --- a/checker/src/synthesis/interfaces.rs +++ b/checker/src/synthesis/interfaces.rs @@ -133,7 +133,7 @@ pub(super) fn synthesise_signatures { let behavior = functions::FunctionBehavior::Method { - is_async: kind.as_ref().map_or(false, |header| header.is_async()), - is_generator: kind.as_ref().map_or(false, |header| header.is_generator()), + is_async: header.is_async(), + is_generator: header.is_generator(), // TODO ... free_this_id: TypeId::ERROR_TYPE, }; - let getter = kind.as_ref().map_or(GetterSetter::None, |header| match header { + let getter = match header { parser::MethodHeader::Get(_) => GetterSetter::Getter, parser::MethodHeader::Set(_) => GetterSetter::Setter, - parser::MethodHeader::GeneratorStar(_, _) - | parser::MethodHeader::Generator(_, _) - | parser::MethodHeader::Async(_) => GetterSetter::None, - }); + _ => GetterSetter::None, + }; let function = synthesise_function_annotation( type_parameters, parameters, @@ -212,6 +210,7 @@ pub(super) fn synthesise_signatures checking_data.raise_unimplemented_error( "interface constructor", position.clone().with_source(environment.get_source()), diff --git a/parser/Cargo.toml b/parser/Cargo.toml index d3b54f14..32d6021d 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -2,7 +2,7 @@ name = "ezno-parser" description = "Parser and AST definitions for Ezno" authors = ["Ben "] -version = "0.0.8" +version = "0.1.0" edition = "2021" license = "MIT" repository = "https://github.com/kaleidawave/ezno" diff --git a/parser/generator/Cargo.toml b/parser/generator/Cargo.toml index e71665b3..1952d430 100644 --- a/parser/generator/Cargo.toml +++ b/parser/generator/Cargo.toml @@ -17,7 +17,7 @@ proc-macro = true quote = "1.0" proc-macro2 = "1.0" self-rust-tokenize = "0.3.3" -ezno-parser = { path = "..", version = "0.0.8", features = [ +ezno-parser = { path = "..", version = "0.1.0", features = [ "self-rust-tokenize", ] } diff --git a/parser/src/declarations/classes/class_member.rs b/parser/src/declarations/classes/class_member.rs index ced80933..f03cf07f 100644 --- a/parser/src/declarations/classes/class_member.rs +++ b/parser/src/declarations/classes/class_member.rs @@ -81,7 +81,7 @@ impl ASTNode for ClassMember { .conditional_next(|tok| *tok == TSXToken::Keyword(TSXKeyword::Readonly)) .map(|token| Keyword::new(token.get_span())); - let header = MethodHeader::optional_from_reader(reader); + let header = MethodHeader::from_reader(reader); let key = WithComment::>::from_reader(reader, state, options)?; match reader.peek() { @@ -181,7 +181,7 @@ impl ClassFunction { reader: &mut impl TokenReader, state: &mut crate::ParsingState, options: &ParseOptions, - get_set_generator: Option, + get_set_generator: MethodHeader, key: WithComment>, ) -> ParseResult { FunctionBase::from_reader_with_header_and_name( @@ -196,7 +196,7 @@ impl ClassFunction { impl FunctionBased for ClassFunctionBase { type Body = Block; - type Header = Option; + type Header = MethodHeader; type Name = WithComment>; fn header_and_name_from_reader( @@ -204,7 +204,7 @@ impl FunctionBased for ClassFunctionBase { state: &mut crate::ParsingState, options: &ParseOptions, ) -> ParseResult<(Self::Header, Self::Name)> { - let header = MethodHeader::optional_from_reader(reader); + let header = MethodHeader::from_reader(reader); let name = WithComment::>::from_reader(reader, state, options)?; Ok((header, name)) } @@ -216,14 +216,12 @@ impl FunctionBased for ClassFunctionBase { options: &crate::ToStringOptions, depth: u8, ) { - if let Some(header) = header { - header.to_string_from_buffer(buf); - } + header.to_string_from_buffer(buf); name.to_string_from_buffer(buf, options, depth); } fn header_left(header: &Self::Header) -> Option { - header.as_ref().map(|header| header.get_start()) + header.get_start() } } diff --git a/parser/src/expressions/object_literal.rs b/parser/src/expressions/object_literal.rs index c10f866c..482bb062 100644 --- a/parser/src/expressions/object_literal.rs +++ b/parser/src/expressions/object_literal.rs @@ -24,7 +24,7 @@ pub struct ObjectLiteral { #[cfg_attr(feature = "self-rust-tokenize", derive(self_rust_tokenize::SelfRustTokenize))] #[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))] pub enum ObjectLiteralMember { - SpreadExpression(Expression, Span), + Spread(Expression, Span), Shorthand(String, Span), Property(WithComment>, Expression, Span), Method(ObjectLiteralMethod), @@ -39,7 +39,7 @@ impl crate::Visitable for ObjectLiteralMember { chain: &mut temporary_annex::Annex, ) { match self { - ObjectLiteralMember::SpreadExpression(_, _) => {} + ObjectLiteralMember::Spread(_, _) => {} ObjectLiteralMember::Shorthand(_, _) => {} ObjectLiteralMember::Property(_, _, _) => {} ObjectLiteralMember::Method(method) => method.visit(visitors, data, options, chain), @@ -54,7 +54,7 @@ impl crate::Visitable for ObjectLiteralMember { chain: &mut temporary_annex::Annex, ) { match self { - ObjectLiteralMember::SpreadExpression(_, _) => {} + ObjectLiteralMember::Spread(_, _) => {} ObjectLiteralMember::Shorthand(_, _) => {} ObjectLiteralMember::Property(_, _, _) => {} ObjectLiteralMember::Method(method) => method.visit_mut(visitors, data, options, chain), @@ -68,20 +68,16 @@ pub type ObjectLiteralMethod = FunctionBase; impl FunctionBased for ObjectLiteralMethodBase { type Name = WithComment>; - type Header = Option; + type Header = MethodHeader; type Body = Block; - // fn get_chain_variable(this: &FunctionBase) -> ChainVariable { - // ChainVariable::UnderClassMethod(this.body.1) - // } - fn header_and_name_from_reader( reader: &mut impl TokenReader, state: &mut crate::ParsingState, options: &ParseOptions, ) -> ParseResult<(Self::Header, Self::Name)> { Ok(( - MethodHeader::optional_from_reader(reader), + MethodHeader::from_reader(reader), WithComment::>::from_reader(reader, state, options)?, )) } @@ -93,14 +89,12 @@ impl FunctionBased for ObjectLiteralMethodBase { options: &crate::ToStringOptions, depth: u8, ) { - if let Some(ref header) = header { - header.to_string_from_buffer(buf); - } + header.to_string_from_buffer(buf); name.to_string_from_buffer(buf, options, depth); } fn header_left(header: &Self::Header) -> Option { - header.as_ref().map(|header| header.get_start()) + header.get_start() } } @@ -129,7 +123,7 @@ impl ASTNode for ObjectLiteral { buf.push('{'); options.add_gap(buf); for (at_end, member) in self.members.iter().endiate() { - member.to_string_from_buffer(buf, options, depth + 1); + member.to_string_from_buffer(buf, options, depth); if !at_end { buf.push(','); options.add_gap(buf); @@ -170,22 +164,31 @@ impl ASTNode for ObjectLiteralMember { state: &mut crate::ParsingState, options: &ParseOptions, ) -> ParseResult { + if let Some(Token(_, spread_start)) = + reader.conditional_next(|tok| matches!(tok, TSXToken::Spread)) + { + // TODO precedence okay? + let expression = Expression::from_reader(reader, state, options)?; + let position = spread_start.union(expression.get_position()); + return Ok(Self::Spread(expression, position)); + }; + // TODO this probably needs with comment here: - let mut header = MethodHeader::optional_from_reader(reader); + let mut header = MethodHeader::from_reader(reader); // Catch for named get or set :( let is_named_get_or_set = matches!( (reader.peek(), &header), ( Some(Token(TSXToken::OpenParentheses | TSXToken::Colon, _)), - Some(MethodHeader::Get(..) | MethodHeader::Set(..)) + MethodHeader::Get(..) | MethodHeader::Set(..) ) ); let key = if is_named_get_or_set { // Backtrack allowing `get` to be a key let (name, position) = match mem::take(&mut header) { - Some(MethodHeader::Get(kw)) => ("get", kw.1), - Some(MethodHeader::Set(kw)) => ("set", kw.1), + MethodHeader::Get(kw) => ("get", kw.1), + MethodHeader::Set(kw) => ("set", kw.1), _ => unreachable!(), }; WithComment::None(PropertyKey::Ident(name.to_owned(), position, ())) @@ -206,8 +209,7 @@ impl ASTNode for ObjectLiteralMember { if header.is_some() { return crate::throw_unexpected_token(reader, &[TSXToken::OpenParentheses]); } - if matches!(reader.peek(), Some(Token(TSXToken::Comma | TSXToken::CloseBrace, _))) { - // TODO fix + if let Some(Token(TSXToken::Comma | TSXToken::CloseBrace, _)) = reader.peek() { if let PropertyKey::Ident(name, position, _) = key.get_ast() { Ok(Self::Shorthand(name, position)) } else { @@ -243,7 +245,7 @@ impl ASTNode for ObjectLiteralMember { Self::Method(func) => { func.to_string_from_buffer(buf, options, depth); } - Self::SpreadExpression(spread_expr, _) => { + Self::Spread(spread_expr, _) => { buf.push_str("..."); spread_expr.to_string_from_buffer(buf, options, depth); } @@ -253,9 +255,7 @@ impl ASTNode for ObjectLiteralMember { fn get_position(&self) -> &Span { match self { Self::Method(method) => method.get_position(), - Self::Shorthand(_, pos) - | Self::Property(_, _, pos) - | Self::SpreadExpression(_, pos) => pos, + Self::Shorthand(_, pos) | Self::Property(_, _, pos) | Self::Spread(_, pos) => pos, } } } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 41c79df6..9f05ea8e 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -167,7 +167,7 @@ impl Default for ToStringOptions { expect_jsx: false, trailing_semicolon: false, expect_cursors: false, - indent_with: " ".to_owned(), + indent_with: "\t".to_owned(), } } } @@ -702,6 +702,40 @@ impl NumberRepresentation { } } +#[derive(Eq, PartialEq, Clone, Debug)] +#[cfg_attr(feature = "self-rust-tokenize", derive(self_rust_tokenize::SelfRustTokenize))] +#[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))] +pub enum GeneratorSpecifier { + Star(Span), + #[cfg(feature = "extras")] + Keyword(Keyword), +} + +impl GeneratorSpecifier { + pub(crate) fn from_reader( + reader: &mut impl TokenReader, + ) -> Option { + match reader.peek() { + Some(Token(TSXToken::Multiply, _)) => { + Some(GeneratorSpecifier::Star(reader.next().unwrap().get_span())) + } + #[cfg(feature = "extras")] + Some(Token(TSXToken::Keyword(TSXKeyword::Generator), _)) => { + Some(GeneratorSpecifier::Keyword(Keyword::new(reader.next().unwrap().get_span()))) + } + _ => None, + } + } + + fn get_start(&self) -> source_map::Start { + match self { + GeneratorSpecifier::Star(pos) => pos.get_start(), + #[cfg(feature = "extras")] + GeneratorSpecifier::Keyword(kw) => kw.get_position().get_start(), + } + } +} + /// This structure removes possible invalid combinations with async #[derive(Eq, PartialEq, Clone, Debug)] #[cfg_attr(feature = "self-rust-tokenize", derive(self_rust_tokenize::SelfRustTokenize))] @@ -709,86 +743,77 @@ impl NumberRepresentation { pub enum MethodHeader { Get(Keyword), Set(Keyword), - GeneratorStar(Option>, Span), - #[cfg(feature = "extras")] - Generator(Option>, Keyword), - Async(Keyword), + Regular { r#async: Option>, generator: Option }, +} + +impl Default for MethodHeader { + fn default() -> Self { + Self::Regular { r#async: None, generator: None } + } } impl MethodHeader { pub(crate) fn to_string_from_buffer(&self, buf: &mut T) { - buf.push_str(match self { - MethodHeader::Get(_) => "get ", - MethodHeader::Set(_) => "set ", - MethodHeader::GeneratorStar(None, _) => "*", - MethodHeader::GeneratorStar(Some(_), _) => "async *", - MethodHeader::Async(_) => "async ", - // Use default - #[cfg(feature = "extras")] - MethodHeader::Generator(None, _) => "*", - #[cfg(feature = "extras")] - MethodHeader::Generator(Some(_), _) => "async *", - }) + match self { + MethodHeader::Get(_) => buf.push_str("get "), + MethodHeader::Set(_) => buf.push_str("set "), + MethodHeader::Regular { r#async, generator } => { + if r#async.is_some() { + buf.push_str("async "); + } + if let Some(_generator) = generator { + buf.push('*'); + } + } + } } - pub(crate) fn optional_from_reader( - reader: &mut impl TokenReader, - ) -> Option { - let async_kw = reader - .conditional_next(|tok| matches!(tok, TSXToken::Keyword(TSXKeyword::Async))) - .map(|tok| Keyword::new(tok.get_span())); - + pub(crate) fn from_reader(reader: &mut impl TokenReader) -> Self { match reader.peek() { Some(Token(TSXToken::Keyword(TSXKeyword::Get), _)) => { - Some(MethodHeader::Get(Keyword::new(reader.next().unwrap().get_span()))) + MethodHeader::Get(Keyword::new(reader.next().unwrap().get_span())) } Some(Token(TSXToken::Keyword(TSXKeyword::Set), _)) => { - Some(MethodHeader::Set(Keyword::new(reader.next().unwrap().get_span()))) + MethodHeader::Set(Keyword::new(reader.next().unwrap().get_span())) } - Some(Token(TSXToken::Multiply, _)) => { - Some(MethodHeader::GeneratorStar(async_kw, reader.next().unwrap().get_span())) + _ => { + let r#async = reader + .conditional_next(|tok| matches!(tok, TSXToken::Keyword(TSXKeyword::Async))) + .map(|tok| Keyword::new(tok.get_span())); + + let generator = GeneratorSpecifier::from_reader(reader); + + MethodHeader::Regular { r#async, generator } } - #[cfg(feature = "extras")] - Some(Token(TSXToken::Keyword(TSXKeyword::Generator), _)) => Some(MethodHeader::Generator( - async_kw, - Keyword::new(reader.next().unwrap().get_span()), - )), - _ => None, } } - pub(crate) fn get_start(&self) -> source_map::Start { + pub(crate) fn get_start(&self) -> Option { match self { - MethodHeader::Get(kw) => kw.1.get_start(), - MethodHeader::Set(kw) => kw.1.get_start(), - MethodHeader::Async(kw) => kw.1.get_start(), - MethodHeader::GeneratorStar(async_kw, a) => { - async_kw.as_ref().map_or(a.get_start(), |kw| kw.1.get_start()) - } - #[cfg(feature = "extras")] - MethodHeader::Generator(async_kw, a) => { - async_kw.as_ref().map_or(a.1.get_start(), |kw| kw.1.get_start()) + MethodHeader::Get(kw) => Some(kw.1.get_start()), + MethodHeader::Set(kw) => Some(kw.1.get_start()), + MethodHeader::Regular { r#async, generator } => { + if let Some(r#async) = r#async { + Some(r#async.get_position().get_start()) + } else if let Some(generator) = generator { + Some(generator.get_start()) + } else { + None + } } } } pub fn is_async(&self) -> bool { - match self { - MethodHeader::GeneratorStar(async_kw, _) => async_kw.is_some(), - #[cfg(feature = "extras")] - MethodHeader::Generator(async_kw, _) => async_kw.is_some(), - MethodHeader::Async(_) => true, - _ => false, - } + matches!(self, Self::Regular { r#async: Some(_), .. }) } pub fn is_generator(&self) -> bool { - match self { - MethodHeader::GeneratorStar(..) => true, - #[cfg(feature = "extras")] - MethodHeader::Generator(..) => true, - _ => false, - } + matches!(self, Self::Regular { generator: Some(_), .. }) + } + + fn is_some(&self) -> bool { + !matches!(self, Self::Regular { r#async: None, generator: None }) } // pub(crate) fn get_end(&self) -> source_map::End { diff --git a/parser/src/property_key.rs b/parser/src/property_key.rs index d4f827d2..6b9e049f 100644 --- a/parser/src/property_key.rs +++ b/parser/src/property_key.rs @@ -14,12 +14,14 @@ use crate::{ // } pub trait PropertyKeyKind: Debug + PartialEq + Eq + Clone { - type Private: Debug + Sync + Send + Clone + PartialEq; + type Private: Debug + Sync + Send + Clone + Copy + PartialEq + Eq; fn parse_ident( first: Token, reader: &mut impl TokenReader, ) -> ParseResult<(String, Span, Self::Private)>; + + fn is_private(p: Self::Private) -> bool; } #[derive(Debug, Clone, PartialEq, Eq)] @@ -34,6 +36,10 @@ impl PropertyKeyKind for AlwaysPublic { ) -> ParseResult<(String, Span, Self::Private)> { token_as_identifier(first, "property key").map(|(name, position)| (name, position, ())) } + + fn is_private(_p: Self::Private) -> bool { + true + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -54,6 +60,10 @@ impl PropertyKeyKind for PublicOrPrivate { .map(|(name, position)| (name, position, false)) } } + + fn is_private(p: Self::Private) -> bool { + p + } } /// A key for a member in a class or object literal @@ -77,6 +87,13 @@ impl PropertyKey { | PropertyKey::Computed(_, pos) => pos, } } + + pub fn is_private(&self) -> bool { + match self { + PropertyKey::Ident(_, _, p) => U::is_private(*p), + _ => false, + } + } } impl PartialEq for PropertyKey { diff --git a/parser/src/types/interface.rs b/parser/src/types/interface.rs index 7d9b7201..89226086 100644 --- a/parser/src/types/interface.rs +++ b/parser/src/types/interface.rs @@ -159,7 +159,7 @@ impl ASTNode for InterfaceDeclaration { #[cfg_attr(feature = "serde-serialize", derive(serde::Serialize))] pub enum InterfaceMember { Method { - kind: Option, + header: MethodHeader, name: PropertyKey, type_parameters: Option>, parameters: TypeAnnotationFunctionParameters, @@ -193,6 +193,8 @@ pub enum InterfaceMember { type_parameters: Option>, return_type: Option, is_readonly: bool, + #[cfg(feature = "extras")] + performs: Option, position: Span, }, Caller { @@ -304,18 +306,25 @@ impl ASTNode for InterfaceMember { None }; + #[cfg(feature = "extras")] + let performs = if let Some(Token(TSXToken::Keyword(TSXKeyword::Performs), _)) = reader.peek() { + Some(super::AnnotationPerforms::from_reader(reader, state, options)?) + } else { + None + }; + + let end = + return_type.as_ref().map(ASTNode::get_position).unwrap_or(¶meters.position); + + let position = + readonly_keyword.as_ref().map_or(&new_span, |kw| kw.get_position()).union(end); + Ok(InterfaceMember::Constructor { is_readonly, - position: readonly_keyword - .as_ref() - .map_or(&new_span, |kw| kw.get_position()) - .union( - return_type - .as_ref() - .map(ASTNode::get_position) - .unwrap_or(¶meters.position), - ), + position, parameters, + #[cfg(feature = "extras")] + performs, type_parameters, return_type, }) @@ -333,7 +342,7 @@ impl ASTNode for InterfaceMember { Ok(InterfaceMember::Comment(comment, span)) } _ => { - let kind = MethodHeader::optional_from_reader(reader); + let header = MethodHeader::from_reader(reader); // TODO tidy let (name, type_parameters) = if let TSXToken::OpenBracket = @@ -504,7 +513,7 @@ impl ASTNode for InterfaceMember { }; Ok(InterfaceMember::Method { - kind, + header, name, parameters, type_parameters, @@ -545,7 +554,7 @@ impl ASTNode for InterfaceMember { }; Ok(InterfaceMember::Method { - kind, + header, name, parameters, type_parameters, diff --git a/parser/tests/expressions.rs b/parser/tests/expressions.rs index e14e02ba..ec5d7d8a 100644 --- a/parser/tests/expressions.rs +++ b/parser/tests/expressions.rs @@ -47,3 +47,23 @@ a.a(...expr, y) let output = module.to_string(&ezno_parser::ToStringOptions::typescript()); assert_eq!(output, input); } + +#[test] +fn objects() { + let input = r#" +({ a: 5 }); +({ ...b, a: 5, ...c, d: 4 }); +({ async e() { + return 2 +} }) + "# + .trim(); + + let module = + Module::from_string(input.to_owned(), Default::default(), SourceId::NULL, None).unwrap(); + + eprintln!("Module: {:#?}", module); + + let output = module.to_string(&ezno_parser::ToStringOptions::typescript()); + assert_eq!(output, input); +} diff --git a/parser/tests/statements.rs b/parser/tests/statements.rs index 1d305abe..44bd5d1a 100644 --- a/parser/tests/statements.rs +++ b/parser/tests/statements.rs @@ -31,7 +31,8 @@ try { interface X { a: string }"# - .trim_start(); + .trim_start() + .replace(" ", "\t"); let module = Module::from_string(input.to_owned(), Default::default(), SourceId::NULL, None).unwrap(); @@ -94,7 +95,8 @@ try { console.error(e) } }"# - .trim_start(); + .trim_start() + .replace(" ", "\t"); let module = Module::from_string(input.to_owned(), Default::default(), SourceId::NULL, None).unwrap(); diff --git a/src/js-based-plugin/index.mjs b/src/js-based-plugin/index.mjs index 18fb06ab..54db0ead 100644 --- a/src/js-based-plugin/index.mjs +++ b/src/js-based-plugin/index.mjs @@ -1,5 +1,5 @@ import { createUnplugin } from "unplugin"; -import { build as eznoBuild, just_imports } from "ezno/initialised"; +import { build as ezno_build, just_imports } from "ezno/initialised"; import { readFileSync } from "node:fs"; /// @@ -12,25 +12,25 @@ function emitDiagnostics(on, diagnostics, plugin) { } for (const diagnostic of diagnostics) { const line = lineSplits.findIndex(count => count >= diagnostic.position.start); - const column = diagnostic.position.start - lineSplits[line - 1] + 1; + const column = diagnostic.position.start - lineSplits[line - 1]; // Unfortunately don't get to set an end point, level or any labels - plugin.warn(diagnostic.label, { line, column }) + plugin.warn(diagnostic.reason, { line, column }) } } /** @param {import("./types").EznoUnpluginOptions} options */ -function plugin(options) { - let allJSFiles = options.allJSFiles ?? false; +function plugin(options = {}) { + let all_js_ts_files = options.all_js_ts_files ?? false; // TODO the other 50 const extensions = ["ts", "tsx", "js", "jsx"]; - const build = options.customBuild ?? eznoBuild; + const build = options.customBuild ?? ezno_build; const name = "ezno"; const esbuild = { name, - setup(build) { - build.onLoad({ filter: /\.ts(x?)$/ }, async ({ path }) => { + setup(esbuild) { + esbuild.onLoad({ filter: /\.ts(x?)$/ }, async ({ path }) => { const code = readFileSync(path, 'utf8'); try { const imports = just_imports(code); @@ -56,7 +56,7 @@ function plugin(options) { transformInclude(id) { const extension = id.split("."); const jsTsLikeExtension = extensions.includes(extension.at(-1)); - if (allJSFiles) { + if (all_js_ts_files) { return jsTsLikeExtension; } else { return jsTsLikeExtension && extension.at(-2) == "ezno"; @@ -73,14 +73,14 @@ function plugin(options) { } } - const output = build(readFile, path, false); + const output = build(path, readFile, false); if (output.Ok) { emitDiagnostics(code, output.Ok.diagnostics, this) return { code: output.Ok.outputs[0].content } } else { - emitDiagnostics(code, output.Err, this) + emitDiagnostics(code, output.Err.diagnostics, this) this.warn("ezno had errors and did not transform"); return code; } diff --git a/src/js-based-plugin/package-lock.json b/src/js-based-plugin/package-lock.json index 1137a116..4f9ce1ca 100644 --- a/src/js-based-plugin/package-lock.json +++ b/src/js-based-plugin/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.15", "license": "MIT", "dependencies": { - "ezno": "^0.0.15", + "ezno": "file:../js-cli-and-library", "unplugin": "^1.3.1" }, "devDependencies": { @@ -20,6 +20,20 @@ "url": "https://github.com/sponsors/kaleidawave" } }, + "../js-cli-and-library": { + "version": "0.0.15", + "license": "MIT", + "bin": { + "ezno": "dist/cli.mjs" + }, + "devDependencies": { + "unbuild": "^1.1.2" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/kaleidawave" + } + }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "dev": true, @@ -865,16 +879,8 @@ "license": "MIT" }, "node_modules/ezno": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/ezno/-/ezno-0.0.15.tgz", - "integrity": "sha512-VUOfnN0yGuYoZos/KxSE7lxZwDsCFalQHkvZwJFk1egDpN86QHWXTsVluGSpYiPGP46igCvtOhTQfxXL61YKRg==", - "bin": { - "ezno": "dist/cli.mjs" - }, - "funding": { - "type": "individual", - "url": "https://github.com/sponsors/kaleidawave" - } + "resolved": "../js-cli-and-library", + "link": true }, "node_modules/fast-glob": { "version": "3.2.12", diff --git a/src/js-based-plugin/package.json b/src/js-based-plugin/package.json index e6489145..7a4b52ff 100644 --- a/src/js-based-plugin/package.json +++ b/src/js-based-plugin/package.json @@ -4,12 +4,13 @@ "description": "Ezno as a plugin", "main": "./dist/index.cjs", "module": "./dist/index.mjs", - "types": "./dist/types.d.ts", + "types": "./types.d.ts", "license": "MIT", "exports": { ".": { "require": "./dist/index.cjs", - "import": "./dist/index.mjs" + "import": "./dist/index.mjs", + "types": "./types.d.ts" } }, "dependencies": { @@ -38,7 +39,8 @@ "unbuild": "^1.1.2" }, "scripts": { - "build": "unbuild" + "build": "unbuild", + "build-js": "unbuild" }, "build": { "entries": [ @@ -52,4 +54,4 @@ "commonjs": true } } -} +} \ No newline at end of file diff --git a/src/js-based-plugin/types.d.ts b/src/js-based-plugin/types.d.ts index d0230d65..eb62afe0 100644 --- a/src/js-based-plugin/types.d.ts +++ b/src/js-based-plugin/types.d.ts @@ -2,7 +2,7 @@ export type ReadFromFS = (path: string) => string | null; export interface EznoUnpluginOptions { /** Defaults to only running on .ezno.* files */ - allJSFiles: bool, + all_js_ts_files: bool, customBuild?: (cb: ReadFromFS, entryPath: string, minify: bool) => any } \ No newline at end of file diff --git a/src/js-cli-and-library/src/index.mjs b/src/js-cli-and-library/src/index.mjs index faf4ee4d..a77d24a1 100644 --- a/src/js-cli-and-library/src/index.mjs +++ b/src/js-cli-and-library/src/index.mjs @@ -1,2 +1,2 @@ -import init, { initSync, build, check, parse_expression, parse_module, just_imports, get_version } from "../build/ezno_lib.js"; -export { init, initSync, build, check, parse_expression, parse_module, just_imports, get_version }; \ No newline at end of file +import init, { initSync, build, check, parse_expression, parse_module, just_imports, minify_module, get_version } from "../build/ezno_lib.js"; +export { init, initSync, build, check, parse_expression, parse_module, just_imports, minify_module, get_version }; \ No newline at end of file diff --git a/src/js-cli-and-library/src/initialised.js b/src/js-cli-and-library/src/initialised.js index 1df10bed..a4631dc5 100644 --- a/src/js-cli-and-library/src/initialised.js +++ b/src/js-cli-and-library/src/initialised.js @@ -1,4 +1,4 @@ -import { initSync, build, check, parse_expression, parse_module, just_imports, get_version } from "./index.mjs"; +import { initSync, build, check, parse_expression, parse_module, just_imports, minify_module, get_version } from "./index.mjs"; import { readFileSync } from "node:fs"; const wasmPath = new URL("./shared/ezno_lib_bg.wasm", import.meta.url); @@ -8,4 +8,4 @@ if (wasmPath.protocol === "https:") { initSync(readFileSync(wasmPath)); } -export { build, check, parse_expression, parse_module, just_imports, get_version } \ No newline at end of file +export { build, check, parse_expression, parse_module, just_imports, minify_module, get_version } \ No newline at end of file diff --git a/src/repl.rs b/src/repl.rs index 7537499d..022677f0 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -40,6 +40,7 @@ fn file_system_resolver(path: &Path) -> Option { } } +#[allow(unused)] pub(crate) fn run_deno_repl( cli_input_resolver: U, ReplArguments { type_output, const_as_let, type_definition_module }: ReplArguments, diff --git a/src/wasm_bindings.rs b/src/wasm_bindings.rs index 19332f2f..08b6b42f 100644 --- a/src/wasm_bindings.rs +++ b/src/wasm_bindings.rs @@ -133,7 +133,7 @@ pub fn minify_module(input: String) -> JsValue { std::panic::set_hook(Box::new(console_error_panic_hook::hook)); let item = Module::from_string(input, Default::default(), SourceId::NULL, None); match item { - Ok(mut item) => { + Ok(item) => { serde_wasm_bindgen::to_value(&item.to_string(&parser::ToStringOptions::minified())) .unwrap() }