Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ build
package-lock.json
/test.ts
examples/desktop
examples/redux
105 changes: 105 additions & 0 deletions common/corpus/functions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,108 @@ class A extends B {
right: (member_expression
object: (this)
property: (property_identifier)))))))))


==================
Function signature
==================

export default function foo(): bar

---

(program
(export_statement
(function_signature
(identifier)
(formal_parameters)
(type_annotation (type_identifier)))))

=============================================================
Ambiguity between function signature and function declaration
=============================================================

function foo()
{}

function foo(bar)
function foo(bar): baz;
function foo(bar)

function foo(): () => { [key: foo]: bar }
function foo(): () => { [key: foo]: bar } {}

---

(program
(function_declaration
(identifier)
(formal_parameters)
(statement_block))
(function_signature
(identifier)
(formal_parameters (required_parameter (identifier))))
(function_signature
(identifier)
(formal_parameters (required_parameter (identifier)))
(type_annotation (type_identifier)))
(function_signature
(identifier)
(formal_parameters
(required_parameter (identifier))))
(function_signature
(identifier)
(formal_parameters)
(type_annotation
(function_type
(formal_parameters)
(object_type
(index_signature
(identifier)
(type_identifier)
(type_annotation (type_identifier)))))))
(function_declaration
(identifier)
(formal_parameters)
(type_annotation
(function_type
(formal_parameters)
(object_type
(index_signature
(identifier)
(type_identifier)
(type_annotation (type_identifier))))))
(statement_block)))

====================================================================================
Ambiguity between function signature and function declaration: comments and newlines
====================================================================================

function foo()
// above is a signature
foo();

function bar()
// above is a function declaration
{}

function foo()
: number;

---

(program
(function_signature (identifier) (formal_parameters))
(comment)
(expression_statement (call_expression (identifier) (arguments)))

(function_declaration
(identifier)
(formal_parameters)
(comment)
(statement_block))

(function_signature
(identifier)
(formal_parameters)
(type_annotation (predefined_type))))
2 changes: 1 addition & 1 deletion common/corpus/types.txt
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,7 @@ function isT(t: T): t is T {
(formal_parameters
(required_parameter
(identifier) (type_annotation (predefined_type))))
(asserts (type_predicate (identifier) (predefined_type)))
(asserts (identifier) (predefined_type))
(statement_block))
(class_declaration
(type_identifier)
Expand Down
15 changes: 10 additions & 5 deletions common/define-grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ module.exports = function defineGrammar(dialect) {
// slightly different when parsing types. Any binary-only operator would
// work.
'||',
$._function_signature_automatic_semicolon,
]),

conflicts: ($, previous) => previous.concat([
Expand Down Expand Up @@ -203,7 +204,8 @@ module.exports = function defineGrammar(dialect) {
export_statement: ($, previous) => prec(PREC.DECLARATION, choice(
previous,
seq('export', '=', $.identifier, $._semicolon),
seq('export', 'as', 'namespace', $.identifier, $._semicolon)
seq('export', 'as', 'namespace', $.identifier, $._semicolon),
seq('export', optional("default"), $.function_signature)
)),

non_null_expression: $ => prec.left(PREC.NON_NULL, seq(
Expand Down Expand Up @@ -266,7 +268,7 @@ module.exports = function defineGrammar(dialect) {
'function',
field('name', $.identifier),
$._call_signature,
$._semicolon
choice($._semicolon, $._function_signature_automatic_semicolon),
),

class_body: $ => seq(
Expand Down Expand Up @@ -506,9 +508,12 @@ module.exports = function defineGrammar(dialect) {
asserts: $ => seq(
':',
'asserts',
choice(
$.identifier,
$.type_predicate
choice($.identifier, $.this),
optional(
seq(
'is',
$._type
)
)
),

Expand Down
10 changes: 9 additions & 1 deletion common/scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ enum TokenType {
AUTOMATIC_SEMICOLON,
TEMPLATE_CHARS,
BINARY_OPERATORS,
FUNCTION_SIGNATURE_AUTOMATIC_SEMICOLON,
};

static void advance(TSLexer *lexer) { lexer->advance(lexer, false); }
Expand Down Expand Up @@ -68,7 +69,10 @@ static inline bool external_scanner_scan(void *payload, TSLexer *lexer, const bo
advance(lexer);
}
}
} else if (valid_symbols[AUTOMATIC_SEMICOLON]) {
} else if (
valid_symbols[AUTOMATIC_SEMICOLON] ||
valid_symbols[FUNCTION_SIGNATURE_AUTOMATIC_SEMICOLON]
) {
lexer->result_symbol = AUTOMATIC_SEMICOLON;
lexer->mark_end(lexer);

Expand Down Expand Up @@ -101,6 +105,10 @@ static inline bool external_scanner_scan(void *payload, TSLexer *lexer, const bo
case ':':
return false;

case '{':
if (valid_symbols[FUNCTION_SIGNATURE_AUTOMATIC_SEMICOLON]) return false;
break;

// Don't insert a semicolon before a '[' or '(', unless we're parsing
// a type. Detect whether we're parsing a type or an expression using
// the validity of a binary operator token.
Expand Down
3 changes: 3 additions & 0 deletions script/known_failures.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
examples/redux/src/types/middleware.ts
examples/redux/src/types/reducers.ts
examples/redux/src/types/store.ts
44 changes: 33 additions & 11 deletions script/parse-examples
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,50 @@
set -e

cd "$(dirname "$0")/.."
root="$PWD"

function clone_repo {
owner=$1
name=$2
sha=$3
clone_repo() {
local owner=$1
local name=$2
local sha=$3
local folder="$4"
local path="$root/examples/$name"

path=examples/$name
if [ ! -d "$path" ]; then
if [ -d "$path" ]; then
pushd "$path" > /dev/null
if [ "$(git rev-parse HEAD 2>/dev/null)" == "$sha" ]; then
popd > /dev/null
return
else
popd > /dev/null
rm -rf "$path"
echo "Updating $owner/$name to $sha"
fi
else
echo "Cloning $owner/$name"
git clone "https://github.com/$owner/$name" "$path"
fi

mkdir -p "$path"
pushd "$path" > /dev/null
if [ "$(git rev-parse head)" != "$sha" ]; then
echo "Updating $owner/$name to $sha"
git fetch
git reset --hard $sha
git init
git remote add origin "https://github.com/$owner/$name"
git pull --ff-only --depth 1 origin "$sha"
if [ "${folder:-}" ]; then
while IFS= read -r file; do
echo "${folder}" "$file"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a print debugging leftover. Not sure if you mind.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, I missed that. What is this while loop for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's for cleaning unrelated folders before the **/*.ts glob. In the TypeScript repository there's the tests folder which has some broken files on purpose, so I thought I only wanted the src folder which is more certain to have only valid syntax.

This loop goes over the files in the directory and removes everything that's not $folder - in this case I was passing src as $folder since that's the only one I'm interested in.

if [ "$file" != "$folder" ]; then
rm -r "$file"
Copy link
Contributor Author

@resolritter resolritter Feb 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be rm -rf instead of -r only because the ls -A will expose .git and other hidden files which might be write-protected. Otherwise, you'll get prompts like those:

> rm -r .git
rm: remove write-protected regular file '.git/objects/58/7ff082e0b98914788500eae5dd6a33f04883c9'? 

The intention is to remove anything unrelated to the $folder, so it makes sense to force removal with -f.

fi
done < <(ls -A "$path")
fi
popd > /dev/null
}

clone_repo desktop desktop d1324f56d02dd9afca5d2e9da545905a7d41d671
clone_repo reduxjs redux 45111a63d39a0c0fbd8b5417b2ad623718d42d66
# commented out due to too many errors
#clone_repo microsoft vscode bead496a613e475819f89f08e9e882b841bc1fe8
#clone_repo microsoft TypeScript 05e2f74fbef4a935f1e7daea9c9eb152e3956085 src

known_failures="$(cat script/known_failures.txt)"

Expand Down
73 changes: 66 additions & 7 deletions tsx/src/grammar.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,31 @@
"name": "_semicolon"
}
]
},
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "export"
},
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "default"
},
{
"type": "BLANK"
}
]
},
{
"type": "SYMBOL",
"name": "function_signature"
}
]
}
]
}
Expand Down Expand Up @@ -4924,7 +4949,7 @@
},
{
"type": "PATTERN",
"value": "[^*]*\\*+([^\\/*][^*]*\\*+)*"
"value": "[^*]*\\*+([^/*][^*]*\\*+)*"
},
{
"type": "STRING",
Expand Down Expand Up @@ -5086,7 +5111,7 @@
},
{
"type": "PATTERN",
"value": "[^\\/\\\\\\[\\n]"
"value": "[^/\\\\\\[\\n]"
}
]
}
Expand Down Expand Up @@ -5595,13 +5620,13 @@
"members": [
{
"type": "PATTERN",
"value": "[^\\x00-\\x1F\\s0-9:;`\"'@#.,|^&<=>+\\-*\\/\\\\%?!~()\\[\\]{}\\uFEFF\\u2060\\u200B\\u00A0]|\\\\u[0-9a-fA-F]{4}|\\\\u\\{[0-9a-fA-F]+\\}"
"value": "[^\\x00-\\x1F\\s0-9:;`\"'@#.,|^&<=>+\\-*/\\\\%?!~()\\[\\]{}\\uFEFF\\u2060\\u200B\\u00A0]|\\\\u[0-9a-fA-F]{4}|\\\\u\\{[0-9a-fA-F]+\\}"
},
{
"type": "REPEAT",
"content": {
"type": "PATTERN",
"value": "[^\\x00-\\x1F\\s:;`\"'@#.,|^&<=>+\\-*\\/\\\\%?!~()\\[\\]{}\\uFEFF\\u2060\\u200B\\u00A0]|\\\\u[0-9a-fA-F]{4}|\\\\u\\{[0-9a-fA-F]+\\}"
"value": "[^\\x00-\\x1F\\s:;`\"'@#.,|^&<=>+\\-*/\\\\%?!~()\\[\\]{}\\uFEFF\\u2060\\u200B\\u00A0]|\\\\u[0-9a-fA-F]{4}|\\\\u\\{[0-9a-fA-F]+\\}"
}
}
]
Expand Down Expand Up @@ -6810,8 +6835,17 @@
"name": "_call_signature"
},
{
"type": "SYMBOL",
"name": "_semicolon"
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "_semicolon"
},
{
"type": "SYMBOL",
"name": "_function_signature_automatic_semicolon"
}
]
}
]
},
Expand Down Expand Up @@ -7747,7 +7781,28 @@
},
{
"type": "SYMBOL",
"name": "type_predicate"
"name": "this"
}
]
},
{
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "is"
},
{
"type": "SYMBOL",
"name": "_type"
}
]
},
{
"type": "BLANK"
}
]
}
Expand Down Expand Up @@ -9282,6 +9337,10 @@
{
"type": "STRING",
"value": "||"
},
{
"type": "SYMBOL",
"name": "_function_signature_automatic_semicolon"
}
],
"inline": [
Expand Down
Loading