├── .github
└── workflows
│ └── pr_validation.yml
├── .gitignore
├── .run
└── JS Regex.run.xml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── README_EXAMPLE.md
├── analysis_options.yaml
├── build.yaml
├── example
└── nlp_example.dart
├── example_js_regex
├── .gitignore
├── .metadata
├── README.md
├── analysis_options.yaml
├── integration_test
│ ├── datetime
│ │ ├── date_extractor_cases.dart
│ │ ├── date_extractor_test.dart
│ │ ├── date_parser_cases.dart
│ │ ├── date_parser_test.dart
│ │ ├── date_period_extractor_cases.dart
│ │ ├── date_period_extractor_test.dart
│ │ ├── date_period_parser_cases.dart
│ │ ├── date_period_parser_test.dart
│ │ ├── date_test.dart
│ │ ├── date_time_extractor_cases.dart
│ │ ├── date_time_extractor_test.dart
│ │ ├── date_time_model_cases.dart
│ │ ├── date_time_parser_cases.dart
│ │ ├── date_time_parser_test.dart
│ │ ├── date_time_period_extractor_cases.dart
│ │ ├── date_time_period_extractor_test.dart
│ │ ├── date_time_period_parser_cases.dart
│ │ ├── date_time_period_parser_test.dart
│ │ ├── date_time_test.dart
│ │ ├── set_extractor_cases.dart
│ │ ├── set_extractor_test.dart
│ │ ├── set_parser_cases.dart
│ │ └── set_parser_test.dart
│ ├── duration
│ │ ├── duration_extractor_cases.dart
│ │ ├── duration_extractor_test.dart
│ │ ├── duration_parser_cases.dart
│ │ └── duration_parser_test.dart
│ ├── infrastructure_test
│ │ └── js_recognition_test.dart
│ └── time
│ │ ├── time_extractor_cases.dart
│ │ ├── time_extractor_test.dart
│ │ ├── time_parser_cases.dart
│ │ ├── time_period_extractor_cases.dart
│ │ ├── time_period_extractor_test.dart
│ │ ├── time_period_parser_cases.dart
│ │ ├── time_period_parser_test.dart
│ │ ├── time_zone_extractor_cases.dart
│ │ ├── time_zone_extractor_test.dart
│ │ └── time_zone_parser_cases.dart
├── lib
│ └── main.dart
├── pubspec.yaml
├── test_driver
│ └── integration_test.dart
└── web
│ ├── favicon.png
│ ├── icons
│ ├── Icon-192.png
│ ├── Icon-512.png
│ ├── Icon-maskable-192.png
│ └── Icon-maskable-512.png
│ ├── index.html
│ └── manifest.json
├── lib
├── nlp.dart
└── src
│ ├── core
│ ├── cultures.dart
│ ├── definition_loader.dart
│ ├── english_merged_extractor.dart
│ ├── extraction.dart
│ ├── global_recognizer.dart
│ ├── model_result.dart
│ ├── number_format_utility.dart
│ ├── parser.dart
│ ├── range_timex_component.dart
│ ├── string_matcher.dart
│ ├── string_utility.dart
│ ├── task_mode_processing.dart
│ └── timex_utility.dart
│ ├── date_time
│ ├── ago_later_util.dart
│ ├── base_date_extractor.dart
│ ├── base_date_parser.dart
│ ├── base_date_period_extractor.dart
│ ├── base_date_period_parser.dart
│ ├── base_date_time_period_extractor.dart
│ ├── base_date_time_period_parser.dart
│ ├── base_datetime_extractor.dart
│ ├── base_datetime_options_configuration.dart
│ ├── base_datetime_parser.dart
│ ├── base_datetime_utility_configuration.dart
│ ├── base_holiday_extractor.dart
│ ├── base_merged_date_time_extractor.dart
│ ├── base_merged_date_time_parser.dart
│ ├── base_set_extractor.dart
│ ├── base_set_parser.dart
│ ├── base_time_extractor.dart
│ ├── base_time_parser.dart
│ ├── base_time_period_extractor.dart
│ ├── base_time_period_parser.dart
│ ├── constants.dart
│ ├── data_structure.dart
│ ├── date_context.dart
│ ├── date_object_extension.dart
│ ├── date_parser_configuration.dart
│ ├── date_time_extraction.dart
│ ├── date_time_format_util.dart
│ ├── date_time_parsing.dart
│ ├── date_time_recognizer.dart
│ ├── date_util.dart
│ ├── datetime_utility_configuration.dart
│ ├── english
│ │ ├── english_date_parser_configuration.dart
│ │ ├── english_date_period_extractor_configuration.dart
│ │ ├── english_date_period_parser_configuration.dart
│ │ ├── english_date_time_extractor_configuration.dart
│ │ ├── english_date_time_parser_configuration.dart
│ │ ├── english_date_time_period_extractor_configuration.dart
│ │ ├── english_date_time_period_parser_configuration.dart
│ │ ├── english_date_time_utility_configuration.dart
│ │ ├── english_holiday_extractor_configuration.dart
│ │ ├── english_set_extractor_configuration.dart
│ │ ├── english_set_parser_configuration.dart
│ │ ├── english_time_extractor_configuration.dart
│ │ ├── english_time_parser_configuration.dart
│ │ ├── english_time_period_extractor_configuration.dart
│ │ └── english_time_period_parser_configuration.dart
│ ├── english_date_extractor.dart
│ ├── english_date_period_extraction.dart
│ ├── english_date_time.dart
│ ├── english_date_time_parser.dart
│ ├── extract_result_extension.dart
│ ├── holiday_extractor_configuration.dart
│ ├── merged_parser_util.dart
│ ├── mod_and_date_result.dart
│ ├── resolution.dart
│ ├── set_handler.dart
│ ├── task_mode_set_handler.dart
│ ├── time_of_day_resolution_result.dart
│ ├── time_period_functions.dart
│ ├── timex_property.dart
│ ├── timex_regex.dart
│ ├── timex_resolver.dart
│ └── timezone_utility.dart
│ ├── duration
│ ├── base_duration_parser.dart
│ ├── duration.dart
│ ├── duration_extractor.dart
│ └── english_duration_parser_configuration.dart
│ ├── numbers
│ ├── base_number_parser.dart
│ ├── english_number_parser_configuration.dart
│ ├── number_constants.dart
│ ├── number_with_unit_tokenizer.dart
│ └── numbers.dart
│ ├── regular_expressions
│ ├── js_regexp.dart
│ ├── matching_util.dart
│ ├── regular_expressions_extensions.dart
│ └── string_extensions.dart
│ └── time
│ ├── english_time_zone.dart
│ ├── english_time_zone_extractor.dart
│ └── time_zone_extractor.dart
├── pubspec.yaml
└── test
├── cases
├── english
│ ├── LICENSE
│ ├── date_time.json
│ └── date_time_test.dart
├── test_case_parsing_test.dart
├── test_case_serialization.dart
└── test_case_serialization.g.dart
└── infrastructure
└── regular_expression_composition_test.dart
/.github/workflows/pr_validation.yml:
--------------------------------------------------------------------------------
1 | name: Run tests for pull requests
2 | on: [pull_request]
3 | jobs:
4 | test:
5 | runs-on: ubuntu-latest
6 | steps:
7 | # Checkout the PR branch
8 | - uses: actions/checkout@v3
9 |
10 | # Setup chromedriver for integration tests
11 | - uses: nanasess/setup-chromedriver@2
12 |
13 | # Setup Flutter environment
14 | - uses: subosito/flutter-action@v2
15 | with:
16 | channel: "stable"
17 |
18 | # Download all the packages that the app uses
19 | - run: flutter pub get
20 |
21 | # Run all tests
22 | - working-directory: ./example_js_regex
23 | run: flutter drive --driver=test_driver/integration_test.dart --target=integration_test/duration/duration_parser_test.dart -d chrome
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | # https://dart.dev/guides/libraries/private-files
4 | # Created by `dart pub`
5 | .dart_tool/
6 |
7 | # Avoid committing pubspec.lock for library packages; see
8 | # https://dart.dev/guides/libraries/private-files#pubspeclock.
9 | pubspec.lock
10 |
11 | # IntelliJ related
12 | *.iml
13 | *.ipr
14 | *.iws
15 | .idea/
--------------------------------------------------------------------------------
/.run/JS Regex.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.0.0 - Oct, 2023
2 | Placeholder version. MVP is under construction.
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Declarative, Inc. All rights reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Natural Language Processing (NLP) for Dart
2 | Natural language processing (NLP) identifies substrings within text, which represent a useful piece
3 | of information, and then associates the substring with that information.
4 |
5 | For example, NLP might receive text that says "I'll do it tomorrow". NLP identifies "tomorrow" as
6 | a meaningful term, and NLP is aware that "tomorrow" means the day after today. As a result, NLP
7 | associates "tomorrow" with a Dart `DateTime` that points to the day after today.
8 |
9 | NLP can be implemented for any such association. For example, NLP might be implemented to recognize
10 | that a number word, like "two", represents an `int`, like `2`. NLP is limited only by the data
11 | models and text recognizers, which are used to inspect the meaning of text.
--------------------------------------------------------------------------------
/README_EXAMPLE.md:
--------------------------------------------------------------------------------
1 | # How recognizers work
2 |
3 | ## Operations
4 | ### Regex Composition
5 | Most regular expressions are built from other regular expressions in a composition process.
6 |
7 | When one regular expression is embedded in another, it creates the possibility that the composed
8 | regex contains multiple group names that are the same. But regex doesn't support repeated group
9 | names. Therefore, each regex pattern has its group names de-duplicated by adding suffixes to those
10 | names.
11 |
12 | Example:
13 | ```java
14 | // Regex for a duration followed by a duration unit
15 | public static final String DurationFollowedUnit = "(^\\s*{DurationUnitRegex}\\s+{SuffixAndRegex})|(^\\s*{SuffixAndRegex}?(\\s+|-)?{DurationUnitRegex})"
16 | .replace("{SuffixAndRegex}", SuffixAndRegex)
17 | .replace("{DurationUnitRegex}", DurationUnitRegex);
18 |
19 | // Regex for suffixes
20 | public static final String SuffixAndRegex = "(?\\s*(and)\\s+(an?\\s+)?(?half|quarter))";
21 |
22 | // Regex for duration units
23 | public static final String DurationUnitRegex = "(?{DateUnitRegex}|h(ou)?rs?|h|min(ute)?s?|sec(ond)?s?|nights?)\\b"
24 | .replace("{DateUnitRegex}", DateUnitRegex);
25 |
26 | // Date unit regex
27 | public static final String DateUnitRegex = "(?(decade|year|(?month|week)|(?(business\\s+|week\\s*))?(?day)|fortnight|weekend)(?s)?|(?<=(^|\\s)\\d{1,4})[ymwd])\\b";
28 | ```
29 |
30 | Duration followed by a unit composed regex:
31 | ```
32 | (^\s*(?(?(decade|year|(?month|week)|(?(business\s+|week\s*))?(?day)|fortnight|weekend)(?s)?|(?(^|\s)\d{1,4})[ymwd])\b|h(ou)?rs?|h|min(ute)?s?|sec(ond)?s?|nights?)\b\s+(?\s*(and)\s+(an?\s+)?(?half|quarter)))|(^\s*(?\s*(and)\s+(an?\s+)?(?half|quarter))?(\s+|-)?(?(?(decade|year|(?month|week)|(?(business\s+|week\s*))?(?day)|fortnight|weekend)(?s)?|(?(^|\s)\d{1,4})[ymwd])\b|h(ou)?rs?|h|min(ute)?s?|sec(ond)?s?|nights?)\b)
33 | ```
34 |
35 | Notice that the fully composed regex contains multiple group names for "unit", "uoy", "suffix", etc.
36 |
37 | These duplicated group names are altered during regex composition so that every group name is
38 | unique. The group names have a separator appended ("iii") and then they have an incrementing number
39 | appended ("unitiii0", "unitiii1").
40 |
41 | ### Extraction
42 | The extraction process identifies pieces of text that represent something of meaning, such as a
43 | number, duration, date, time, etc. These pieces of text are extracted from the input text, along
44 | with their position data within the input text.
45 |
46 | Given the source: `it happened when the baby was only ten months old`
47 |
48 | Using regex:
49 | ```
50 | (^\s*(?(?(decade|year|(?month|week)|(?(business\s+|week\s*))?(?day)|fortnight|weekend)(?s)?|(?(^|\s)\d{1,4})[ymwd])\b|h(ou)?rs?|h|min(ute)?s?|sec(ond)?s?|nights?)\b\s+(?\s*(and)\s+(an?\s+)?(?half|quarter)))|(^\s*(?\s*(and)\s+(an?\s+)?(?half|quarter))?(\s+|-)?(?(?(decade|year|(?month|week)|(?(business\s+|week\s*))?(?day)|fortnight|weekend)(?s)?|(?(^|\s)\d{1,4})[ymwd])\b|h(ou)?rs?|h|min(ute)?s?|sec(ond)?s?|nights?)\b)
51 | ```
52 |
53 | Duration Extraction:
54 | ```
55 | Text: "ten months"
56 | Start: 35
57 | Length: 45
58 | ```
59 |
60 | ### Re-ifying group names
61 | During regex composition, group names are altered for the purpose of de-duplication. However, the
62 | original group names are important - the group names represent the domain meaning of what they
63 | capture. For example "unitiii1" needs to recover "unit" so that the tokenization and parsing
64 | processes understand the meaning of the associated text.
65 |
66 | The Recognizers package implements `getMatches()` in `RegExpUtility` to recover the original group
67 | names and return matches with those names.
68 |
69 | ### Tokenization
70 | The extraction process produces `ExtractResult`s. These `ExtractResult`s are then converted into
71 | `Token`s. These tokens are then merged and otherwise processed. Then, the `Token`s are converted
72 | back into `ExtractResult`s and returned from the extraction process.
73 |
74 | ### Parsing
75 | Given a set of extractions, the parsing process attaches meaning to the extracted text.
76 |
77 | Given the source: `it happened when the baby was only ten months old`
78 |
79 | With Extraction:
80 | ```
81 | Text: "ten months"
82 | Start: 35
83 | Length: 45
84 | ```
85 |
86 | Parses the following:
87 | ```
88 | text: ten months
89 | type: duration
90 | start: 35
91 | length: 10
92 | data: null
93 | timex: P10M
94 | resolution string:
95 | date time resolution result:
96 | - timex: P10M
97 | - mod: null
98 | - comment: null
99 | - past value: 2.592E7
100 | ```
101 |
102 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the static analysis results for your project (errors,
2 | # warnings, and lints).
3 | #
4 | # This enables the 'recommended' set of lints from `package:lints`.
5 | # This set helps identify many issues that may lead to problems when running
6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic
7 | # style and format.
8 | #
9 | # If you want a smaller set of lints you can change this to specify
10 | # 'package:lints/core.yaml'. These are just the most critical lints
11 | # (the recommended set includes the core lints).
12 | # The core lints are also what is used by pub.dev for scoring packages.
13 |
14 | include: package:lints/recommended.yaml
15 |
16 | # Uncomment the following section to specify additional rules.
17 |
18 | # linter:
19 | # rules:
20 | # - camel_case_types
21 |
22 | # analyzer:
23 | # exclude:
24 | # - path/to/excluded/files/**
25 |
26 | # For more information about the core and recommended set of lints, see
27 | # https://dart.dev/go/core-lints
28 |
29 | # For additional information about configuring this file, see
30 | # https://dart.dev/guides/language/analysis-options
31 |
--------------------------------------------------------------------------------
/build.yaml:
--------------------------------------------------------------------------------
1 | targets:
2 | $default:
3 | builders:
4 | json_serializable:
5 | options:
6 | # Options configure how source code is generated for every
7 | # `@JsonSerializable`-annotated class in the package.
8 | #
9 | # The default value for each is listed.
10 | any_map: false
11 | checked: true
12 | constructor: ""
13 | create_factory: true
14 | create_field_map: false
15 | create_per_field_to_json: false
16 | create_to_json: false
17 | disallow_unrecognized_keys: true
18 | explicit_to_json: false
19 | field_rename: none
20 | generic_argument_factories: false
21 | ignore_unannotated: false
22 | include_if_null: true
--------------------------------------------------------------------------------
/example/nlp_example.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: non_constant_identifier_names, constant_identifier_names
2 |
3 | import 'dart:convert';
4 |
5 | import 'package:mason_logger/mason_logger.dart';
6 | import 'package:nlp/nlp.dart';
7 |
8 | final logger = Logger();
9 |
10 | void main() {
11 | _printIntro();
12 |
13 | do {
14 | final textToRecognize = logger.prompt("Enter the text to recognize:");
15 | if (textToRecognize.trim().toLowerCase() == "exit") {
16 | logger.info("Thanks for trying the example app! Goodbye.");
17 | logger.info("");
18 | return;
19 | }
20 | if (textToRecognize.trim().isEmpty) {
21 | logger.info("You didn't enter any text to recognize!");
22 | logger.info("");
23 | continue;
24 | }
25 |
26 | // Datetime recognizer This model will find any Date even if its write in colloquial language
27 | // E.g "I'll go back 8pm today" will return "2017-10-04 20:00:00"
28 | final results = DateTimeRecognizer.recognizeDateTime(textToRecognize);
29 |
30 | logger.info("I found the following entities (${results.length})");
31 | for (final result in results) {
32 | logger.info("Type: ${result.typeName}");
33 | JsonEncoder.withIndent(" ").convert({
34 | "parentText": result.parentText,
35 | "text": result.text,
36 | "start": result.start,
37 | "end": result.end,
38 | "typeName": result.typeName,
39 | "resolution": result.resolution,
40 | });
41 | }
42 |
43 | logger.info("");
44 | } while (true);
45 | }
46 |
47 | void _printIntro() {
48 | logger.info("Welcome to the example app for the nlp package!");
49 | logger.info("");
50 | logger.info("This example app is based on the Microsoft Recognizers project.");
51 | logger.info("");
52 | logger.info(
53 | "To try the recognizers enter a phrase and let us show you the different outputs for each recognizer or just type 'exit' to leave the application.");
54 | logger.info("");
55 | logger.info("Here are some examples you could try:");
56 | logger.info("");
57 | logger.info("\"I want twenty meters of cable for tomorrow\"");
58 | logger.info("\"I'll be available tomorrow from 11am to 2pm to receive up to 5kg of sugar\"");
59 | logger.info("\"I'll be out between 4 and 22 this month\"");
60 | logger.info("\"I was the fifth person to finish the 10 km race\"");
61 | logger.info("\"The temperature this night will be of 40 deg celsius\"");
62 | logger
63 | .info("\"The american stock exchange said a seat was sold for down \$5,000 from the previous sale last friday\"");
64 | logger.info("\"It happened when the baby was only ten months old\"");
65 | logger.info("");
66 | }
67 |
--------------------------------------------------------------------------------
/example_js_regex/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Symbolication related
35 | app.*.symbols
36 |
37 | # Obfuscation related
38 | app.*.map.json
39 |
40 | # Android Studio will place build artifacts here
41 | /android/app/debug
42 | /android/app/profile
43 | /android/app/release
44 |
--------------------------------------------------------------------------------
/example_js_regex/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: "92094802fecd9aa4c71492d6c0ea7e9eed82498e"
8 | channel: "master"
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 92094802fecd9aa4c71492d6c0ea7e9eed82498e
17 | base_revision: 92094802fecd9aa4c71492d6c0ea7e9eed82498e
18 | - platform: web
19 | create_revision: 92094802fecd9aa4c71492d6c0ea7e9eed82498e
20 | base_revision: 92094802fecd9aa4c71492d6c0ea7e9eed82498e
21 |
22 | # User provided section
23 |
24 | # List of Local paths (relative to this file) that should be
25 | # ignored by the migrate tool.
26 | #
27 | # Files that are not part of the templates will be ignored by default.
28 | unmanaged_files:
29 | - 'lib/main.dart'
30 | - 'ios/Runner.xcodeproj/project.pbxproj'
31 |
--------------------------------------------------------------------------------
/example_js_regex/README.md:
--------------------------------------------------------------------------------
1 | # Flutter project to run RegEx with JS support
2 |
3 | Dart lacks support for reporting the indices of named capture groups, i.e., we can't get
4 | the location in the original string where a named capture group was found. But we need this
5 | RegEx behavior.
6 |
7 | JavaScript supports the reporting of indices for named capture groups.
8 |
9 | This Flutter project exists to bring JavaScript named group indices into Dart. This is a
10 | temporary approach because we'll eventually need cross-platform support. We'll prove our
11 | ability to implement the desired behavior using JS and then find some way to port that
12 | behavior to Dart for the final product.
13 |
14 | ## Integration Tests
15 | Because we depend upon JavaScript RegExp behavior, we can't run traditional Dart language
16 | tests, or even Flutter widget tests. We have to run web integration tests so that we can
17 | access JavaScript RegEx capabilities.
18 |
19 | To run the integration tests in this project, first start chromedriver:
20 |
21 | ```shell
22 | chromedriver --port=4444
23 | ```
24 |
25 | Then, run the desired integration tests:
26 |
27 | ```shell
28 | flutter drive --driver=test_driver/integration_test.dart --target=integration_test/time/time_zone_extractor_test.dart -d chrome
29 | ```
30 |
31 |
--------------------------------------------------------------------------------
/example_js_regex/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at https://dart.dev/lints.
17 | #
18 | # Instead of disabling a lint rule for the entire project in the
19 | # section below, it can also be suppressed for a single line of code
20 | # or a specific dart file by using the `// ignore: name_of_lint` and
21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
22 | # producing the lint.
23 | rules:
24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
26 |
27 | # Additional information about this file can be found at
28 | # https://dart.dev/guides/language/analysis-options
29 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/date_extractor_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'date_extractor_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Date > extractor >', () {
11 | for (final testCase in dateExtractorTestCases) {
12 | final input = testCase["Input"] as String;
13 | testWidgets(input, (tester) async {
14 | final extractor = BaseDateExtractor(
15 | EnglishDateExtractorConfiguration(
16 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
17 | ),
18 | );
19 |
20 | DateTime? referenceDate;
21 |
22 | final context = testCase["Context"] as Map?;
23 |
24 | if (context != null) {
25 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
26 | }
27 |
28 | final extractions = extractor.extractDateTime(input, referenceDate ?? DateTime.now());
29 |
30 | final actualResults = extractions.map((extraction) => extraction.toTestCaseJson()).toList();
31 | final expectedResults = testCase["Results"] as List;
32 |
33 | expect(actualResults, expectedResults);
34 | });
35 | }
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/date_parser_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'date_parser_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Date > parser >', () {
11 | print('executing ${dateParserTestCases.length} test cases');
12 |
13 | for (final testCase in dateParserTestCases) {
14 | final input = testCase["Input"] as String;
15 | testWidgets(input, (tester) async {
16 | final extractor = BaseDateExtractor(
17 | EnglishDateExtractorConfiguration(
18 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
19 | ),
20 | );
21 |
22 | final parser = BaseDateParser(
23 | EnglishDateParserConfiguration(
24 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
25 | ),
26 | );
27 |
28 | DateTime? referenceDate;
29 |
30 | final context = testCase["Context"] as Map?;
31 |
32 | if (context != null) {
33 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
34 | }
35 |
36 | referenceDate ??= DateTime.now();
37 |
38 | final extractions = extractor.extractDateTime(input, referenceDate);
39 |
40 | final actualResults =
41 | extractions.map((extraction) => parser.parseDateTime(extraction, referenceDate!).toTestCaseJson()).toList();
42 | final expectedResults = testCase["Results"] as List;
43 |
44 | expect(actualResults, expectedResults);
45 | });
46 | }
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/date_period_extractor_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'date_period_extractor_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Date period > extractor >', () {
11 | for (final testCase in datePeriodExtractorTestCases) {
12 | final input = testCase["Input"] as String;
13 | testWidgets(input, (tester) async {
14 | final extractor = BaseDatePeriodExtractor(
15 | EnglishDatePeriodExtractorConfiguration(
16 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
17 | ),
18 | );
19 |
20 | DateTime? referenceDate;
21 |
22 | final context = testCase["Context"] as Map?;
23 |
24 | if (context != null) {
25 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
26 | }
27 |
28 | final extractions = extractor.extractDateTime(input, referenceDate ?? DateTime.now());
29 |
30 | final actualResults = extractions.map((extraction) => extraction.toTestCaseJson()).toList();
31 | final expectedResults = testCase["Results"] as List;
32 |
33 | expect(actualResults, expectedResults);
34 | });
35 | }
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/date_period_parser_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'date_period_parser_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Date period > parser >', () {
11 | print('executing ${datePeriodParserTestCases.length} test cases');
12 |
13 | for (final testCase in datePeriodParserTestCases) {
14 | final input = testCase["Input"] as String;
15 | testWidgets(input, (tester) async {
16 | final extractor = BaseDatePeriodExtractor(
17 | EnglishDatePeriodExtractorConfiguration(
18 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
19 | ),
20 | );
21 |
22 | final parser = BaseDatePeriodParser(
23 | EnglishDatePeriodParserConfiguration(
24 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
25 | ),
26 | );
27 |
28 | DateTime? referenceDate;
29 |
30 | final context = testCase["Context"] as Map?;
31 |
32 | if (context != null) {
33 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
34 | }
35 |
36 | referenceDate ??= DateTime.now();
37 |
38 | final extractions = extractor.extractDateTime(input, referenceDate);
39 |
40 | final actualResults =
41 | extractions.map((extraction) => parser.parseDateTime(extraction, referenceDate!).toTestCaseJson()).toList();
42 | final expectedResults = testCase["Results"] as List;
43 |
44 | expect(actualResults, expectedResults);
45 | });
46 | }
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/date_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | void main() {
6 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
7 |
8 | group('Recognition > date >', () {
9 | testWidgets('04th Jan 2019', (widgetTester) async {
10 | final matches = DateTimeRecognizer.recognizeDateTime(
11 | "I'll go back 04th Jan 2019",
12 | );
13 |
14 | expect(matches.length, 1);
15 | expect(
16 | matches.first.toJson(),
17 | {
18 | "parentText": null,
19 | "text": "04th jan 2019",
20 | "start": 13,
21 | "end": 25,
22 | "typeName": "datetimeV2.date",
23 | "resolution": {
24 | "values": [
25 | {
26 | "timex": "2019-01-04",
27 | "type": "date",
28 | "value": "2019-01-04",
29 | },
30 | ],
31 | },
32 | },
33 | );
34 | });
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/date_time_extractor_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'date_time_extractor_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('DateTime > extractor >', () {
11 | for (final testCase in dateTimeExtractorTestCases) {
12 | final input = testCase["Input"] as String;
13 | testWidgets(input, (tester) async {
14 | final extractor = BaseDateTimeExtractor(
15 | EnglishDateTimeExtractorConfiguration(
16 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
17 | ),
18 | );
19 |
20 | DateTime? referenceDate;
21 |
22 | final context = testCase["Context"] as Map?;
23 |
24 | if (context != null) {
25 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
26 | }
27 |
28 | final extractions = extractor.extractDateTime(input, referenceDate ?? DateTime.now());
29 |
30 | final actualResults = extractions.map((extraction) => extraction.toTestCaseJson()).toList();
31 | final expectedResults = testCase["Results"] as List;
32 |
33 | expect(actualResults, expectedResults);
34 | });
35 | }
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/date_time_parser_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'date_time_parser_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Datetime > parser >', () {
11 | print('executing ${dateTimeParserTestCases.length} test cases');
12 |
13 | for (final testCase in dateTimeParserTestCases) {
14 | final input = testCase["Input"] as String;
15 | testWidgets(input, (tester) async {
16 | final extractor = BaseDateTimeExtractor(
17 | EnglishDateTimeExtractorConfiguration(
18 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
19 | ),
20 | );
21 |
22 | final parser = BaseDateTimeParser(
23 | EnglishDateTimeParserConfiguration(
24 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
25 | ),
26 | );
27 |
28 | DateTime? referenceDate;
29 |
30 | final context = testCase["Context"] as Map?;
31 |
32 | if (context != null) {
33 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
34 | }
35 |
36 | referenceDate ??= DateTime.now();
37 |
38 | final extractions = extractor.extractDateTime(input, referenceDate);
39 |
40 | final actualResults =
41 | extractions.map((extraction) => parser.parseDateTime(extraction, referenceDate!).toTestCaseJson()).toList();
42 | final expectedResults = testCase["Results"] as List;
43 |
44 | expect(actualResults, expectedResults);
45 | });
46 | }
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/date_time_period_extractor_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'date_time_period_extractor_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Datetime period > extractor >', () {
11 | for (final testCase in dateTimePeriodExtractorTestCases) {
12 | final input = testCase["Input"] as String;
13 | testWidgets(input, (tester) async {
14 | final extractor = BaseDateTimePeriodExtractor(
15 | EnglishDateTimePeriodExtractorConfiguration(
16 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
17 | ),
18 | );
19 |
20 | DateTime? referenceDate;
21 |
22 | final context = testCase["Context"] as Map?;
23 |
24 | if (context != null) {
25 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
26 | }
27 |
28 | final extractions = extractor.extractDateTime(input, referenceDate ?? DateTime.now());
29 |
30 | final actualResults = extractions.map((extraction) => extraction.toTestCaseJson()).toList();
31 | final expectedResults = testCase["Results"] as List;
32 |
33 | expect(actualResults, expectedResults);
34 | });
35 | }
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/date_time_period_parser_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'date_time_period_parser_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Datetime period > parser >', () {
11 | print('executing ${dateTimePeriodParserTestCases.length} test cases');
12 |
13 | for (final testCase in dateTimePeriodParserTestCases) {
14 | final input = testCase["Input"] as String;
15 | testWidgets(input, (tester) async {
16 | final extractor = BaseDateTimePeriodExtractor(
17 | EnglishDateTimePeriodExtractorConfiguration(
18 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
19 | ),
20 | );
21 |
22 | final parser = BaseDateTimePeriodParser(
23 | EnglishDateTimePeriodParserConfiguration(
24 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
25 | ),
26 | );
27 |
28 | DateTime? referenceDate;
29 |
30 | final context = testCase["Context"] as Map?;
31 |
32 | if (context != null) {
33 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
34 | }
35 |
36 | referenceDate ??= DateTime.now();
37 |
38 | final extractions = extractor.extractDateTime(input, referenceDate);
39 |
40 | final actualResults =
41 | extractions.map((extraction) => parser.parseDateTime(extraction, referenceDate!).toTestCaseJson()).toList();
42 | final expectedResults = testCase["Results"] as List;
43 |
44 | expect(actualResults, expectedResults);
45 | });
46 | }
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/set_extractor_cases.dart:
--------------------------------------------------------------------------------
1 | const setExtractorTestCases = [
2 | {
3 | "Input": "I'll leave weekly",
4 | "Results": [
5 | {"Text": "weekly", "Type": "set", "Start": 11, "Length": 6}
6 | ]
7 | },
8 | {
9 | "Input": "I'll leave daily",
10 | "Results": [
11 | {"Text": "daily", "Type": "set", "Start": 11, "Length": 5}
12 | ]
13 | },
14 | {
15 | "Input": "I'll leave every day",
16 | "Results": [
17 | {"Text": "every day", "Type": "set", "Start": 11, "Length": 9}
18 | ]
19 | },
20 | {
21 | "Input": "I'll leave each month",
22 | "Results": [
23 | {"Text": "each month", "Type": "set", "Start": 11, "Length": 10}
24 | ]
25 | },
26 | {
27 | "Input": "I'll leave annually",
28 | "Results": [
29 | {"Text": "annually", "Type": "set", "Start": 11, "Length": 8}
30 | ]
31 | },
32 | {
33 | "Input": "I'll leave annual",
34 | "Results": [
35 | {"Text": "annual", "Type": "set", "Start": 11, "Length": 6}
36 | ]
37 | },
38 | {
39 | "Input": "I'll leave each two days",
40 | "Results": [
41 | {"Text": "each two days", "Type": "set", "Start": 11, "Length": 13}
42 | ]
43 | },
44 | {
45 | "Input": "I'll leave every three week",
46 | "Results": [
47 | {"Text": "every three week", "Type": "set", "Start": 11, "Length": 16}
48 | ]
49 | },
50 | {
51 | "Input": "I'll leave 3pm every day",
52 | "Results": [
53 | {"Text": "3pm every day", "Type": "set", "Start": 11, "Length": 13}
54 | ]
55 | },
56 | {
57 | "Input": "I'll leave 3pm each day",
58 | "Results": [
59 | {"Text": "3pm each day", "Type": "set", "Start": 11, "Length": 12}
60 | ]
61 | },
62 | {
63 | "Input": "I'll leave each 4/15",
64 | "Results": [
65 | {"Text": "each 4/15", "Type": "set", "Start": 11, "Length": 9}
66 | ]
67 | },
68 | {
69 | "Input": "I'll leave every monday",
70 | "Results": [
71 | {"Text": "every monday", "Type": "set", "Start": 11, "Length": 12}
72 | ]
73 | },
74 | {
75 | "Input": "I'll leave each monday 4pm",
76 | "Results": [
77 | {"Text": "each monday 4pm", "Type": "set", "Start": 11, "Length": 15}
78 | ]
79 | },
80 | {
81 | "Input": "I'll leave every morning",
82 | "Results": [
83 | {"Text": "every morning", "Type": "set", "Start": 11, "Length": 13}
84 | ]
85 | },
86 | {
87 | "Input": "I'll leave every morning at 9am",
88 | "Results": [
89 | {"Text": "every morning at 9am", "Type": "set", "Start": 11, "Length": 20}
90 | ]
91 | },
92 | {
93 | "Input": "I'll leave every afternoon at 4pm",
94 | "Results": [
95 | {"Text": "every afternoon at 4pm", "Type": "set", "Start": 11, "Length": 22}
96 | ]
97 | },
98 | {
99 | "Input": "I'll leave every night at 9pm",
100 | "Results": [
101 | {"Text": "every night at 9pm", "Type": "set", "Start": 11, "Length": 18}
102 | ]
103 | },
104 | {
105 | "Input": "I'll leave every night at 9",
106 | "Results": [
107 | {"Text": "every night at 9", "Type": "set", "Start": 11, "Length": 16}
108 | ]
109 | },
110 | {
111 | "Input": "I'll leave mornings at 9am",
112 | "Results": [
113 | {"Text": "mornings at 9am", "Type": "set", "Start": 11, "Length": 15}
114 | ]
115 | },
116 | {
117 | "Input": "I'll leave on mornings at 9",
118 | "Results": [
119 | {"Text": "on mornings at 9", "Type": "set", "Start": 11, "Length": 16}
120 | ]
121 | },
122 | {
123 | "Input": "I'll leave at 9am every Sunday",
124 | "Results": [
125 | {"Text": "9am every sunday", "Type": "set", "Start": 14, "Length": 16}
126 | ]
127 | },
128 | {
129 | "Input": "I'll leave at 9am on mondays",
130 | "Results": [
131 | {"Text": "9am on mondays", "Type": "set", "Start": 14, "Length": 14}
132 | ]
133 | },
134 | {
135 | "Input": "I'll leave at 9am mondays",
136 | "Results": [
137 | {"Text": "9am mondays", "Type": "set", "Start": 14, "Length": 11}
138 | ]
139 | },
140 | {
141 | "Input": "I'll leave on mondays",
142 | "Results": [
143 | {"Text": "on mondays", "Type": "set", "Start": 11, "Length": 10}
144 | ]
145 | },
146 | {
147 | "Input": "I'll leave on sundays",
148 | "Results": [
149 | {"Text": "on sundays", "Type": "set", "Start": 11, "Length": 10}
150 | ]
151 | },
152 | {
153 | "Input": "I'll leave sundays",
154 | "Results": [
155 | {"Text": "sundays", "Type": "set", "Start": 11, "Length": 7}
156 | ]
157 | },
158 | {
159 | "Input": "Can I do a booking for the 09th of May for 2 nights?",
160 | "Results": [
161 | {"Text": "nights", "Type": "set", "Start": 45, "Length": 6}
162 | ]
163 | },
164 | {
165 | "Input": "Let's meet once a week",
166 | "Context": {"ReferenceDateTime": "2016-11-07T00:00:00"},
167 | "Results": [
168 | {"Text": "once a week", "Type": "set", "Start": 11, "Length": 11}
169 | ]
170 | },
171 | {
172 | "Input": "I go on vacation once a year",
173 | "Context": {"ReferenceDateTime": "2016-11-07T00:00:00"},
174 | "Results": [
175 | {"Text": "once a year", "Type": "set", "Start": 17, "Length": 11}
176 | ]
177 | },
178 | {
179 | "Input": "Every other friday",
180 | "Results": [
181 | {"Text": "every other friday", "Type": "set", "Start": 0, "Length": 18}
182 | ]
183 | },
184 | {
185 | "Input": "Let's have a quarterly meeting.",
186 | "Results": [
187 | {"Text": "quarterly", "Type": "set", "Start": 13, "Length": 9}
188 | ]
189 | }
190 | ];
191 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/set_extractor_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'set_extractor_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Set > extractor >', () {
11 | for (final testCase in setExtractorTestCases) {
12 | final input = testCase["Input"] as String;
13 | testWidgets(input, (tester) async {
14 | final extractor = BaseSetExtractor(
15 | EnglishSetExtractorConfiguration(
16 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
17 | ),
18 | );
19 |
20 | DateTime? referenceDate;
21 |
22 | final context = testCase["Context"] as Map?;
23 |
24 | if (context != null) {
25 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
26 | }
27 |
28 | final extractions = extractor.extractDateTime(input, referenceDate ?? DateTime.now());
29 |
30 | final actualResults = extractions.map((extraction) => extraction.toTestCaseJson()).toList();
31 | final expectedResults = testCase["Results"] as List;
32 |
33 | expect(actualResults, expectedResults);
34 | });
35 | }
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/datetime/set_parser_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'set_parser_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Datetime > parser >', () {
11 | print('executing ${setParserTestCases.length} test cases');
12 |
13 | for (final testCase in setParserTestCases) {
14 | final input = testCase["Input"] as String;
15 | testWidgets(input, (tester) async {
16 | final extractor = BaseSetExtractor(
17 | EnglishSetExtractorConfiguration(
18 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
19 | ),
20 | );
21 |
22 | final parser = BaseSetParser(
23 | EnglishSetParserConfiguration(
24 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
25 | ),
26 | );
27 |
28 | DateTime? referenceDate;
29 |
30 | final context = testCase["Context"] as Map?;
31 |
32 | if (context != null) {
33 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
34 | }
35 |
36 | referenceDate ??= DateTime.now();
37 |
38 | final extractions = extractor.extractDateTime(input, referenceDate);
39 |
40 | final actualResults =
41 | extractions.map((extraction) => parser.parseDateTime(extraction, referenceDate!).toTestCaseJson()).toList();
42 | final expectedResults = testCase["Results"] as List;
43 |
44 | expect(actualResults, expectedResults);
45 | });
46 | }
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/duration/duration_extractor_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'duration_extractor_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Duration > extractor >', () {
11 | for (final testCase in durationExtractorTestCases) {
12 | final input = testCase["Input"] as String;
13 |
14 | testWidgets(input, (widgetTester) async {
15 | final extractions = DurationExtractor(config: EnglishDurationExtractorConfiguration()).extract(input);
16 | final actualResults = extractions.map((extraction) => extraction.toTestCaseJson()).toList();
17 | final expectedResults = testCase["Results"] as List;
18 |
19 | expect(actualResults, expectedResults);
20 | });
21 | }
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/duration/duration_parser_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:intl/intl.dart';
4 | import 'package:nlp/nlp.dart';
5 |
6 | import 'duration_parser_cases.dart';
7 |
8 | void main() {
9 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
10 |
11 | final dateTimeFormat = DateFormat("yyyy-MM-ddThh:mm:ss");
12 |
13 | group('Duration > parser >', () {
14 | for (final testCase in durationParserTestCases) {
15 | final input = testCase["Input"] as String;
16 |
17 | final referenceDateTimeString = (testCase["Context"] as Map?)?["ReferenceDateTime"];
18 | final referenceDateTime =
19 | referenceDateTimeString != null ? dateTimeFormat.parse(referenceDateTimeString) : DateTime.now();
20 |
21 | testWidgets(input, (widgetTester) async {
22 | final extractions = DurationExtractor(config: EnglishDurationExtractorConfiguration()).extract(input);
23 |
24 | final parser = BaseDurationParser(
25 | EnglishDurationParserConfiguration(
26 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
27 | ),
28 | );
29 | final parseResults = [];
30 | for (final extraction in extractions) {
31 | parseResults.add(parser.parseDateTime(extraction, referenceDateTime));
32 | }
33 |
34 | final actualResults = parseResults.map((parseResult) => parseResult.toTestCaseJson()).toList();
35 | final expectedResults = testCase["Results"] as List;
36 |
37 | expect(actualResults, expectedResults);
38 | });
39 | }
40 | });
41 | }
42 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/infrastructure_test/js_recognition_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | void main() {
6 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
7 |
8 | group('JS regular expressions >', () {
9 | testWidgets('reports capture group names', (widgetTester) async {
10 | final dartResult = JsRegExp(
11 | '^(?\\d+),(?.+)\$',
12 | d: true,
13 | ).exec("1560979912,Caroline");
14 |
15 | expect(dartResult!.matchingGroupNames, ['timestamp', 'author']);
16 | });
17 |
18 | testWidgets('reports capture group indices', (widgetTester) async {
19 | final dartResult = JsRegExp(
20 | '^(?\\d+),(?.+)\$',
21 | d: true,
22 | ).exec("1560979912,Caroline");
23 |
24 | expect(dartResult!.groups['timestamp'], '1560979912');
25 | expect(dartResult.groups['author'], 'Caroline');
26 | expect(dartResult.indices, [(0, 19), (0, 10), (11, 19)]);
27 | });
28 |
29 | testWidgets('reports capture group indices by name', (widgetTester) async {
30 | final dartResult = JsRegExp(
31 | '^(?\\d+),(?.+)\$',
32 | d: true,
33 | ).exec("1560979912,Caroline");
34 |
35 | expect(dartResult!.getGroupBounds('timestamp'), (0, 10));
36 | expect(dartResult.getGroupBounds('author'), (11, 19));
37 | });
38 | });
39 | }
40 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/time/time_extractor_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 |
4 | import 'time_extractor_cases.dart';
5 |
6 | void main() {
7 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
8 |
9 | group('Time > extractor >', () {
10 | for (final testCase in timeExtractorTestCases) {
11 | final input = testCase["Input"] as String;
12 |
13 | // TODO:
14 | // testWidgets(input, (widgetTester) async {
15 | // final extractions = TimeExtractor(config: EnglishDurationExtractorConfiguration()).extract(input);
16 | // final actualResults = extractions.map((extraction) => extraction.toTestCaseJson()).toList();
17 | // final expectedResults = testCase["Results"] as List;
18 | //
19 | // expect(actualResults, expectedResults);
20 | // });
21 | }
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/time/time_period_extractor_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'time_period_extractor_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Time period > extractor >', () {
11 | for (final testCase in timePeriodExtractorTestCases) {
12 | final input = testCase["Input"] as String;
13 | testWidgets(input, (tester) async {
14 | final extractor = BaseTimePeriodExtractor(
15 | EnglishTimePeriodExtractorConfiguration(
16 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
17 | ),
18 | );
19 |
20 | DateTime? referenceDate;
21 |
22 | final context = testCase["Context"] as Map?;
23 |
24 | if (context != null) {
25 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
26 | }
27 |
28 | final extractions = extractor.extractDateTime(input, referenceDate ?? DateTime.now());
29 |
30 | final actualResults = extractions.map((extraction) => extraction.toTestCaseJson()).toList();
31 | final expectedResults = testCase["Results"] as List;
32 |
33 | expect(actualResults, expectedResults);
34 | });
35 | }
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/time/time_period_parser_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'time_period_parser_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Time period > parser >', () {
11 | print('executing ${timePeriodParserTestCases.length} test cases');
12 |
13 | for (final testCase in timePeriodParserTestCases) {
14 | final input = testCase["Input"] as String;
15 | testWidgets(input, (tester) async {
16 | final extractor = BaseTimePeriodExtractor(
17 | EnglishTimePeriodExtractorConfiguration(
18 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
19 | ),
20 | );
21 |
22 | final parser = BaseTimePeriodParser(
23 | EnglishTimePeriodParserConfiguration(
24 | EnglishCommonDateTimeParserConfiguration(DateTimeOptions.None),
25 | ),
26 | );
27 |
28 | DateTime? referenceDate;
29 |
30 | final context = testCase["Context"] as Map?;
31 |
32 | if (context != null) {
33 | referenceDate = DateTime.parse(context["ReferenceDateTime"]);
34 | }
35 |
36 | referenceDate ??= DateTime.now();
37 |
38 | final extractions = extractor.extractDateTime(input, referenceDate);
39 |
40 | final actualResults =
41 | extractions.map((extraction) => parser.parseDateTime(extraction, referenceDate!).toTestCaseJson()).toList();
42 | final expectedResults = testCase["Results"] as List;
43 |
44 | expect(actualResults, expectedResults);
45 | });
46 | }
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/time/time_zone_extractor_cases.dart:
--------------------------------------------------------------------------------
1 | final timeZoneExtractorTestCases = [
2 | {
3 | "Input": "Book me a room at beijing time",
4 | "NotSupported": "javascript",
5 | "Results": [
6 | {"Text": "beijing time", "Type": "timezone", "Start": 18, "Length": 12}
7 | ]
8 | },
9 | {
10 | "Input": "Book me a room at utc4:30",
11 | "NotSupported": "javascript",
12 | "Results": [
13 | {"Text": "utc4:30", "Type": "timezone", "Start": 18, "Length": 7}
14 | ]
15 | },
16 | {
17 | "Input": "Book me a room at gmt-3",
18 | "NotSupported": "javascript",
19 | "Results": [
20 | {"Text": "gmt-3", "Type": "timezone", "Start": 18, "Length": 5}
21 | ]
22 | },
23 | {
24 | "Input": "Book me a room at afghanistan standard time",
25 | "NotSupported": "javascript",
26 | "Results": [
27 | {"Text": "afghanistan standard time", "Type": "timezone", "Start": 18, "Length": 25}
28 | ]
29 | },
30 | {
31 | "Input": "Book me a room at aft",
32 | "NotSupported": "javascript",
33 | "Results": [
34 | {"Text": "aft", "Type": "timezone", "Start": 18, "Length": 3}
35 | ]
36 | },
37 | {
38 | "Input": "Book me a room at beijing-time",
39 | "NotSupported": "javascript",
40 | "Results": [
41 | {"Text": "beijing-time", "Type": "timezone", "Start": 18, "Length": 12}
42 | ]
43 | },
44 | {
45 | "Input": "Book me a room at St. Louis-time",
46 | "NotSupported": "javascript",
47 | "Results": [
48 | {"Text": "St. Louis-time", "Type": "timezone", "Start": 18, "Length": 14}
49 | ]
50 | },
51 | {
52 | "Input": "Book me a room at San José Time",
53 | "NotSupported": "javascript",
54 | "Results": [
55 | {"Text": "San José Time", "Type": "timezone", "Start": 18, "Length": 13}
56 | ]
57 | },
58 | {
59 | "Input": "For me, Christchurch Time, Colchester-time or Edinburgh time is OK.",
60 | "NotSupported": "javascript",
61 | "Results": [
62 | {"Text": "Christchurch Time", "Type": "timezone", "Start": 8, "Length": 17},
63 | {"Text": "Colchester-time", "Type": "timezone", "Start": 27, "Length": 15},
64 | {"Text": "Edinburgh time", "Type": "timezone", "Start": 46, "Length": 14}
65 | ]
66 | },
67 | {
68 | "Input": "Cortana will email you to find a time which works in the Sydney timezone.",
69 | "NotSupported": "javascript",
70 | "Results": [
71 | {"Text": "Sydney timezone", "Type": "timezone", "Start": 57, "Length": 15}
72 | ]
73 | },
74 | {
75 | "Input": "Cortana will email you to find a time which works in the Montréal time.",
76 | "NotSupported": "javascript",
77 | "Results": [
78 | {"Text": "Montréal time", "Type": "timezone", "Start": 57, "Length": 13}
79 | ]
80 | },
81 | {
82 | "Input": "Cortana will email you to find a time which works in the Montreal time.",
83 | "NotSupported": "javascript",
84 | "Results": [
85 | {"Text": "Montreal time", "Type": "timezone", "Start": 57, "Length": 13}
86 | ]
87 | },
88 | {
89 | "Input": "Book me a room at pt",
90 | "NotSupported": "javascript",
91 | "Results": [
92 | {"Text": "pt", "Type": "timezone", "Start": 18, "Length": 2}
93 | ]
94 | },
95 | {
96 | "Input": "Book me a room at et",
97 | "NotSupported": "javascript",
98 | "Results": [
99 | {"Text": "et", "Type": "timezone", "Start": 18, "Length": 2}
100 | ]
101 | },
102 | {
103 | "Input": "let's meet Saint Barthélemy time",
104 | "NotSupported": "javascript",
105 | "Results": [
106 | {"Text": "Saint Barthélemy time", "Type": "timezone", "Start": 11, "Length": 21}
107 | ]
108 | },
109 | {
110 | "Input": "let's meet saint barthelemy timezone",
111 | "NotSupported": "javascript",
112 | "Results": [
113 | {"Text": "saint barthelemy timezone", "Type": "timezone", "Start": 11, "Length": 25}
114 | ]
115 | },
116 | {
117 | "Input": "It is the outcome of the vote that counts.",
118 | "Comment": "This case is to verify that the utc substring won't be extracted from the word outcome.",
119 | "NotSupported": "javascript",
120 | "Results": []
121 | },
122 | {
123 | "Input": "Show me times at Lincoln Square",
124 | "Comment": "This case is to verify that the 'me time' substring won't be extracted from the 'me times'.",
125 | "NotSupported": "javascript",
126 | "Results": []
127 | },
128 | {
129 | "Input": "I said New York time, not York time",
130 | "Comment": "Extract longest item when there are some overlap items",
131 | "NotSupported": "javascript",
132 | "Results": [
133 | {"Text": "New York time", "Type": "timezone", "Start": 7, "Length": 13},
134 | {"Text": "York time", "Type": "timezone", "Start": 26, "Length": 9}
135 | ]
136 | },
137 | {
138 | "Input": "I'm in the pacific timezone",
139 | "NotSupported": "javascript",
140 | "Results": [
141 | {"Text": "pacific timezone", "Type": "timezone", "Start": 11, "Length": 16}
142 | ]
143 | },
144 | {
145 | "Input": "Let's meet at 1pm mountain timezone",
146 | "NotSupported": "javascript",
147 | "Results": [
148 | {"Text": "mountain timezone", "Type": "timezone", "Start": 18, "Length": 17}
149 | ]
150 | },
151 | {
152 | "Input": "It's 1pm Eastern Daylight Time",
153 | "NotSupported": "javascript",
154 | "Results": [
155 | {"Text": "eastern daylight time", "Type": "timezone", "Start": 9, "Length": 21}
156 | ]
157 | },
158 | {
159 | "Input": "It's about 1pm ACDT",
160 | "NotSupported": "javascript",
161 | "Results": [
162 | {"Text": "acdt", "Type": "timezone", "Start": 15, "Length": 4}
163 | ]
164 | },
165 | {"Input": "Once upon a time...", "NotSupported": "javascript", "Results": []}
166 | ];
167 |
--------------------------------------------------------------------------------
/example_js_regex/integration_test/time/time_zone_extractor_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 | import 'package:integration_test/integration_test.dart';
3 | import 'package:nlp/nlp.dart';
4 |
5 | import 'time_zone_extractor_cases.dart';
6 |
7 | void main() {
8 | IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9 |
10 | group('Time zone > extractor >', () {
11 | for (final testCase in timeZoneExtractorTestCases.sublist(0, 1)) {
12 | final input = testCase["Input"] as String;
13 |
14 | testWidgets(input, (widgetTester) async {
15 | final extractions = BaseTimeZoneExtractor(EnglishTimeZoneExtractorConfiguration()).extract(input);
16 | final actualResults = extractions.map((extraction) => extraction.toTestCaseJson()).toList();
17 | final expectedResults = testCase["Results"] as List;
18 |
19 | expect(actualResults, expectedResults);
20 | });
21 | }
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/example_js_regex/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: example_js_regex
2 | description: "A new Flutter project."
3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
4 |
5 | version: 1.0.0+1
6 |
7 | environment:
8 | sdk: '>=3.0.0 <4.0.0'
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 |
14 | nlp:
15 | path: ../
16 |
17 | collection: ^1.18.0
18 | intl: ^0.19.0
19 | super_editor:
20 | git:
21 | url: https://github.com/superlistapp/super_editor
22 | path: super_editor
23 | ref: stable
24 |
25 | dependency_overrides:
26 | super_text_layout:
27 | git:
28 | url: https://github.com/superlistapp/super_editor
29 | path: super_text_layout
30 | ref: stable
31 | attributed_text:
32 | git:
33 | url: https://github.com/superlistapp/super_editor
34 | path: attributed_text
35 | ref: stable
36 |
37 | dev_dependencies:
38 | flutter_test:
39 | sdk: flutter
40 | integration_test:
41 | sdk: flutter
42 |
43 | flutter_lints: ^3.0.0
44 |
45 | flutter:
46 | uses-material-design: true
47 |
48 | # To add assets to your application, add an assets section, like this:
49 | # assets:
50 | # - images/a_dot_burr.jpeg
51 | # - images/a_dot_ham.jpeg
52 |
53 | # An image asset can refer to one or more resolution-specific "variants", see
54 | # https://flutter.dev/assets-and-images/#resolution-aware
55 |
56 | # For details regarding adding assets from package dependencies, see
57 | # https://flutter.dev/assets-and-images/#from-packages
58 |
59 | # To add custom fonts to your application, add a fonts section here,
60 | # in this "flutter" section. Each entry in this list should have a
61 | # "family" key with the font family name, and a "fonts" key with a
62 | # list giving the asset and other descriptors for the font. For
63 | # example:
64 | # fonts:
65 | # - family: Schyler
66 | # fonts:
67 | # - asset: fonts/Schyler-Regular.ttf
68 | # - asset: fonts/Schyler-Italic.ttf
69 | # style: italic
70 | # - family: Trajan Pro
71 | # fonts:
72 | # - asset: fonts/TrajanPro.ttf
73 | # - asset: fonts/TrajanPro_Bold.ttf
74 | # weight: 700
75 | #
76 | # For details regarding fonts from package dependencies,
77 | # see https://flutter.dev/custom-fonts/#from-packages
78 |
--------------------------------------------------------------------------------
/example_js_regex/test_driver/integration_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:integration_test/integration_test_driver_extended.dart';
2 |
3 | Future main() => integrationDriver();
4 |
--------------------------------------------------------------------------------
/example_js_regex/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flutter-Bounty-Hunters/nlp/d28f812c0ee1d67ce900b9ebe399f58d11418bd6/example_js_regex/web/favicon.png
--------------------------------------------------------------------------------
/example_js_regex/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flutter-Bounty-Hunters/nlp/d28f812c0ee1d67ce900b9ebe399f58d11418bd6/example_js_regex/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/example_js_regex/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flutter-Bounty-Hunters/nlp/d28f812c0ee1d67ce900b9ebe399f58d11418bd6/example_js_regex/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/example_js_regex/web/icons/Icon-maskable-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flutter-Bounty-Hunters/nlp/d28f812c0ee1d67ce900b9ebe399f58d11418bd6/example_js_regex/web/icons/Icon-maskable-192.png
--------------------------------------------------------------------------------
/example_js_regex/web/icons/Icon-maskable-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Flutter-Bounty-Hunters/nlp/d28f812c0ee1d67ce900b9ebe399f58d11418bd6/example_js_regex/web/icons/Icon-maskable-512.png
--------------------------------------------------------------------------------
/example_js_regex/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | example_js_regex
33 |
34 |
35 |
39 |
40 |
41 |
42 |
43 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/example_js_regex/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example_js_regex",
3 | "short_name": "example_js_regex",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | },
22 | {
23 | "src": "icons/Icon-maskable-192.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "icons/Icon-maskable-512.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "maskable"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/lib/nlp.dart:
--------------------------------------------------------------------------------
1 | library;
2 |
3 | // NLP Domain
4 | export 'src/date_time/constants.dart';
5 | export 'src/date_time/english_date_time.dart';
6 | export 'src/date_time/english_date_time_parser.dart';
7 | export 'src/date_time/date_time_recognizer.dart';
8 | export 'src/date_time/base_date_extractor.dart';
9 | export 'src/date_time/english_date_extractor.dart';
10 | export 'src/date_time/base_date_parser.dart';
11 | export 'src/date_time/english/english_date_parser_configuration.dart';
12 | export 'src/date_time/base_datetime_extractor.dart';
13 | export 'src/date_time/english/english_date_time_extractor_configuration.dart';
14 | export 'src/date_time/base_time_parser.dart';
15 | export 'src/date_time/base_datetime_parser.dart';
16 | export 'src/date_time/english/english_date_time_parser_configuration.dart';
17 | export 'src/date_time/base_time_period_extractor.dart';
18 | export 'src/date_time/english/english_time_period_extractor_configuration.dart';
19 | export 'src/date_time/base_time_period_parser.dart';
20 | export 'src/date_time/english/english_time_period_parser_configuration.dart';
21 |
22 | export 'src/date_time/base_date_period_extractor.dart';
23 | export 'src/date_time/english/english_date_period_extractor_configuration.dart';
24 |
25 | export 'src/date_time/base_date_time_period_extractor.dart';
26 | export 'src/date_time/english/english_date_time_period_extractor_configuration.dart';
27 | export 'src/date_time/base_date_period_parser.dart';
28 | export 'src/date_time/english/english_date_period_parser_configuration.dart';
29 |
30 | export 'src/date_time/base_date_time_period_parser.dart';
31 | export 'src/date_time/english/english_date_time_period_parser_configuration.dart';
32 |
33 | export 'src/date_time/base_set_extractor.dart';
34 | export 'src/date_time/english/english_set_extractor_configuration.dart';
35 |
36 | export 'src/date_time/base_set_parser.dart';
37 | export 'src/date_time/english/english_set_parser_configuration.dart';
38 |
39 | export 'src/date_time/base_merged_date_time_extractor.dart';
40 | export 'src/date_time/base_merged_date_time_parser.dart';
41 | export 'src/core/english_merged_extractor.dart';
42 | export 'src/core/global_recognizer.dart';
43 |
44 | export 'src/duration/base_duration_parser.dart';
45 | export 'src/duration/duration.dart';
46 | export 'src/duration/duration_extractor.dart';
47 | export 'src/duration/english_duration_parser_configuration.dart';
48 |
49 | export 'src/numbers/numbers.dart';
50 |
51 | export 'src/time/english_time_zone.dart';
52 | export 'src/time/english_time_zone_extractor.dart';
53 | export 'src/time/time_zone_extractor.dart';
54 |
55 | // NLP Processing Primitives
56 | export 'src/core/extraction.dart';
57 | export 'src/core/parser.dart';
58 |
59 | // Infrastructure
60 | export 'src/regular_expressions/regular_expressions_extensions.dart';
61 |
62 | // Custom RegExp
63 | export 'src/regular_expressions/js_regexp.dart';
64 |
--------------------------------------------------------------------------------
/lib/src/core/cultures.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: constant_identifier_names
2 |
3 | class Culture {
4 | static List getSupportedCultureCodes() {
5 | return SupportedCultures.map((c) => c.cultureCode).toList();
6 | }
7 |
8 | static const SupportedCultures = {
9 | Culture("English", English),
10 | Culture("Chinese", Chinese),
11 | Culture("Spanish", Spanish),
12 | Culture("Portuguese", Portuguese),
13 | Culture("French", French),
14 | Culture("German", German),
15 | Culture("Japanese", Japanese),
16 | Culture("Dutch", Dutch),
17 | Culture("Italian", Italian),
18 | };
19 |
20 | static const English = "en-us";
21 | static const Chinese = "zh-cn";
22 | static const Spanish = "es-es";
23 | static const Portuguese = "pt-br";
24 | static const French = "fr-fr";
25 | static const German = "de-de";
26 | static const Japanese = "ja-jp";
27 | static const Dutch = "nl-nl";
28 | static const Italian = "it-it";
29 |
30 | const Culture(this.cultureName, this.cultureCode);
31 |
32 | final String cultureName;
33 | final String cultureCode;
34 | }
35 |
36 | class CultureInfo {
37 | static CultureInfo getCultureInfo(String cultureCode) {
38 | return CultureInfo(cultureCode);
39 | }
40 |
41 | const CultureInfo(this.cultureCode);
42 |
43 | final String cultureCode;
44 | }
45 |
--------------------------------------------------------------------------------
/lib/src/core/definition_loader.dart:
--------------------------------------------------------------------------------
1 | class DefinitionLoader {
2 | static Map LoadAmbiguityFilters(Map filters) {
3 | var ambiguityFiltersDict = {};
4 |
5 | for (var item in filters.entries) {
6 | if ("null" != item.key) {
7 | ambiguityFiltersDict.putIfAbsent(RegExp(item.key), () => RegExp(item.value));
8 | }
9 | }
10 |
11 | return ambiguityFiltersDict;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lib/src/core/extraction.dart:
--------------------------------------------------------------------------------
1 | abstract interface class IExtractor {
2 | List extract(String input);
3 | }
4 |
5 | class ExtractResult {
6 | ExtractResult({
7 | required this.start,
8 | required this.length,
9 | required this.text,
10 | this.type,
11 | this.data,
12 | this.metadata,
13 | });
14 |
15 | int start;
16 | int length;
17 | String? type;
18 | String text;
19 | Object? data;
20 | Metadata? metadata;
21 |
22 | bool isOverlap(ExtractResult er) {
23 | return _isOverlap(this, er);
24 | }
25 |
26 | _isOverlap(ExtractResult er1, ExtractResult er2) {
27 | return !(er1.start >= er2.start + er2.length) && !(er2.start >= er1.start + er1.length);
28 | }
29 |
30 | bool isCover(ExtractResult er) {
31 | return _isCover(this, er);
32 | }
33 |
34 | bool _isCover(ExtractResult er1, ExtractResult er2) {
35 | return ((er2.start < er1.start) && ((er2.start + er2.length) >= (er1.start + er1.length))) ||
36 | ((er2.start <= er1.start) && ((er2.start + er2.length) > (er1.start + er1.length)));
37 | }
38 |
39 | /// Serialized this [ExtractResult] into a form that can be compared with JSON test cases.
40 | Map toTestCaseJson() {
41 | return {
42 | if (type != null) //
43 | "Type": type,
44 | "Text": text,
45 | "Start": start,
46 | "Length": length,
47 | // We don't include "data" or "metadata" because either the test cases don't want them,
48 | // or we're incorrectly setting these properties and they're triggering test failures.
49 | // Either way, for now, we don't include them.
50 | };
51 | }
52 | }
53 |
54 | class Metadata {
55 | Metadata({
56 | this.isDurationWithBeforeAndAfter = false,
57 | this.isHoliday = false,
58 | this.isMealtime = false,
59 | this.possiblyIncludePeriodEnd = false,
60 | this.isOrdinalRelative = false,
61 | this.hasMod = false,
62 | });
63 |
64 | bool isHoliday;
65 |
66 | bool hasMod = false;
67 |
68 | String offset = "";
69 |
70 | String relativeTo = "";
71 |
72 | // For cases like "from 2014 to 2018", the period end "2018" could be inclusive or exclusive
73 | // For extraction, we only mark this flag to avoid future duplicate judgment, whether to include the period end or not is not determined in the extraction step
74 | bool isPossiblyIncludePeriodEnd = false;
75 |
76 | // For cases like "2015年以前" (usually regards as "before 2015" in English), "5天以前"
77 | // (usually regards as "5 days ago" in English) in Chinese, we need to decide whether this is a "Date with Mode" or "Duration with Before and After".
78 | // We use this flag to avoid duplicate judgment both in the Extraction step and Parse step.
79 | // Currently, this flag is only used in Chinese DateTime as other languages don't have this ambiguity cases.
80 | bool isDurationWithBeforeAndAfter;
81 |
82 | bool isDurationDateWithWeekday = false;
83 |
84 | bool isHolidayRange = false;
85 |
86 | bool isHolidayWeekend = false;
87 |
88 | String holidayName = "";
89 |
90 | bool isOrdinalRelative;
91 |
92 | bool isMealtime = false;
93 |
94 | bool possiblyIncludePeriodEnd;
95 |
96 | bool isDurationWithAgoAndLater = false;
97 | }
98 |
99 | class Token {
100 | static List mergeAllTokens(List tokens, String text, String extractorName) {
101 | final result = [];
102 | final mergedTokens = [];
103 |
104 | tokens.sort((o1, o2) {
105 | if (o1.start != o2.start) {
106 | return o1.start - o2.start;
107 | }
108 |
109 | return o2.length - o1.length;
110 | });
111 |
112 | for (final token in tokens) {
113 | bool bAdd = true;
114 | for (int i = 0; i < mergedTokens.length && bAdd; i++) {
115 | // It is included in one of the current tokens
116 | if (token.start >= mergedTokens[i].start && token.end <= mergedTokens[i].end) {
117 | bAdd = false;
118 | }
119 |
120 | // If it contains overlaps
121 | if (token.start > mergedTokens[i].start && token.start < mergedTokens[i].end) {
122 | bAdd = false;
123 | }
124 |
125 | // It includes one of the tokens and should replace the included one
126 | if (token.start <= mergedTokens[i].start && token.end >= mergedTokens[i].end) {
127 | bAdd = false;
128 | mergedTokens[i] = token;
129 | }
130 | }
131 |
132 | if (bAdd) {
133 | mergedTokens.add(token);
134 | }
135 | }
136 |
137 | for (final token in mergedTokens) {
138 | String substring = text.substring(token.start, token.end);
139 |
140 | ExtractResult er = ExtractResult(
141 | start: token.start,
142 | length: token.length,
143 | text: substring,
144 | type: extractorName,
145 | data: null,
146 | metadata: token.metadata,
147 | );
148 |
149 | result.add(er);
150 | }
151 |
152 | return result;
153 | }
154 |
155 | static List getTokenFromRegex(RegExp pattern, String text) {
156 | final result = [];
157 |
158 | final matches = pattern.allMatches(text);
159 | for (final match in matches) {
160 | result.add(
161 | Token(match.start, match.end),
162 | );
163 | }
164 |
165 | return result;
166 | }
167 |
168 | Token(this.start, this.end, [this.text, this.metadata]);
169 |
170 | final int start;
171 | final int end;
172 | int get length => end < start ? 0 : end - start;
173 | final String? text;
174 | final Metadata? metadata;
175 | }
176 |
--------------------------------------------------------------------------------
/lib/src/core/global_recognizer.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/model_result.dart';
2 | import 'package:nlp/src/date_time/date_time_recognizer.dart';
3 |
4 | class GlobalRecognizer {
5 | static List recognize(String text) {
6 | final results = [
7 | ...DateTimeRecognizer.recognizeDateTime(text),
8 | ];
9 |
10 | return results;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/lib/src/core/model_result.dart:
--------------------------------------------------------------------------------
1 | class ModelResult {
2 | static final String parentTextKey = "parentText";
3 |
4 | const ModelResult({
5 | required this.text,
6 | required this.start,
7 | required this.end,
8 | required this.typeName,
9 | required this.resolution,
10 | this.parentText,
11 | });
12 |
13 | final String? parentText;
14 | final String text;
15 | final int start;
16 | final int end;
17 | final String typeName;
18 | final Map resolution; // Note: This is expected to be sorted
19 |
20 | ModelResult copyWith({
21 | String? parentText,
22 | String? text,
23 | int? start,
24 | int? end,
25 | String? typeName,
26 | Map? resolution,
27 | }) {
28 | return ModelResult(
29 | parentText: parentText ?? this.parentText,
30 | text: text ?? this.text,
31 | start: start ?? this.start,
32 | end: end ?? this.end,
33 | typeName: typeName ?? this.typeName,
34 | resolution: resolution ?? this.resolution,
35 | );
36 | }
37 |
38 | Map toJson() {
39 | return {
40 | "parentText": parentText,
41 | "text": text,
42 | "start": start,
43 | "end": end,
44 | "typeName": typeName,
45 | "resolution": resolution,
46 | };
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/src/core/number_format_utility.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: non_constant_identifier_names
2 |
3 | import 'package:nlp/src/core/cultures.dart';
4 | import 'package:nlp/src/numbers/number_constants.dart';
5 |
6 | class NumberFormatUtility {
7 | static final _supportedCultures = {
8 | Culture.English: LongFormatType.DoubleNumCommaDot,
9 | Culture.Spanish: LongFormatType.DoubleNumDotComma,
10 | Culture.Portuguese: LongFormatType.DoubleNumDotComma,
11 | Culture.French: LongFormatType.DoubleNumDotComma,
12 | Culture.German: LongFormatType.DoubleNumDotComma,
13 | Culture.Chinese: null,
14 | Culture.Japanese: LongFormatType.DoubleNumDotComma,
15 | };
16 |
17 | static String format(Object value, CultureInfo culture) {
18 | double doubleValue = value as double;
19 | String result;
20 |
21 | // EXPONENTIAL_AT: [-5, 15] });
22 | // For small positive decimal places. E.g.: 0,000015 or 0,0000015 -> 1.5E-05 or 1.5E-06
23 | try {
24 | if (doubleValue > 0 && doubleValue != doubleValue.round() && doubleValue < 1E-4) {
25 | result = doubleValue.toString();
26 | } else {
27 | // FIXME (Matt): I wasn't sure what the following code was trying to achieve so I
28 | // replaced it with a direct toString() on the double.
29 | // BigDecimal bc = BigDecimal(doubleValue, MathContext(15, RoundingMode.HALF_EVEN));
30 | // result = bc.toString();
31 | result = doubleValue.toString();
32 | }
33 | } catch (exception) {
34 | return value.toString();
35 | }
36 |
37 | result = result.replaceAll('e', 'E');
38 | if (result.contains("E-")) {
39 | final parts = result.split(RegExp.escape("E-"));
40 | parts[0] = QueryProcessor.trimEnd(parts[0], ".0");
41 | parts[1] = parts[1].padLeft(2, '0');
42 | result = parts.join("E-");
43 | }
44 |
45 | if (result.contains("E+")) {
46 | final parts = result.split(RegExp.escape("E+"));
47 | parts[0] = QueryProcessor.trimEnd(parts[0], "0");
48 | result = parts.join("E+");
49 | }
50 |
51 | if (result.contains(".")) {
52 | result = QueryProcessor.trimEnd(result, "0");
53 | result = QueryProcessor.trimEnd(result, ".");
54 | }
55 |
56 | if (_supportedCultures.containsKey(culture.cultureCode)) {
57 | final longFormat = _supportedCultures[culture.cultureCode];
58 | if (longFormat != null) {
59 | final buffer = StringBuffer();
60 | for (int i = 0; i < result.length; i += 1) {
61 | buffer.write(_changeMark(result[i], longFormat));
62 | }
63 |
64 | result = buffer.toString();
65 | }
66 | }
67 |
68 | return result;
69 | }
70 |
71 | static String _changeMark(String c, LongFormatType longFormat) {
72 | if (c == '.') {
73 | return longFormat.decimalsMark;
74 | } else if (c == ',') {
75 | return longFormat.thousandsMark;
76 | }
77 |
78 | return c;
79 | }
80 | }
81 |
82 | class LongFormatType {
83 | // Reference Value : 1234567.89
84 |
85 | // 1,234,567
86 | static LongFormatType IntegerNumComma = LongFormatType(',', '\0');
87 |
88 | // 1.234.567
89 | static LongFormatType IntegerNumDot = LongFormatType('.', '\0');
90 |
91 | // 1 234 567
92 | static LongFormatType IntegerNumBlank = LongFormatType(' ', '\0');
93 |
94 | // 1 234 567
95 | static LongFormatType IntegerNumNoBreakSpace = LongFormatType(Constants.NO_BREAK_SPACE, '\0');
96 |
97 | // 1'234'567
98 | static LongFormatType IntegerNumQuote = LongFormatType('\'', '\0');
99 |
100 | // 1,234,567.89
101 | static LongFormatType DoubleNumCommaDot = LongFormatType(',', '.');
102 |
103 | // 1,234,567·89
104 | static LongFormatType DoubleNumCommaCdot = LongFormatType(',', '·');
105 |
106 | // 1 234 567,89
107 | static LongFormatType DoubleNumBlankComma = LongFormatType(' ', ',');
108 |
109 | // 1 234 567,89
110 | static LongFormatType DoubleNumNoBreakSpaceComma = LongFormatType(Constants.NO_BREAK_SPACE, ',');
111 |
112 | // 1 234 567.89
113 | static LongFormatType DoubleNumBlankDot = LongFormatType(' ', '.');
114 |
115 | // 1 234 567.89
116 | static LongFormatType DoubleNumNoBreakSpaceDot = LongFormatType(Constants.NO_BREAK_SPACE, '.');
117 |
118 | // 1.234.567,89
119 | static LongFormatType DoubleNumDotComma = LongFormatType('.', ',');
120 |
121 | // 1'234'567,89
122 | static LongFormatType DoubleNumQuoteComma = LongFormatType('\'', ',');
123 |
124 | LongFormatType(this.thousandsMark, this.decimalsMark);
125 |
126 | final String decimalsMark;
127 | final String thousandsMark;
128 | }
129 |
130 | class QueryProcessor {
131 | static String trimEnd(String input, String chars) {
132 | return input.replaceAll("[${RegExp.escape(chars)}]+\$", "");
133 | }
134 |
135 | static List split(String input, List delimiters) {
136 | final delimitersRegex = delimiters.map((s) => RegExp.escape(s)).join("|");
137 |
138 | return input.split(delimitersRegex).where((s) => s.isNotEmpty).toList();
139 | }
140 |
141 | static String? removeDiacritics(String? query) {
142 | if (query == null) {
143 | return null;
144 | }
145 |
146 | return removeDiacritics(query);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/lib/src/core/parser.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/nlp.dart';
2 | import 'package:nlp/src/core/extraction.dart';
3 | import 'package:nlp/src/date_time/date_time_parsing.dart';
4 | import 'package:nlp/src/date_time/english_date_time_parser.dart';
5 |
6 | abstract interface class IMergedParserConfiguration implements ICommonDateTimeParserConfiguration {
7 | RegExp getBeforeRegex();
8 |
9 | RegExp getAfterRegex();
10 |
11 | RegExp getSinceRegex();
12 |
13 | RegExp getAroundRegex();
14 |
15 | RegExp getSuffixAfterRegex();
16 |
17 | RegExp getYearRegex();
18 |
19 | RegExp getEqualRegex();
20 |
21 | bool checkBothBeforeAfter();
22 |
23 | // IDateTimeParser getGetParser();
24 | //
25 | // IDateTimeParser getHolidayParser();
26 |
27 | // IDateTimeParser getTimeZoneParser();
28 |
29 | // TODO: bring back
30 | // StringMatcher getSuperfluousWordMatcher();
31 | }
32 |
33 | abstract interface class IParser {
34 | ParseResult? parse(ExtractResult extractResult);
35 | }
36 |
37 | class ParseResult extends ExtractResult {
38 | factory ParseResult.fromExtractResult(ExtractResult extractResult) {
39 | return ParseResult(
40 | start: extractResult.start,
41 | length: extractResult.length,
42 | text: extractResult.text,
43 | type: extractResult.type,
44 | data: extractResult.data,
45 | metadata: extractResult.metadata,
46 | );
47 | }
48 |
49 | ParseResult({
50 | required super.start,
51 | required super.length,
52 | required super.text,
53 | super.type,
54 | super.data,
55 | this.value,
56 | this.resolutionStr,
57 | super.metadata,
58 | });
59 |
60 | // Value is for resolution.
61 | // e.g. 1000 for "one thousand".
62 | // The resolutions are different for different parsers.
63 | // Therefore, we use object here.
64 | Object? value;
65 |
66 | // Output the value in string format.
67 | // It is used in some parsers.
68 | String? resolutionStr;
69 |
70 | /// Serialized this [ParseResult] into a form that can be compared with JSON test cases.
71 | @override
72 | Map toTestCaseJson() {
73 | return {
74 | ...super.toTestCaseJson(),
75 | if (value != null) //
76 | "Value": value is DateTimeResolutionResult ? (value as DateTimeResolutionResult).toTestCaseJson() : value,
77 | if (resolutionStr != null && resolutionStr!.isNotEmpty) //
78 | "resolutionStr": resolutionStr,
79 | };
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/lib/src/core/range_timex_component.dart:
--------------------------------------------------------------------------------
1 | class RangeTimexComponents {
2 | RangeTimexComponents({
3 | this.beginTimex = '',
4 | this.endTimex = '',
5 | this.durationTimex = '',
6 | this.isValid = false,
7 | });
8 | String beginTimex;
9 |
10 | String endTimex;
11 |
12 | String durationTimex;
13 |
14 | bool isValid;
15 | }
16 |
--------------------------------------------------------------------------------
/lib/src/core/string_utility.dart:
--------------------------------------------------------------------------------
1 | class StringUtility {
2 | static bool isNullOrEmpty(String? source) {
3 | return source == null || source.isEmpty;
4 | }
5 |
6 | static bool isNullOrWhiteSpace(String? source) {
7 | return source == null || source.trim().isEmpty;
8 | }
9 |
10 | static bool isNotNullOrWhiteSpace(String? source) {
11 | return !isNullOrWhiteSpace(source);
12 | }
13 |
14 | static String format(double d) {
15 | if (d == d.toInt()) {
16 | return "${d.toInt()}";
17 | }
18 |
19 | return d.toString();
20 | }
21 |
22 | static bool isDigit(
23 | String? character, {
24 | bool positiveOnly = false,
25 | }) {
26 | if (character == null || character.isEmpty || character.length > 1) {
27 | return false;
28 | }
29 | final unicodeValue = character.codeUnitAt(0);
30 | return unicodeValue >= 48 && unicodeValue <= 57;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/src/core/task_mode_processing.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/date_time/constants.dart';
2 | import 'package:nlp/src/date_time/time_of_day_resolution_result.dart';
3 |
4 | class TasksModeProcessing {
5 | static TimeOfDayResolutionResult TasksModeResolveTimeOfDay(String tod) {
6 | var result = TimeOfDayResolutionResult();
7 | switch (tod) {
8 | case DateTimeConstants.EarlyMorning:
9 | result.Timex = DateTimeConstants.EarlyMorning;
10 | result.BeginHour = TasksModeConstants.EarlyMorningBeginHour;
11 | result.EndHour = TasksModeConstants.EarlyMorningEndHour;
12 | break;
13 | case DateTimeConstants.Morning:
14 | result.Timex = DateTimeConstants.Morning;
15 | result.BeginHour = TasksModeConstants.MorningBeginHour;
16 | result.EndHour = TasksModeConstants.MorningEndHour;
17 | break;
18 | case DateTimeConstants.MidDay:
19 | result.Timex = DateTimeConstants.MidDay;
20 | result.BeginHour = TasksModeConstants.MidDayBeginHour;
21 | result.EndHour = TasksModeConstants.MidDayEndHour;
22 | break;
23 | case DateTimeConstants.Afternoon:
24 | result.Timex = DateTimeConstants.Afternoon;
25 | result.BeginHour = TasksModeConstants.AfternoonBeginHour;
26 | result.EndHour = TasksModeConstants.AfternoonEndHour;
27 | break;
28 | case DateTimeConstants.Evening:
29 | result.Timex = DateTimeConstants.Evening;
30 | result.BeginHour = TasksModeConstants.EveningBeginHour;
31 | result.EndHour = TasksModeConstants.EveningEndHour;
32 | break;
33 | case DateTimeConstants.Daytime:
34 | result.Timex = DateTimeConstants.Daytime;
35 | result.BeginHour = TasksModeConstants.DaytimeBeginHour;
36 | result.EndHour = TasksModeConstants.DaytimeEndHour;
37 | break;
38 | case DateTimeConstants.Nighttime:
39 | result.Timex = DateTimeConstants.Nighttime;
40 | result.BeginHour = TasksModeConstants.NighttimeBeginHour;
41 | result.EndHour = TasksModeConstants.NighttimeEndHour;
42 | break;
43 | case DateTimeConstants.BusinessHour:
44 | result.Timex = DateTimeConstants.BusinessHour;
45 | result.BeginHour = TasksModeConstants.BusinessBeginHour;
46 | result.EndHour = TasksModeConstants.BusinessEndHour;
47 | break;
48 | case DateTimeConstants.Night:
49 | result.Timex = DateTimeConstants.Night;
50 | result.BeginHour = TasksModeConstants.NightBeginHour;
51 | result.EndHour = TasksModeConstants.NightEndHour;
52 | result.EndMin = TasksModeConstants.NightEndMin;
53 | break;
54 | case DateTimeConstants.MealtimeBreakfast:
55 | result.Timex = DateTimeConstants.MealtimeBreakfast;
56 | result.BeginHour = TasksModeConstants.MealtimeBreakfastBeginHour;
57 | result.EndHour = TasksModeConstants.MealtimeBreakfastEndHour;
58 | break;
59 | case DateTimeConstants.MealtimeBrunch:
60 | result.Timex = DateTimeConstants.MealtimeBrunch;
61 | result.BeginHour = TasksModeConstants.MealtimeBrunchBeginHour;
62 | result.EndHour = TasksModeConstants.MealtimeBrunchEndHour;
63 | break;
64 | case DateTimeConstants.MealtimeLunch:
65 | result.Timex = DateTimeConstants.MealtimeLunch;
66 | result.BeginHour = TasksModeConstants.MealtimeLunchBeginHour;
67 | result.EndHour = TasksModeConstants.MealtimeLunchEndHour;
68 | break;
69 | case DateTimeConstants.MealtimeDinner:
70 | result.Timex = DateTimeConstants.MealtimeDinner;
71 | result.BeginHour = TasksModeConstants.MealtimeDinnerBeginHour;
72 | result.EndHour = TasksModeConstants.MealtimeDinnerEndHour;
73 | break;
74 | default:
75 | break;
76 | }
77 |
78 | return result;
79 | }
80 |
81 | /*
82 | Change beginHour and endHour for subjective time refereneces under TasksMode.
83 | morning get's mapped to 6:00 am
84 | */
85 | static (int beginHour, int endHour, int endMin) GetMatchedTimeRangeForTasksMode(String text, String todSymbol) {
86 | var trimmedText = text.trim();
87 | int beginHour = 0;
88 | int endHour = 0;
89 | int endMin = 0;
90 | if (todSymbol == DateTimeConstants.Morning) {
91 | beginHour = TasksModeConstants.MorningBeginHour;
92 | endHour = TasksModeConstants.EarlyMorningEndHour;
93 | } else if (todSymbol == DateTimeConstants.Afternoon) {
94 | beginHour = DateTimeConstants.AfternoonBeginHour;
95 | endHour = DateTimeConstants.AfternoonEndHour;
96 | } else if (todSymbol == DateTimeConstants.Evening) {
97 | beginHour = DateTimeConstants.EveningBeginHour;
98 | endHour = DateTimeConstants.EveningEndHour;
99 | } else if (todSymbol == DateTimeConstants.Night) {
100 | beginHour = TasksModeConstants.NightBeginHour;
101 | endHour = TasksModeConstants.NightEndHour;
102 | } else if (todSymbol == DateTimeConstants.MealtimeBreakfast) {
103 | beginHour = TasksModeConstants.MealtimeBreakfastBeginHour;
104 | endHour = TasksModeConstants.MealtimeBreakfastEndHour;
105 | } else if (todSymbol == DateTimeConstants.MealtimeBrunch) {
106 | beginHour = TasksModeConstants.MealtimeBrunchBeginHour;
107 | endHour = TasksModeConstants.MealtimeBrunchEndHour;
108 | } else if (todSymbol == DateTimeConstants.MealtimeDinner) {
109 | beginHour = TasksModeConstants.MealtimeDinnerBeginHour;
110 | endHour = TasksModeConstants.MealtimeDinnerEndHour;
111 | } else if (todSymbol == DateTimeConstants.MealtimeLunch) {
112 | beginHour = TasksModeConstants.MealtimeLunchBeginHour;
113 | endHour = TasksModeConstants.MealtimeLunchEndHour;
114 | } else {
115 | return (beginHour, endHour, endMin);
116 | }
117 |
118 | return (beginHour, endHour, endMin);
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/lib/src/date_time/base_datetime_options_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/date_time/base_date_extractor.dart';
2 | import 'package:nlp/src/duration/duration.dart';
3 |
4 | class BaseDateTimeOptionsConfiguration implements IDateTimeOptionsConfiguration {
5 | BaseDateTimeOptionsConfiguration({
6 | required DateTimeOptions options,
7 | bool dmyDateFormat = false,
8 | }) : _options = options,
9 | _dmyDateFormat = dmyDateFormat;
10 |
11 | final bool _dmyDateFormat;
12 | @override
13 | bool get dmyDateFormat => _dmyDateFormat;
14 |
15 | final DateTimeOptions _options;
16 | @override
17 | DateTimeOptions get options => _options;
18 | }
19 |
--------------------------------------------------------------------------------
/lib/src/date_time/base_datetime_utility_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/nlp.dart';
2 | import 'package:nlp/src/date_time/datetime_utility_configuration.dart';
3 |
4 | abstract class BaseDatetimeUtilityConfiguration implements IDateTimeUtilityConfiguration {
5 | BaseDatetimeUtilityConfiguration(
6 | String agoRegex,
7 | String laterRegex,
8 | String inConnectorRegex,
9 | String sinceYearSuffixRegex,
10 | String withinNextPrefixRegex,
11 | String amDescRegex,
12 | String pmDescRegex,
13 | String amPmDescRegex,
14 | String rangeUnitRegex,
15 | String timeUnitRegex,
16 | String dateUnitRegex,
17 | String commonDatePrefixRegex,
18 | String rangePrefixRegex,
19 | //RegexOptions options,
20 | bool checkBothBeforeAfter)
21 | : _agoRegExp = RegExpComposer.sanitizeGroupsAndCompile(agoRegex),
22 | _laterRegExp = RegExpComposer.sanitizeGroupsAndCompile(laterRegex),
23 | _inConnectorRegExp = RegExpComposer.sanitizeGroupsAndCompile(inConnectorRegex),
24 | _sinceYearSuffixRegExp = RegExpComposer.sanitizeGroupsAndCompile(sinceYearSuffixRegex),
25 | _withinNextPrefixRegExp = RegExpComposer.sanitizeGroupsAndCompile(withinNextPrefixRegex),
26 | _amDescRegExp = RegExpComposer.sanitizeGroupsAndCompile(amDescRegex),
27 | _pmDescRegExp = RegExpComposer.sanitizeGroupsAndCompile(pmDescRegex),
28 | _amPmDescRegExp = RegExpComposer.sanitizeGroupsAndCompile(amPmDescRegex),
29 | _rangeUnitRegExp = RegExpComposer.sanitizeGroupsAndCompile(rangeUnitRegex),
30 | _timeUnitRegExp = RegExpComposer.sanitizeGroupsAndCompile(timeUnitRegex),
31 | _dateUnitRegExp = RegExpComposer.sanitizeGroupsAndCompile(dateUnitRegex),
32 | _commonDatePrefixRegExp = RegExpComposer.sanitizeGroupsAndCompile(commonDatePrefixRegex),
33 | _rangePrefixRegExp = RegExpComposer.sanitizeGroupsAndCompile(rangePrefixRegex),
34 | _checkBothBeforeAfter = checkBothBeforeAfter;
35 |
36 | // static TimeSpan RegexTimeOut => DateTimeRecognizer.GetTimeout(MethodBase.GetCurrentMethod().DeclaringType);
37 |
38 | final RegExp _agoRegExp;
39 | @override
40 | RegExp agoRegExp() => _agoRegExp;
41 |
42 | final RegExp _laterRegExp;
43 | @override
44 | RegExp laterRegExp() => _laterRegExp;
45 |
46 | final RegExp _inConnectorRegExp;
47 | @override
48 | RegExp inConnectorRegExp() => _inConnectorRegExp;
49 |
50 | final RegExp _sinceYearSuffixRegExp;
51 | @override
52 | RegExp sinceYearSuffixRegExp() => _sinceYearSuffixRegExp;
53 |
54 | final RegExp _withinNextPrefixRegExp;
55 | @override
56 | RegExp withinNextPrefixRegExp() => _withinNextPrefixRegExp;
57 |
58 | final RegExp _rangeUnitRegExp;
59 | @override
60 | RegExp rangeUnitRegExp() => _rangeUnitRegExp;
61 |
62 | final RegExp _timeUnitRegExp;
63 | @override
64 | RegExp timeUnitRegExp() => _timeUnitRegExp;
65 |
66 | final RegExp _dateUnitRegExp;
67 | @override
68 | RegExp dateUnitRegExp() => _dateUnitRegExp;
69 |
70 | final RegExp _amDescRegExp;
71 | @override
72 | RegExp amDescRegExp() => _amDescRegExp;
73 |
74 | final RegExp _pmDescRegExp;
75 | @override
76 | RegExp pmDescRegExp() => _pmDescRegExp;
77 |
78 | final RegExp _amPmDescRegExp;
79 | @override
80 | RegExp amPmDescRegExp() => _amPmDescRegExp;
81 |
82 | final RegExp _commonDatePrefixRegExp;
83 | @override
84 | RegExp commonDatePrefixRegExp() => _commonDatePrefixRegExp;
85 |
86 | final RegExp _rangePrefixRegExp;
87 | @override
88 | RegExp rangePrefixRegExp() => _rangePrefixRegExp;
89 |
90 | final bool _checkBothBeforeAfter;
91 | @override
92 | bool checkBothBeforeAfter() => _checkBothBeforeAfter;
93 | }
94 |
--------------------------------------------------------------------------------
/lib/src/date_time/base_holiday_extractor.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/nlp.dart';
2 | import 'package:nlp/src/date_time/date_time_extraction.dart';
3 | import 'package:nlp/src/date_time/holiday_extractor_configuration.dart';
4 |
5 | class BaseHolidayExtractor implements IDateTimeExtractor {
6 | BaseHolidayExtractor({required this.config});
7 |
8 | final IHolidayExtractorConfiguration config;
9 |
10 | final _rangeExtractorName = DateTimeConstants.SYS_DATETIME_DATEPERIOD;
11 |
12 | @override
13 | List extract(String input) {
14 | return extractDateTime(input, DateTime.now());
15 | }
16 |
17 | @override
18 | List extractDateTime(String input, DateTime reference) {
19 | final tokens = [];
20 | tokens.addAll(_holidayMatch(input));
21 | var ers = Token.mergeAllTokens(tokens, input, getExtractorName());
22 | for (var er in ers) {
23 | // If this is a daterange that contains a holiday, we should change its
24 | // type to indicate that.
25 |
26 | if (er.metadata?.isHolidayRange ?? false) {
27 | er.type = _rangeExtractorName;
28 | }
29 | }
30 |
31 | return ers;
32 | }
33 |
34 | @override
35 | String getExtractorName() => DateTimeConstants.SYS_DATETIME_DATE;
36 |
37 | List _holidayMatch(String text) {
38 | var ret = [];
39 | for (var regex in config.holidayRegexes()) {
40 | var matches = RegExpComposer.getMatchesSimple(regex, text);
41 |
42 | for (final match in matches) {
43 | var metaData = Metadata();
44 |
45 | // The objective here is to not lose the information of the holiday name
46 | // and year (if captured) when choosing. The data is extracted from the match
47 | // groups.
48 | final holidayWeekendGroup = match.getGroup(DateTimeConstants.HolidayWeekend);
49 |
50 | if (holidayWeekendGroup.value.isNotEmpty) {
51 | metaData.isHolidayRange = metaData.isHolidayWeekend = true;
52 |
53 | final holidayGroup = match.getGroup(DateTimeConstants.Holiday);
54 | metaData.holidayName = holidayGroup.value;
55 |
56 | final yearGroup = match.getGroup(DateTimeConstants.YearGroupName);
57 | if (yearGroup.value.isNotEmpty) {
58 | metaData.holidayName = "${metaData.holidayName} ${yearGroup.value}";
59 | }
60 | }
61 |
62 | metaData.isHoliday = true;
63 |
64 | ret.add(Token(match.index, match.index + match.length, "", metaData));
65 | }
66 | }
67 |
68 | return ret;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/lib/src/date_time/base_time_extractor.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 | import 'package:nlp/src/date_time/base_date_extractor.dart';
3 | import 'package:nlp/src/date_time/constants.dart';
4 | import 'package:nlp/src/date_time/date_time_extraction.dart';
5 | import 'package:nlp/src/date_time/extract_result_extension.dart';
6 | import 'package:nlp/src/date_time/timezone_utility.dart';
7 | import 'package:nlp/src/duration/duration.dart';
8 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
9 |
10 | class BaseTimeExtractor implements IDateTimeExtractor {
11 | BaseTimeExtractor(this.config);
12 |
13 | final ITimeExtractorConfiguration config;
14 |
15 | @override
16 | String getExtractorName() {
17 | return DateTimeConstants.SYS_DATETIME_TIME;
18 | }
19 |
20 | @override
21 | List extract(String input) {
22 | return extractDateTime(input, DateTime.now());
23 | }
24 |
25 | @override
26 | List extractDateTime(String input, DateTime reference) {
27 | final normalizedInput = input.toLowerCase();
28 |
29 | var tokens = [];
30 | tokens.addAll(BasicRegexMatch(normalizedInput));
31 | tokens.addAll(AtRegexMatch(normalizedInput));
32 | tokens.addAll(BeforeAfterRegexMatch(normalizedInput));
33 | tokens.addAll(SpecialCasesRegexMatch(normalizedInput, reference));
34 |
35 | var timeErs = Token.mergeAllTokens(tokens, normalizedInput, getExtractorName());
36 |
37 | if (config.options.match(DateTimeOptions.EnablePreview)) {
38 | timeErs = TimeZoneUtility.MergeTimeZones(
39 | timeErs, config.TimeZoneExtractor().extractDateTime(normalizedInput, reference), normalizedInput);
40 | }
41 |
42 | // Remove common ambiguous cases
43 | timeErs = ExtractResultExtension.FilterAmbiguity(timeErs, normalizedInput, config.AmbiguityFiltersDict());
44 |
45 | return timeErs;
46 | }
47 |
48 | List BasicRegexMatch(String text) {
49 | var results = [];
50 |
51 | for (var regex in config.TimeRegexList()) {
52 | var matches = RegExpComposer.getMatchesSimple(regex, text);
53 |
54 | for (final match in matches) {
55 | // @TODO Workaround to avoid incorrect partial-only matches. Remove after time regex reviews across languages.
56 | var lth = match.getGroup("lth").value;
57 |
58 | if (lth.isEmpty ||
59 | (lth.length != match.length && !(match.length == lth.length + 1 && match.value.endsWith(" ")))) {
60 | results.add(Token(match.index, match.index + match.length));
61 | }
62 | }
63 | }
64 |
65 | return results;
66 | }
67 |
68 | List AtRegexMatch(String text) {
69 | var result = [];
70 |
71 | // handle "at 5", "at seven"
72 | if (config.AtRegex().hasMatch(text)) {
73 | var matches = config.AtRegex().allMatches(text);
74 | for (Match match in matches) {
75 | if (match.end - match.start + 1 < text.length && text[match.end - 1] == '%') {
76 | continue;
77 | }
78 |
79 | result.add(Token(match.start, match.end));
80 | }
81 | }
82 |
83 | return result;
84 | }
85 |
86 | List BeforeAfterRegexMatch(String text) {
87 | var result = [];
88 |
89 | // only enabled in CalendarMode
90 | if (config.options.match(DateTimeOptions.CalendarMode)) {
91 | // handle "before 3", "after three"
92 | var beforeAfterRegex = config.TimeBeforeAfterRegex();
93 | if (beforeAfterRegex.hasMatch(text)) {
94 | var matches = beforeAfterRegex.allMatches(text);
95 | for (Match match in matches) {
96 | result.add(Token(match.start, match.end));
97 | }
98 | }
99 | }
100 |
101 | return result;
102 | }
103 |
104 | List SpecialCasesRegexMatch(String text, DateTime reference) {
105 | var result = [];
106 |
107 | // handle "ish"
108 | if (config.IshRegex().hasMatch(text)) {
109 | var matches = config.IshRegex().allMatches(text);
110 |
111 | for (Match match in matches) {
112 | result.add(Token(match.start, match.end));
113 | }
114 | }
115 |
116 | return result;
117 | }
118 |
119 | // public static readonly Regex HourRegex =
120 | // new Regex(BaseDateTime.HourRegex, RegexOptions.Singleline | RegexOptions.Compiled, RegexTimeOut);
121 |
122 | // public static readonly Regex MinuteRegex =
123 | // new Regex(BaseDateTime.MinuteRegex, RegexOptions.Singleline | RegexOptions.Compiled, RegexTimeOut);
124 |
125 | // public static readonly Regex SecondRegex =
126 | // new Regex(BaseDateTime.SecondRegex, RegexOptions.Singleline | RegexOptions.Compiled, RegexTimeOut);
127 | }
128 |
129 | abstract interface class ITimeExtractorConfiguration extends IDateTimeOptionsConfiguration {
130 | IDateTimeExtractor TimeZoneExtractor();
131 |
132 | List TimeRegexList();
133 |
134 | RegExp AtRegex();
135 |
136 | RegExp IshRegex();
137 |
138 | RegExp TimeBeforeAfterRegex();
139 |
140 | RegExp TimeTokenPrefix();
141 |
142 | Map AmbiguityFiltersDict();
143 | }
144 |
--------------------------------------------------------------------------------
/lib/src/date_time/data_structure.dart:
--------------------------------------------------------------------------------
1 | enum DatePeriodTimexType {
2 | ///
3 | /// Represents a day Period
4 | ///
5 | ByDay,
6 |
7 | ///
8 | /// Represents a week Period
9 | ///
10 | ByWeek,
11 |
12 | ///
13 | /// Represents a fortnight Period
14 | ///
15 | ByFortnight,
16 |
17 | ///
18 | /// Represents a month Period
19 | ///
20 | ByMonth,
21 |
22 | ///
23 | /// Represents a year Period
24 | ///
25 | ByYear,
26 | }
27 |
28 | enum PeriodType {
29 | ///
30 | /// Represents a ShortTime.
31 | ///
32 | ShortTime,
33 |
34 | ///
35 | /// Represents a FullTime.
36 | ///
37 | FullTime,
38 | }
39 |
40 | enum TimeType {
41 | ///
42 | /// 十二点二十三分五十八秒,12点23分53秒
43 | ///
44 | CjkTime,
45 |
46 | ///
47 | /// 差五分十二点
48 | ///
49 | LessTime,
50 |
51 | ///
52 | /// 大约早上10:00
53 | ///
54 | DigitTime,
55 | }
56 |
--------------------------------------------------------------------------------
/lib/src/date_time/date_context.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/timex_utility.dart';
2 | import 'package:nlp/src/date_time/base_date_parser.dart';
3 | import 'package:nlp/src/date_time/constants.dart';
4 | import 'package:nlp/src/date_time/date_time_parsing.dart';
5 | import 'package:nlp/src/date_time/date_time_recognizer.dart';
6 | import 'package:nlp/src/date_time/date_util.dart';
7 |
8 | class DateContext {
9 | DateContext({
10 | this.year = DateTimeConstants.InvalidYear,
11 | });
12 |
13 | final int year;
14 |
15 | static (DateTime future, DateTime past) GenerateDates(
16 | bool noYear, DateTime referenceDate, int year, int month, int day) {
17 | var futureDate = DateUtil.createSafeDate(year, month, day);
18 | var pastDate = DateUtil.createSafeDate(year, month, day);
19 | var futureYear = year;
20 | var pastYear = year;
21 | if (noYear) {
22 | if (IsFeb29th(year, month, day)) {
23 | if (isLeapYear(year)) {
24 | if (futureDate.isBefore(referenceDate)) {
25 | futureDate = DateTime(futureYear + 4, month, day);
26 | } else {
27 | pastDate = DateTime(pastYear - 4, month, day);
28 | }
29 | } else {
30 | pastYear = pastYear >> 2 << 2;
31 | if (!isLeapYear(pastYear)) {
32 | pastYear -= 4;
33 | }
34 |
35 | futureYear = pastYear + 4;
36 | if (!isLeapYear(futureYear)) {
37 | futureYear += 4;
38 | }
39 |
40 | futureDate = DateTime(futureYear, month, day);
41 | pastDate = DateTime(pastYear, month, day);
42 | }
43 | } else {
44 | if (futureDate.isBefore(referenceDate) && futureDate != DateUtil.minValue()) {
45 | futureDate = DateTime(year + 1, month, day);
46 | }
47 |
48 | if (!referenceDate.isAfter(pastDate) && pastDate != DateUtil.minValue()) {
49 | pastDate = DateTime(year - 1, month, day);
50 | }
51 | }
52 | }
53 |
54 | futureDate = adjustTimeZones(referenceDate, futureDate);
55 | pastDate = adjustTimeZones(referenceDate, pastDate);
56 |
57 | return (futureDate, pastDate);
58 | }
59 |
60 | static bool IsFeb29th(int year, int month, int day) {
61 | return month == 2 && day == 29;
62 | }
63 |
64 | static DateTime adjustTimeZones(DateTime original, DateTime dateTime) {
65 | final timeZoneOffset = original.timeZoneOffset;
66 | if (dateTime.timeZoneOffset != timeZoneOffset) {
67 | final newDate = dateTime.subtract(dateTime.timeZoneOffset).add(timeZoneOffset);
68 | }
69 | return dateTime;
70 | }
71 |
72 | static bool IsFeb29thDate(DateTime date) {
73 | return date.month == 2 && date.day == 29;
74 | }
75 |
76 | static bool isLeapYear(int year) {
77 | return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
78 | }
79 |
80 | (DateTimeParseResult pr1, DateTimeParseResult pr2) SyncYear(DateTimeParseResult pr1, DateTimeParseResult pr2) {
81 | if (IsEmpty()) {
82 | int futureYear;
83 | int pastYear;
84 | if (IsFeb29thDate((pr1.value as DateTimeResolutionResult).futureValue as DateTime)) {
85 | futureYear = ((pr1.value as DateTimeResolutionResult).futureValue as DateTime).year;
86 | pastYear = ((pr1.value as DateTimeResolutionResult).pastValue as DateTime).year;
87 | pr2.value = SyncDateEntityResolutionInFeb29th(pr2.value as DateTimeResolutionResult, futureYear, pastYear);
88 | } else if (IsFeb29thDate((pr2.value as DateTimeResolutionResult).futureValue as DateTime)) {
89 | futureYear = ((pr2.value as DateTimeResolutionResult).futureValue as DateTime).year;
90 | pastYear = ((pr2.value as DateTimeResolutionResult).pastValue as DateTime).year;
91 | pr1.value = SyncDateEntityResolutionInFeb29th(pr1.value as DateTimeResolutionResult, futureYear, pastYear);
92 | }
93 | }
94 |
95 | return (pr1, pr2);
96 | }
97 |
98 | static DateTime SwiftDateObject(DateTime beginDate, DateTime endDate) {
99 | if (beginDate.isAfter(endDate)) {
100 | beginDate = beginDate.AddYears(-1);
101 | }
102 |
103 | return beginDate;
104 | }
105 |
106 | bool IsEmpty() {
107 | return year == DateTimeConstants.InvalidYear;
108 | }
109 |
110 | DateTimeResolutionResult ProcessDatePeriodEntityResolution(DateTimeResolutionResult resolutionResult) {
111 | if (!IsEmpty()) {
112 | resolutionResult.timex = TimexUtility.SetTimexWithContext(resolutionResult.timex ?? '', this);
113 | resolutionResult.futureValue = SetDateRangeWithContext(resolutionResult.futureValue as (DateTime, DateTime));
114 | resolutionResult.pastValue = SetDateRangeWithContext(resolutionResult.pastValue as (DateTime, DateTime));
115 | }
116 |
117 | return resolutionResult;
118 | }
119 |
120 | DateTimeResolutionResult ProcessDateEntityResolution(DateTimeResolutionResult resolutionResult) {
121 | if (!IsEmpty()) {
122 | resolutionResult.timex = TimexUtility.SetTimexWithContext(resolutionResult.timex ?? '', this);
123 | resolutionResult.futureValue = SetDateWithContext(resolutionResult.futureValue as DateTime);
124 | resolutionResult.pastValue = SetDateWithContext(resolutionResult.pastValue as DateTime);
125 | }
126 |
127 | return resolutionResult;
128 | }
129 |
130 | DateTimeParseResult ProcessDateEntityParsingResult(DateTimeParseResult originalResult) {
131 | if (!IsEmpty()) {
132 | originalResult.timexStr = TimexUtility.SetTimexWithContext(originalResult.timexStr ?? '', this);
133 | originalResult.value = ProcessDateEntityResolution(originalResult.value as DateTimeResolutionResult);
134 | }
135 |
136 | return originalResult;
137 | }
138 |
139 | DateTimeResolutionResult SyncDateEntityResolutionInFeb29th(
140 | DateTimeResolutionResult resolutionResult, int futureYear, int pastYear) {
141 | resolutionResult.futureValue = SetDateWithContext(resolutionResult.futureValue as DateTime, futureYear);
142 | resolutionResult.pastValue = SetDateWithContext(resolutionResult.pastValue as DateTime, pastYear);
143 |
144 | return resolutionResult;
145 | }
146 |
147 | (DateTime, DateTime) SetDateRangeWithContext((DateTime, DateTime) originalDateRange) {
148 | var startDate = SetDateWithContext(originalDateRange.$1);
149 | var endDate = SetDateWithContext(originalDateRange.$2);
150 |
151 | return (startDate, endDate);
152 | }
153 |
154 | DateTime SetDateWithContext(DateTime originalDate, [int year = -1]) {
155 | if (originalDate != DateUtil.minValue()) {
156 | return DateUtil.createSafeDate(year == -1 ? this.year : year, originalDate.month, originalDate.day);
157 | }
158 |
159 | return originalDate;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/lib/src/date_time/date_object_extension.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/date_time/base_date_parser.dart';
2 | import 'package:nlp/src/date_time/constants.dart';
3 | import 'package:nlp/src/date_time/date_util.dart';
4 |
5 | class DateObjectExtension {
6 | static const IndexOfLeapMonth = 1;
7 | static List MonthValidDays = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
8 |
9 | static DateTime GetFirstThursday(int year, [int month = DateTimeConstants.InvalidMonth]) {
10 | var targetMonth = month;
11 |
12 | if (month == DateTimeConstants.InvalidMonth) {
13 | targetMonth = 1;
14 | }
15 |
16 | var firstDay = DateUtil.createSafeDate(year, targetMonth, 1);
17 | DateTime firstThursday = firstDay.This(DateTime.thursday);
18 |
19 | // Thursday falls into previous year or previous month
20 | if (firstThursday.month != targetMonth) {
21 | firstThursday = firstDay.AddDays(DateTimeConstants.WeekDayCount);
22 | }
23 |
24 | return firstThursday;
25 | }
26 |
27 | static DateTime GetLastThursday(int year, [int month = DateTimeConstants.InvalidMonth]) {
28 | var targetMonth = month;
29 |
30 | if (month == DateTimeConstants.InvalidMonth) {
31 | targetMonth = 12;
32 | }
33 |
34 | var lastDay = GetLastDay(year, targetMonth);
35 | DateTime lastThursday = lastDay.This(DateTime.thursday);
36 |
37 | // Thursday falls into next year or next month
38 | if (lastThursday.month != targetMonth) {
39 | lastThursday = lastThursday.AddDays(-DateTimeConstants.WeekDayCount);
40 | }
41 |
42 | return lastThursday;
43 | }
44 |
45 | static DateTime GetLastDay(int year, int month) {
46 | month++;
47 |
48 | if (month == 13) {
49 | year++;
50 | month = 1;
51 | }
52 |
53 | var firstDayOfNextMonth = DateUtil.createSafeDate(year, month, 1);
54 |
55 | return firstDayOfNextMonth.AddDays(-1);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/lib/src/date_time/date_parser_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 | import 'package:nlp/src/core/parser.dart';
3 | import 'package:nlp/src/date_time/base_date_extractor.dart';
4 | import 'package:nlp/src/date_time/date_time_extraction.dart';
5 | import 'package:nlp/src/date_time/date_time_parsing.dart';
6 | import 'package:nlp/src/date_time/datetime_utility_configuration.dart';
7 |
8 | abstract interface class IDateParserConfiguration extends IDateTimeOptionsConfiguration {
9 | String dateTokenPrefix();
10 |
11 | IExtractor integerExtractor();
12 |
13 | IExtractor ordinalExtractor();
14 |
15 | IExtractor cardinalExtractor();
16 |
17 | IParser numberParser();
18 |
19 | IDateTimeExtractor durationExtractor();
20 |
21 | IDateExtractor dateExtractor();
22 |
23 | IDateTimeParser durationParser();
24 |
25 | IDateTimeParser holidayParser();
26 |
27 | List dateRegexes();
28 |
29 | RegExp onRegExp();
30 |
31 | RegExp specialDayRegExp();
32 |
33 | RegExp specialDayWithNumRegExp();
34 |
35 | RegExp nextRegExp();
36 |
37 | RegExp thisRegExp();
38 |
39 | RegExp lastRegExp();
40 |
41 | RegExp unitRegExp();
42 |
43 | RegExp upcomingPrefixRegExp();
44 |
45 | RegExp pastPrefixRegExp();
46 |
47 | RegExp weekDayRegExp();
48 |
49 | RegExp monthRegExp();
50 |
51 | RegExp weekDayOfMonthRegExp();
52 |
53 | RegExp forTheRegExp();
54 |
55 | RegExp weekDayAndDayOfMothRegExp();
56 |
57 | RegExp weekDayAndDayRegExp();
58 |
59 | RegExp relativeMonthRegExp();
60 |
61 | RegExp strictRelativeRegExp();
62 |
63 | RegExp yearSuffix();
64 |
65 | RegExp relativeWeekDayRegExp();
66 |
67 | RegExp relativeDayRegExp();
68 |
69 | RegExp nextPrefixRegExp();
70 |
71 | RegExp previousPrefixRegExp();
72 |
73 | RegExp beforeAfterRegExp();
74 |
75 | RegExp tasksModeDurationToDatePatterns();
76 |
77 | Map unitMap();
78 |
79 | Map dayOfMonth();
80 |
81 | Map dayOfWeek();
82 |
83 | Map monthOfYear();
84 |
85 | Map cardinalMap();
86 |
87 | List sameDayTerms();
88 |
89 | List plusOneDayTerms();
90 |
91 | List minusOneDayTerms();
92 |
93 | List plusTwoDayTerms();
94 |
95 | List minusTwoDayTerms();
96 |
97 | bool checkBothBeforeAfter();
98 |
99 | IDateTimeUtilityConfiguration utilityConfiguration();
100 |
101 | int getSwiftMonthOrYear(String text);
102 |
103 | bool isCardinalLast(String text);
104 |
105 | String normalize(String text);
106 | }
107 |
--------------------------------------------------------------------------------
/lib/src/date_time/date_time_extraction.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
3 |
4 | abstract interface class IDateExtractor extends IDateTimeExtractor {
5 | int getYearFromText(NlpMatch match);
6 | }
7 |
8 | abstract interface class IDateTimeExtractor implements IExtractor {
9 | String getExtractorName();
10 |
11 | List extractDateTime(String input, DateTime reference);
12 | }
13 |
--------------------------------------------------------------------------------
/lib/src/date_time/date_time_parsing.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 | import 'package:nlp/src/core/parser.dart';
3 |
4 | abstract interface class IDateTimeParser implements IParser {
5 | String getParserName();
6 |
7 | DateTimeParseResult parseDateTime(ExtractResult extractResult, DateTime reference);
8 |
9 | List filterResults(String query, List candidateResults);
10 | }
11 |
12 | class DateTimeParseResult extends ParseResult {
13 | factory DateTimeParseResult.fromExtractResult(ExtractResult extractResult) {
14 | return DateTimeParseResult(
15 | start: extractResult.start,
16 | length: extractResult.length,
17 | text: extractResult.text,
18 | type: extractResult.type,
19 | data: extractResult.data,
20 | );
21 | }
22 |
23 | factory DateTimeParseResult.fromParseResult(ParseResult parseResult) {
24 | return DateTimeParseResult(
25 | start: parseResult.start,
26 | length: parseResult.length,
27 | text: parseResult.text,
28 | type: parseResult.type,
29 | data: parseResult.data,
30 | value: parseResult.value,
31 | resolutionStr: parseResult.resolutionStr,
32 | );
33 | }
34 |
35 | DateTimeParseResult({
36 | required super.start,
37 | required super.length,
38 | required super.text,
39 | super.type,
40 | super.data,
41 | super.value,
42 | super.resolutionStr,
43 | this.timexStr,
44 | super.metadata,
45 | });
46 |
47 | //TimexStr is only used in extractors related with date and time
48 | //It will output the TIMEX representation of a time string.
49 | String? timexStr;
50 | }
51 |
--------------------------------------------------------------------------------
/lib/src/date_time/date_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:intl/intl.dart';
2 | import 'package:nlp/src/date_time/constants.dart';
3 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
4 |
5 | class DateUtil {
6 | static final DateFormat DATE_TIME_FORMATTER = DateFormat("yyyy-MM-dd");
7 |
8 | static final List _monthValidDays = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
9 | static const _leapMonth = 2;
10 |
11 | static DateTime minValue() {
12 | return DateTime(1, 1, 1, 0, 0, 0, 0);
13 | }
14 |
15 | static int daysInMonth(int year, int month) {
16 | final daysInMonth = month == _leapMonth //
17 | ? LeapMonthDays(year)
18 | : _monthValidDays[month - 1];
19 | return daysInMonth;
20 | }
21 |
22 | static bool isValidDate(int year, int month, int day) {
23 | final yearStr = year.toString();
24 | final monthStr = month.toString().padLeft(2, '0');
25 | final dayStr = day.toString().padLeft(2, '0');
26 | return DateTime.tryParse('$yearStr-$monthStr-$dayStr') != null;
27 | }
28 |
29 | static DateTime createSafeDate(int year, int month, int day,
30 | [int hour = 0, int minute = 0, int second = 0, int millisecond = 0]) {
31 | if (year < 1 || year > 9999) {
32 | return minValue();
33 | }
34 |
35 | final daysInMonth = month == _leapMonth //
36 | ? LeapMonthDays(year)
37 | : _monthValidDays[month - 1];
38 |
39 | if (month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth) {
40 | return DateTime(year, month, day, hour, minute, second, millisecond);
41 | }
42 |
43 | return minValue();
44 | }
45 |
46 | static DateTime? tryParse(String? date) {
47 | if (date == null) {
48 | return null;
49 | }
50 |
51 | try {
52 | return DATE_TIME_FORMATTER.parse(date);
53 | } catch (exception) {
54 | return null;
55 | }
56 | }
57 |
58 | static bool validateMatch(
59 | NlpMatch match, String text, List dateRegExpList, RegExp rangeConnectorSymbolRegExp) {
60 | // If the match doesn't contains "year" part, it will not be ambiguous and it's a valid match
61 | final yearGroup = match.getGroup(DateTimeConstants.YearGroupName);
62 | var isValidMatch = yearGroup.value.isNotEmpty;
63 |
64 | if (!isValidMatch) {
65 | // If the "year" part is not at the end of the match, it's a valid match
66 | if (yearGroup.index + yearGroup.length != match.index + match.length) {
67 | isValidMatch = true;
68 | } else {
69 | var subText = text.substring(yearGroup.index);
70 |
71 | // If the following text (include the "year" part) doesn't start with a Date entity, it's a valid match
72 | if (!StartsWithBasicDate(subText, dateRegExpList)) {
73 | isValidMatch = true;
74 | } else {
75 | // If the following text (include the "year" part) starts with a Date entity, but the following text (doesn't include the "year" part) also starts with a valid Date entity, the current match is still valid
76 | // For example, "10-1-2018-10-2-2018". Match "10-1-2018" is valid because though "2018-10-2" a valid match (indicates the first year "2018" might belongs to the second Date entity), but "10-2-2018" is also a valid match.
77 | subText = text.substring(yearGroup.index + yearGroup.length).trim();
78 | subText = TrimStartRangeConnectorSymbols(subText, rangeConnectorSymbolRegExp);
79 | isValidMatch = StartsWithBasicDate(subText, dateRegExpList);
80 | }
81 | }
82 |
83 | // Expressions with mixed separators are not considered valid dates e.g. "30/4.85" (unless one is a comma "30/4, 2016")
84 | final dayGroup = match.getGroup(DateTimeConstants.DayGroupName);
85 | final monthGroup = match.getGroup(DateTimeConstants.MonthGroupName);
86 |
87 | if (dayGroup.value.isNotEmpty && monthGroup.value.isNotEmpty) {
88 | var noDateText =
89 | match.value.replaceAll(yearGroup.value, "").replaceAll(monthGroup.value, "").replaceAll(dayGroup.value, "");
90 |
91 | final weekDayGroup = match.getGroup(DateTimeConstants.WeekdayGroupName);
92 |
93 | noDateText = weekDayGroup.value.isNotEmpty ? noDateText.replaceAll(weekDayGroup.value, "") : noDateText;
94 | final separators = ['/', '\\', '-', '.'];
95 | final separatorCount = separators.fold(
96 | 0,
97 | (count, separator) => count + (noDateText.contains(separator) ? 1 : 0),
98 | );
99 |
100 | if (separatorCount > 1) {
101 | isValidMatch = false;
102 | }
103 | }
104 | }
105 |
106 | return isValidMatch;
107 | }
108 |
109 | static bool StartsWithBasicDate(String text, List dateRegexList) {
110 | for (final regex in dateRegexList) {
111 | var match = regex.matchBegin(text, true);
112 |
113 | if (match != null) {
114 | return true;
115 | }
116 | }
117 |
118 | return false;
119 | }
120 |
121 | static String TrimStartRangeConnectorSymbols(String text, RegExp rangeConnectorSymbolRegex) {
122 | final rangeConnectorSymbolMatches = RegExpComposer.getMatchesSimple(rangeConnectorSymbolRegex, text);
123 |
124 | for (final symbolMatch in rangeConnectorSymbolMatches) {
125 | var startSymbolLength = -1;
126 |
127 | if (symbolMatch.index == 0 && symbolMatch.length > startSymbolLength) {
128 | startSymbolLength = symbolMatch.length;
129 | }
130 |
131 | if (startSymbolLength > 0) {
132 | text = text.substring(startSymbolLength);
133 | }
134 | }
135 |
136 | return text.trim();
137 | }
138 |
139 | static bool TrimStartRangeConnectorSymbolsStartsWithBasicDate(String text, List dateRegexList) {
140 | for (final regex in dateRegexList) {
141 | var match = regex.matchBegin(text, true);
142 |
143 | if (match != null) {
144 | return true;
145 | }
146 | }
147 |
148 | return false;
149 | }
150 |
151 | static int LeapMonthDays(int year) {
152 | return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 ? 29 : 28;
153 | }
154 |
155 | const DateUtil._();
156 | }
157 |
--------------------------------------------------------------------------------
/lib/src/date_time/datetime_utility_configuration.dart:
--------------------------------------------------------------------------------
1 | abstract interface class IDateTimeUtilityConfiguration {
2 | RegExp agoRegExp();
3 |
4 | RegExp laterRegExp();
5 |
6 | RegExp inConnectorRegExp();
7 |
8 | RegExp sinceYearSuffixRegExp();
9 |
10 | RegExp withinNextPrefixRegExp();
11 |
12 | RegExp rangeUnitRegExp();
13 |
14 | RegExp timeUnitRegExp();
15 |
16 | RegExp dateUnitRegExp();
17 |
18 | RegExp amDescRegExp();
19 |
20 | RegExp pmDescRegExp();
21 |
22 | RegExp amPmDescRegExp();
23 |
24 | RegExp commonDatePrefixRegExp();
25 |
26 | RegExp rangePrefixRegExp();
27 |
28 | bool checkBothBeforeAfter();
29 | }
30 |
--------------------------------------------------------------------------------
/lib/src/date_time/english/english_date_time_extractor_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 | import 'package:nlp/src/date_time/base_date_extractor.dart';
3 | import 'package:nlp/src/date_time/base_datetime_extractor.dart';
4 | import 'package:nlp/src/date_time/date_time_extraction.dart';
5 | import 'package:nlp/src/date_time/datetime_utility_configuration.dart';
6 | import 'package:nlp/src/date_time/english/english_date_time_utility_configuration.dart';
7 | import 'package:nlp/src/date_time/english_date_extractor.dart';
8 | import 'package:nlp/src/date_time/english_date_time.dart';
9 | import 'package:nlp/src/date_time/english_date_time_parser.dart';
10 | import 'package:nlp/src/duration/duration.dart';
11 | import 'package:nlp/src/duration/duration_extractor.dart';
12 | import 'package:nlp/src/numbers/numbers.dart';
13 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
14 |
15 | class EnglishDateTimeExtractorConfiguration extends BaseOptionsConfiguration
16 | implements IDateTimeExtractorConfiguration {
17 | EnglishDateTimeExtractorConfiguration(this.config, [super.options = DateTimeOptions.None])
18 | : _datePointExtractor = BaseDateExtractor(EnglishDateExtractorConfiguration(config));
19 |
20 | final ICommonDateTimeParserConfiguration config;
21 |
22 | @override
23 | RegExp DateNumberConnectorRegExp() =>
24 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.DateNumberConnectorRegex);
25 |
26 | final IDateExtractor _datePointExtractor;
27 | @override
28 | IDateExtractor DatePointExtractor() => _datePointExtractor;
29 |
30 | @override
31 | IDateTimeExtractor DurationExtractor() => config.durationExtractor;
32 |
33 | @override
34 | IDateTimeExtractor HolidayExtractor() => config.holidayExtractor;
35 |
36 | @override
37 | IExtractor integerExtractor() => IntegerExtractor.getInstance();
38 |
39 | @override
40 | bool IsConnector(String text) {
41 | text = text.trim();
42 | return text.isEmpty ||
43 | RegExp(EnglishDateTime.PrepositionRegex).hasMatch(text) ||
44 | RegExp(EnglishDateTime.ConnectorRegex).hasMatch(text);
45 | }
46 |
47 | @override
48 | RegExp NowRegExp() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.NowRegex);
49 |
50 | @override
51 | RegExp NumberAsTimeRegExp() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.NumberAsTimeRegex);
52 |
53 | @override
54 | RegExp SimpleTimeOfTodayAfterRegExp() =>
55 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SimpleTimeOfTodayAfterRegex);
56 |
57 | @override
58 | RegExp SimpleTimeOfTodayBeforeRegExp() =>
59 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SimpleTimeOfTodayBeforeRegex);
60 |
61 | @override
62 | RegExp SpecificEndOfRegExp() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SpecificEndOfRegex);
63 |
64 | @override
65 | RegExp SuffixAfterRegExp() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SuffixAfterRegex);
66 |
67 | @override
68 | RegExp SuffixRegExp() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SuffixRegex);
69 |
70 | @override
71 | RegExp TimeOfDayRegExp() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeOfDayRegex);
72 |
73 | @override
74 | RegExp TimeOfTodayAfterRegExp() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeOfTodayAfterRegex);
75 |
76 | @override
77 | RegExp TimeOfTodayBeforeRegExp() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeOfTodayBeforeRegex);
78 |
79 | @override
80 | IDateTimeExtractor TimePointExtractor() => config.timeExtractor;
81 |
82 | @override
83 | RegExp UnitRegExp() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeUnitRegex);
84 |
85 | @override
86 | RegExp UnspecificEndOfRegExp() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.UnspecificEndOfRegex);
87 |
88 | @override
89 | IDateTimeUtilityConfiguration UtilityConfiguration() => EnglishDatetimeUtilityConfiguration();
90 |
91 | @override
92 | RegExp YearRegExp() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.YearRegex);
93 |
94 | @override
95 | RegExp YearSuffix() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.YearSuffix);
96 | }
97 |
--------------------------------------------------------------------------------
/lib/src/date_time/english/english_date_time_parser_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 | import 'package:nlp/src/core/parser.dart';
3 | import 'package:nlp/src/date_time/base_datetime_options_configuration.dart';
4 | import 'package:nlp/src/date_time/base_datetime_parser.dart';
5 | import 'package:nlp/src/date_time/constants.dart';
6 | import 'package:nlp/src/date_time/date_time_extraction.dart';
7 | import 'package:nlp/src/date_time/date_time_parsing.dart';
8 | import 'package:nlp/src/date_time/datetime_utility_configuration.dart';
9 | import 'package:nlp/src/date_time/english/english_date_time_utility_configuration.dart';
10 | import 'package:nlp/src/date_time/english/english_holiday_extractor_configuration.dart';
11 | import 'package:nlp/src/date_time/english_date_time.dart';
12 | import 'package:nlp/src/date_time/english_date_time_parser.dart';
13 | import 'package:nlp/src/duration/duration.dart';
14 | import 'package:nlp/src/numbers/numbers.dart';
15 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
16 |
17 | class EnglishDateTimeParserConfiguration extends BaseDateTimeOptionsConfiguration
18 | implements IDateTimeParserConfiguration {
19 | EnglishDateTimeParserConfiguration(this.config, {super.options = DateTimeOptions.None});
20 |
21 | final ICommonDateTimeParserConfiguration config;
22 |
23 | @override
24 | RegExp AMTimeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.AMTimeRegex);
25 |
26 | @override
27 | IExtractor CardinalExtractor() => config.cardinalExtractor;
28 |
29 | @override
30 | bool ContainsAmbiguousToken(String text, String matchedText) => false;
31 |
32 | @override
33 | IDateExtractor DateExtractor() => config.dateExtractor;
34 |
35 | @override
36 | RegExp DateNumberConnectorRegex() =>
37 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.DateNumberConnectorRegex);
38 |
39 | @override
40 | IDateTimeParser DateParser() => config.dateParser;
41 |
42 | @override
43 | IDateTimeExtractor DurationExtractor() => config.durationExtractor;
44 |
45 | @override
46 | IDateTimeParser DurationParser() => config.durationParser;
47 |
48 | @override
49 | int GetHour(String text, int hour) {
50 | int result = hour;
51 |
52 | var trimmedText = text.trim();
53 |
54 | if (AMTimeRegex().matchEnd(trimmedText, true) != null && hour >= DateTimeConstants.HalfDayHourCount) {
55 | result -= DateTimeConstants.HalfDayHourCount;
56 | } else if (AMTimeRegex().matchEnd(trimmedText, true) == null &&
57 | hour < DateTimeConstants.HalfDayHourCount &&
58 | !(NightTimeRegex().matchEnd(trimmedText, true) != null && hour < DateTimeConstants.QuarterDayHourCount)) {
59 | result += DateTimeConstants.HalfDayHourCount;
60 | }
61 |
62 | return result;
63 | }
64 |
65 | @override
66 | String? GetMatchedNowTimex(String text) {
67 | var trimmedText = text.trim();
68 |
69 | if (NowTimeRegex().matchEnd(trimmedText, true) != null) {
70 | return "PRESENT_REF";
71 | } else if (RecentlyTimeRegex().matchExact(trimmedText, true) != null) {
72 | return "PAST_REF";
73 | } else if (AsapTimeRegex().matchExact(trimmedText, true) != null) {
74 | return "FUTURE_REF";
75 | } else {
76 | return null;
77 | }
78 | }
79 |
80 | @override
81 | int GetSwiftDay(String text) {
82 | var trimmedText = text.trim();
83 |
84 | var swift = 0;
85 | if (NextPrefixRegex().matchBegin(trimmedText, true) != null) {
86 | swift = 1;
87 | } else if (PreviousPrefixRegex().matchBegin(trimmedText, true) != null) {
88 | swift = -1;
89 | }
90 |
91 | return swift;
92 | }
93 |
94 | @override
95 | IDateTimeExtractor HolidayExtractor() => config.holidayExtractor;
96 |
97 | @override
98 | IDateTimeParser HolidayTimeParser() {
99 | // TODO: implement HolidayTimeParser
100 | throw UnimplementedError();
101 | }
102 |
103 | @override
104 | IExtractor integerExtractor() => IntegerExtractor.getInstance();
105 |
106 | @override
107 | RegExp NowRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.NowRegex);
108 |
109 | @override
110 | IParser NumberParser() => config.numberParser;
111 |
112 | @override
113 | Map Numbers() => config.numbers;
114 |
115 | @override
116 | RegExp PMTimeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PMTimeRegex);
117 |
118 | @override
119 | RegExp SimpleTimeOfTodayAfterRegex() =>
120 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SimpleTimeOfTodayAfterRegex);
121 |
122 | @override
123 | RegExp SimpleTimeOfTodayBeforeRegex() =>
124 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SimpleTimeOfTodayBeforeRegex);
125 |
126 | @override
127 | RegExp SpecificEndOfRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SpecificEndOfRegex);
128 |
129 | @override
130 | RegExp SpecificTimeOfDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SpecificTimeOfDayRegex);
131 |
132 | @override
133 | IDateTimeExtractor TimeExtractor() => config.timeExtractor;
134 |
135 | @override
136 | IDateTimeParser TimeParser() => config.timeParser;
137 |
138 | @override
139 | String TokenBeforeDate() => EnglishDateTime.TokenBeforeDate;
140 |
141 | @override
142 | String TokenBeforeTime() => EnglishDateTime.TokenBeforeTime;
143 |
144 | @override
145 | Map UnitMap() => config.unitMap;
146 |
147 | @override
148 | RegExp UnitRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeUnitRegex);
149 |
150 | @override
151 | RegExp UnspecificEndOfRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.UnspecificEndOfRegex);
152 |
153 | @override
154 | IDateTimeUtilityConfiguration UtilityConfiguration() => EnglishDatetimeUtilityConfiguration();
155 |
156 | @override
157 | RegExp YearRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.YearRegex);
158 |
159 | RegExp NightTimeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.NightTimeRegex);
160 |
161 | RegExp NowTimeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.NowTimeRegex);
162 |
163 | RegExp RecentlyTimeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.RecentlyTimeRegex);
164 |
165 | RegExp AsapTimeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.AsapTimeRegex);
166 |
167 | RegExp NextPrefixRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.NextPrefixRegex);
168 |
169 | RegExp PreviousPrefixRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PreviousPrefixRegex);
170 | }
171 |
--------------------------------------------------------------------------------
/lib/src/date_time/english/english_date_time_period_extractor_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 | import 'package:nlp/src/date_time/base_date_extractor.dart';
3 | import 'package:nlp/src/date_time/base_date_time_period_extractor.dart';
4 | import 'package:nlp/src/date_time/base_datetime_extractor.dart';
5 | import 'package:nlp/src/date_time/base_datetime_options_configuration.dart';
6 | import 'package:nlp/src/date_time/base_time_extractor.dart';
7 | import 'package:nlp/src/date_time/date_time_extraction.dart';
8 | import 'package:nlp/src/date_time/english/english_date_time_extractor_configuration.dart';
9 | import 'package:nlp/src/date_time/english/english_time_extractor_configuration.dart';
10 | import 'package:nlp/src/date_time/english_date_extractor.dart';
11 | import 'package:nlp/src/date_time/english_date_time.dart';
12 | import 'package:nlp/src/date_time/english_date_time_parser.dart';
13 | import 'package:nlp/src/duration/duration.dart';
14 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
15 |
16 | class EnglishDateTimePeriodExtractorConfiguration extends BaseDateTimeOptionsConfiguration
17 | implements IDateTimePeriodExtractorConfiguration {
18 | EnglishDateTimePeriodExtractorConfiguration(this.config, {super.options = DateTimeOptions.None});
19 |
20 | final ICommonDateTimeParserConfiguration config;
21 |
22 | @override
23 | RegExp AfterRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.AfterRegex);
24 |
25 | @override
26 | RegExp AmDescRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.AmDescRegex);
27 |
28 | @override
29 | RegExp BeforeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.BeforeRegex);
30 |
31 | @override
32 | IExtractor CardinalExtractor() => config.cardinalExtractor;
33 |
34 | @override
35 | bool CheckBothBeforeAfter() => EnglishDateTime.CheckBothBeforeAfter;
36 |
37 | @override
38 | RegExp DateUnitRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.DateUnitRegex);
39 |
40 | @override
41 | IDateTimeExtractor DurationExtractor() => config.durationExtractor;
42 |
43 | @override
44 | RegExp FollowedUnit() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.FollowedDateUnit);
45 |
46 | @override
47 | RegExp FutureSuffixRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.FutureSuffixRegex);
48 |
49 | @override
50 | RegExp GeneralEndingRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.GeneralEndingRegex);
51 |
52 | @override
53 | int? GetBetweenTokenIndex(String text) {
54 | var betweenMatch = BetweenTokenRegex().firstMatch(text);
55 | if (betweenMatch != null) {
56 | return betweenMatch.start;
57 | }
58 |
59 | return null;
60 | }
61 |
62 | @override
63 | int? GetFromTokenIndex(String text) {
64 | var fromMatch = FromTokenRegex().firstMatch(text);
65 | if (fromMatch != null) {
66 | return fromMatch.start;
67 | }
68 |
69 | return null;
70 | }
71 |
72 | @override
73 | bool HasConnectorToken(String text) {
74 | return RangeConnectorRegex().matchExact(text, true) != null;
75 | }
76 |
77 | @override
78 | IDateTimeExtractor HolidayExtractor() => config.holidayExtractor;
79 |
80 | @override
81 | RegExp MiddlePauseRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.MiddlePauseRegex);
82 |
83 | @override
84 | RegExp NextPrefixRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.NextPrefixRegex);
85 |
86 | @override
87 | RegExp NumberCombinedWithUnit() =>
88 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.NumberCombinedWithDateUnit);
89 |
90 | @override
91 | RegExp PeriodTimeOfDayWithDateRegex() =>
92 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PeriodTimeOfDayWithDateRegex);
93 |
94 | @override
95 | RegExp PmDescRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PmDescRegex);
96 |
97 | @override
98 | RegExp PrefixDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PrefixDayRegex);
99 |
100 | @override
101 | RegExp PrepositionRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PrepositionRegex);
102 |
103 | @override
104 | RegExp PreviousPrefixRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PreviousPrefixRegex);
105 |
106 | @override
107 | RegExp RelativeTimeUnitRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.RelativeTimeUnitRegex);
108 |
109 | @override
110 | RegExp RestOfDateTimeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.RestOfDateTimeRegex);
111 |
112 | @override
113 | List SimpleCasesRegex() => [
114 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PureNumFromTo),
115 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PureNumBetweenAnd),
116 | ];
117 |
118 | @override
119 | IDateTimeExtractor SingleDateExtractor() => BaseDateExtractor(EnglishDateExtractorConfiguration(config));
120 |
121 | @override
122 | IDateTimeExtractor SingleDateTimeExtractor() => BaseDateTimeExtractor(EnglishDateTimeExtractorConfiguration(config));
123 |
124 | @override
125 | IDateTimeExtractor SingleTimeExtractor() => BaseTimeExtractor(EnglishTimeExtractorConfiguration(config));
126 |
127 | @override
128 | RegExp SpecificTimeOfDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SpecificTimeOfDayRegex);
129 |
130 | @override
131 | RegExp SuffixRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SuffixRegex);
132 |
133 | @override
134 | RegExp TasksmodeMealTimeofDayRegex() =>
135 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TasksmodeMealTimeofDayRegex);
136 |
137 | @override
138 | RegExp TillRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TillRegex);
139 |
140 | @override
141 | RegExp TimeOfDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeOfDayRegex);
142 |
143 | @override
144 | IDateTimeExtractor TimePeriodExtractor() => config.timePeriodExtractor;
145 |
146 | @override
147 | RegExp TimeUnitRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeUnitRegex);
148 |
149 | @override
150 | IDateTimeExtractor TimeZoneExtractor() {
151 | // TODO: implement TimeZoneExtractor
152 | throw UnimplementedError();
153 | }
154 |
155 | @override
156 | String TokenBeforeDate() => EnglishDateTime.TokenBeforeDate;
157 |
158 | @override
159 | RegExp WeekDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.WeekDayRegex);
160 |
161 | @override
162 | RegExp WithinNextPrefixRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.WithinNextPrefixRegex);
163 |
164 | RegExp BetweenTokenRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.BetweenTokenRegex);
165 |
166 | RegExp FromTokenRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.FromRegex);
167 |
168 | RegExp RangeConnectorRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.RangeConnectorRegex);
169 | }
170 |
--------------------------------------------------------------------------------
/lib/src/date_time/english/english_date_time_utility_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/date_time/base_datetime_utility_configuration.dart';
2 | import 'package:nlp/src/date_time/english_date_time.dart';
3 |
4 | class EnglishDatetimeUtilityConfiguration extends BaseDatetimeUtilityConfiguration {
5 | EnglishDatetimeUtilityConfiguration()
6 | : super(
7 | EnglishDateTime.AgoRegex,
8 | EnglishDateTime.LaterRegex,
9 | EnglishDateTime.InConnectorRegex,
10 | EnglishDateTime.SinceYearSuffixRegex,
11 | EnglishDateTime.WithinNextPrefixRegex,
12 | EnglishDateTime.AmDescRegex,
13 | EnglishDateTime.PmDescRegex,
14 | EnglishDateTime.AmPmDescRegex,
15 | EnglishDateTime.RangeUnitRegex,
16 | EnglishDateTime.TimeUnitRegex,
17 | EnglishDateTime.DateUnitRegex,
18 | EnglishDateTime.CommonDatePrefixRegex,
19 | EnglishDateTime.RangePrefixRegex,
20 | EnglishDateTime.CheckBothBeforeAfter,
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/lib/src/date_time/english/english_holiday_extractor_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/date_time/base_datetime_options_configuration.dart';
2 | import 'package:nlp/src/date_time/english_date_time.dart';
3 | import 'package:nlp/src/date_time/holiday_extractor_configuration.dart';
4 | import 'package:nlp/src/duration/duration.dart';
5 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
6 |
7 | class EnglishHolidayExtractorConfiguration extends BaseDateTimeOptionsConfiguration
8 | implements IHolidayExtractorConfiguration {
9 | static RegExp H = RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.HolidayRegex);
10 |
11 | EnglishHolidayExtractorConfiguration([DateTimeOptions options = DateTimeOptions.None])
12 | : _holidayRegexes = [H],
13 | super(options: options);
14 |
15 | final List _holidayRegexes;
16 | @override
17 | List holidayRegexes() => _holidayRegexes;
18 | }
19 |
--------------------------------------------------------------------------------
/lib/src/date_time/english/english_set_extractor_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/date_time/base_datetime_options_configuration.dart';
2 | import 'package:nlp/src/date_time/date_time_extraction.dart';
3 | import 'package:nlp/src/date_time/english_date_time.dart';
4 | import 'package:nlp/src/date_time/english_date_time_parser.dart';
5 | import 'package:nlp/src/duration/duration.dart';
6 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
7 | import 'package:nlp/src/date_time/base_set_extractor.dart';
8 | import 'package:nlp/src/date_time/set_handler.dart';
9 |
10 | class EnglishSetExtractorConfiguration extends BaseDateTimeOptionsConfiguration implements ISetExtractorConfiguration {
11 | EnglishSetExtractorConfiguration(this.config, {super.options = DateTimeOptions.None});
12 |
13 | final ICommonDateTimeParserConfiguration config;
14 |
15 | @override
16 | RegExp BeforeEachDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.DayRegex);
17 |
18 | @override
19 | bool CheckBothBeforeAfter() => EnglishDateTime.CheckBothBeforeAfter;
20 |
21 | @override
22 | IDateExtractor DateExtractor() => config.dateExtractor;
23 |
24 | @override
25 | IDateTimeExtractor DatePeriodExtractor() => config.datePeriodExtractor;
26 |
27 | @override
28 | IDateTimeExtractor DateTimeExtractor() => config.dateTimeExtractor;
29 |
30 | @override
31 | IDateTimeExtractor DateTimePeriodExtractor() => config.dateTimePeriodExtractor;
32 |
33 | @override
34 | IDateTimeExtractor DurationExtractor() => config.durationExtractor;
35 |
36 | @override
37 | RegExp EachDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.EachDayRegex);
38 |
39 | @override
40 | RegExp EachPrefixRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.EachPrefixRegex);
41 |
42 | @override
43 | RegExp EachUnitRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.EachUnitRegex);
44 |
45 | @override
46 | RegExp LastRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.FirstLastRegex);
47 |
48 | @override
49 | RegExp PeriodicRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PeriodicRegex);
50 |
51 | @override
52 | RegExp SetEachRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SetEachRegex);
53 |
54 | @override
55 | RegExp SetWeekDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SetWeekDayRegex);
56 |
57 | @override
58 | IDateTimeExtractor TimeExtractor() => config.timeExtractor;
59 |
60 | @override
61 | IDateTimeExtractor TimePeriodExtractor() => config.timePeriodExtractor;
62 |
63 | @override
64 | (String, int) WeekDayGroupMatchTuple(NlpMatch match) => SetHandler.WeekDayGroupMatchTuple(match);
65 | }
66 |
--------------------------------------------------------------------------------
/lib/src/date_time/english/english_set_parser_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/timex_utility.dart';
2 | import 'package:nlp/src/date_time/base_datetime_options_configuration.dart';
3 | import 'package:nlp/src/date_time/base_set_parser.dart';
4 | import 'package:nlp/src/date_time/date_time_extraction.dart';
5 | import 'package:nlp/src/date_time/date_time_parsing.dart';
6 | import 'package:nlp/src/date_time/english_date_time.dart';
7 | import 'package:nlp/src/date_time/english_date_time_parser.dart';
8 | import 'package:nlp/src/date_time/set_handler.dart';
9 | import 'package:nlp/src/date_time/task_mode_set_handler.dart';
10 | import 'package:nlp/src/duration/duration.dart';
11 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
12 |
13 | class EnglishSetParserConfiguration extends BaseDateTimeOptionsConfiguration implements ISetParserConfiguration {
14 | EnglishSetParserConfiguration(this.config, {super.options = DateTimeOptions.None});
15 |
16 | final ICommonDateTimeParserConfiguration config;
17 |
18 | @override
19 | IDateExtractor DateExtractor() => config.dateExtractor;
20 |
21 | @override
22 | IDateTimeParser DateParser() => config.dateParser;
23 |
24 | @override
25 | IDateTimeExtractor DatePeriodExtractor() => config.datePeriodExtractor;
26 |
27 | @override
28 | IDateTimeParser DatePeriodParser() => config.datePeriodParser;
29 |
30 | @override
31 | IDateTimeExtractor DateTimeExtractor() => config.dateTimeExtractor;
32 |
33 | @override
34 | IDateTimeParser DateTimeParser() => config.dateTimeParser;
35 |
36 | @override
37 | IDateTimeExtractor DateTimePeriodExtractor() => config.dateTimePeriodExtractor;
38 |
39 | @override
40 | IDateTimeParser DateTimePeriodParser() => config.dateTimePeriodParser;
41 |
42 | @override
43 | IDateTimeExtractor DurationExtractor() => config.durationExtractor;
44 |
45 | @override
46 | IDateTimeParser DurationParser() => config.durationParser;
47 |
48 | @override
49 | RegExp EachDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.EachDayRegex);
50 |
51 | @override
52 | RegExp EachPrefixRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.EachPrefixRegex);
53 |
54 | @override
55 | RegExp EachUnitRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.EachUnitRegex);
56 |
57 | @override
58 | String? GetMatchedDailyTimex(String text) {
59 | var trimmedText = text.trim();
60 | String? timex;
61 |
62 | double durationLength = 1; // Default value
63 | double multiplier = 1;
64 | String durationType;
65 |
66 | if (DoubleMultiplierRegex().hasMatch(trimmedText)) {
67 | multiplier = 2;
68 | } else if (HalfMultiplierRegex().hasMatch(trimmedText)) {
69 | multiplier = 0.5;
70 | }
71 |
72 | if (WeekDayTypeRegex().hasMatch(trimmedText)) {
73 | durationType = EnglishDateTime.UnitMap["weekday"]!;
74 | } else if (DayTypeRegex().hasMatch(trimmedText)) {
75 | durationType = EnglishDateTime.UnitMap["day"]!;
76 | } else if (WeekTypeRegex().hasMatch(trimmedText)) {
77 | durationType = EnglishDateTime.UnitMap["week"]!;
78 | } else if (WeekendTypeRegex().hasMatch(trimmedText)) {
79 | durationType = EnglishDateTime.UnitMap["weekend"]!;
80 | } else if (FortNightRegex().hasMatch(trimmedText)) {
81 | durationLength = 2;
82 | durationType = EnglishDateTime.UnitMap["week"]!;
83 | } else if (MonthTypeRegex().hasMatch(trimmedText)) {
84 | durationType = EnglishDateTime.UnitMap["m"]!;
85 | } else if (QuarterTypeRegex().hasMatch(trimmedText)) {
86 | durationLength = 3;
87 | durationType = EnglishDateTime.UnitMap["m"]!;
88 | } else if (YearTypeRegex().hasMatch(trimmedText)) {
89 | durationType = EnglishDateTime.UnitMap["y"]!;
90 | } else {
91 | return null;
92 | }
93 |
94 | timex = TimexUtility.GenerateSetTimex(durationType, durationLength, multiplier);
95 |
96 | return timex;
97 | }
98 |
99 | @override
100 | String? GetMatchedUnitTimex(String text) {
101 | return GetMatchedDailyTimex(text);
102 | }
103 |
104 | @override
105 | RegExp PeriodicRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PeriodicRegex);
106 |
107 | @override
108 | String ReplaceValueInTextWithFutTerm(String text, String value) {
109 | return TasksModeSetHandler.ReplaceValueInTextWithFutTerm(text, value, EnglishDateTime.FutureTerms);
110 | }
111 |
112 | @override
113 | RegExp SetEachRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SetEachRegex);
114 |
115 | @override
116 | RegExp SetWeekDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SetWeekDayRegex);
117 |
118 | @override
119 | IDateTimeExtractor TimeExtractor() => config.timeExtractor;
120 |
121 | @override
122 | IDateTimeParser TimeParser() => config.timeParser;
123 |
124 | @override
125 | IDateTimeExtractor TimePeriodExtractor() => config.timePeriodExtractor;
126 |
127 | @override
128 | IDateTimeParser TimePeriodParser() => config.timePeriodParser;
129 |
130 | @override
131 | Map UnitMap() => config.unitMap;
132 |
133 | @override
134 | String WeekDayGroupMatchString(NlpMatch match) {
135 | return SetHandler.WeekDayGroupMatchString(match);
136 | }
137 |
138 | RegExp DoubleMultiplierRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.DoubleMultiplierRegex);
139 |
140 | RegExp HalfMultiplierRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.HalfMultiplierRegex);
141 |
142 | RegExp YearTypeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.YearTypeRegex);
143 |
144 | RegExp MonthTypeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.MonthTypeRegex);
145 |
146 | RegExp QuarterTypeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.QuarterTypeRegex);
147 |
148 | RegExp WeekTypeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.WeekTypeRegex);
149 |
150 | RegExp WeekendTypeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.WeekendTypeRegex);
151 |
152 | RegExp WeekDayTypeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.WeekDayTypeRegex);
153 |
154 | RegExp DayTypeRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.DayTypeRegex);
155 |
156 | RegExp FortNightRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.FortNightRegex);
157 | }
158 |
--------------------------------------------------------------------------------
/lib/src/date_time/english/english_time_extractor_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/definition_loader.dart';
2 | import 'package:nlp/src/date_time/base_datetime_options_configuration.dart';
3 | import 'package:nlp/src/date_time/base_time_extractor.dart';
4 | import 'package:nlp/src/date_time/date_time_extraction.dart';
5 | import 'package:nlp/src/date_time/english_date_time.dart';
6 | import 'package:nlp/src/date_time/english_date_time_parser.dart';
7 | import 'package:nlp/src/duration/duration.dart';
8 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
9 |
10 | class EnglishTimeExtractorConfiguration extends BaseDateTimeOptionsConfiguration
11 | implements ITimeExtractorConfiguration {
12 | EnglishTimeExtractorConfiguration(this.config, {super.options = DateTimeOptions.None});
13 |
14 | final ICommonDateTimeParserConfiguration config;
15 |
16 | @override
17 | Map AmbiguityFiltersDict() =>
18 | DefinitionLoader.LoadAmbiguityFilters(EnglishDateTime.AmbiguityDurationFiltersDict);
19 |
20 | @override
21 | RegExp AtRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.AtRegex);
22 |
23 | @override
24 | RegExp IshRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.IshRegex);
25 |
26 | @override
27 | RegExp TimeBeforeAfterRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeBeforeAfterRegex);
28 |
29 | @override
30 | List TimeRegexList() => [
31 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeRegex1),
32 |
33 | // (three min past)? 3:00(:00)? (pm)?
34 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeRegex2),
35 |
36 | // (three min past)? 3.00 (pm)
37 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeRegex3),
38 |
39 | // (three min past) (five thirty|seven|7|7:00(:00)?) (pm)? (in the night)
40 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeRegex4),
41 |
42 | // (three min past) (five thirty|seven|7|7:00(:00)?) (pm)?
43 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeRegex5),
44 |
45 | // (five thirty|seven|7|7:00(:00)?) (pm)? (in the night)
46 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeRegex6),
47 |
48 | // (in the night) at? (five thirty|seven|7|7:00(:00)?) (pm)?
49 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeRegex7),
50 |
51 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeRegex9),
52 |
53 | // (three min past)? 3h00 (pm)?
54 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeRegex10),
55 |
56 | // at 2.30, "at" prefix is required here
57 | // 3.30pm, "am/pm" suffix is required here
58 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeRegex11),
59 |
60 | // 340pm
61 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.ConnectNumRegex),
62 | ];
63 |
64 | @override
65 | RegExp TimeTokenPrefix() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeTokenPrefix);
66 |
67 | @override
68 | IDateTimeExtractor TimeZoneExtractor() {
69 | // TODO: implement TimeZoneExtractor
70 | throw UnimplementedError();
71 | }
72 |
73 | static RegExp LessThanOneHour() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.LessThanOneHour);
74 | }
75 |
--------------------------------------------------------------------------------
/lib/src/date_time/english/english_time_parser_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/date_time/base_datetime_options_configuration.dart';
2 | import 'package:nlp/src/date_time/base_time_parser.dart';
3 | import 'package:nlp/src/date_time/constants.dart';
4 | import 'package:nlp/src/date_time/date_time_parsing.dart';
5 | import 'package:nlp/src/date_time/datetime_utility_configuration.dart';
6 | import 'package:nlp/src/date_time/english/english_date_time_utility_configuration.dart';
7 | import 'package:nlp/src/date_time/english/english_time_extractor_configuration.dart';
8 | import 'package:nlp/src/date_time/english_date_time.dart';
9 | import 'package:nlp/src/date_time/english_date_time_parser.dart';
10 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
11 |
12 | class EnglishTimeParserConfiguration extends BaseDateTimeOptionsConfiguration implements ITimeParserConfiguration {
13 | EnglishTimeParserConfiguration(this.config, {required super.options});
14 |
15 | final ICommonDateTimeParserConfiguration config;
16 |
17 | @override
18 | (int hour, int min, bool hasMin) AdjustByPrefix(String prefix, int hour, int min, bool hasMin) {
19 | int deltaMin;
20 |
21 | var trimedPrefix = prefix.trim();
22 |
23 | if (HalfTokenRegex().hasMatch(trimedPrefix)) {
24 | deltaMin = 30;
25 | } else if (QuarterTokenRegex().hasMatch(trimedPrefix)) {
26 | deltaMin = 15;
27 | } else if (ThreeQuarterTokenRegex().hasMatch(trimedPrefix)) {
28 | deltaMin = 45;
29 | } else {
30 | var match =
31 | RegExpComposer.getMatchesSimple(EnglishTimeExtractorConfiguration.LessThanOneHour(), trimedPrefix).first;
32 | var minStr = match.getGroup("deltamin").value;
33 | if (minStr.isNotEmpty) {
34 | deltaMin = int.parse(minStr);
35 | } else {
36 | minStr = match.getGroup("deltaminnum").value;
37 | deltaMin = Numbers()[minStr]!;
38 | }
39 | }
40 |
41 | if (ToTokenRegex().hasMatch(trimedPrefix)) {
42 | deltaMin = -deltaMin;
43 | }
44 |
45 | int adjustedMin = min;
46 | int adjustedHour = hour;
47 |
48 | adjustedMin += deltaMin;
49 | if (adjustedMin < 0) {
50 | adjustedMin += 60;
51 | adjustedHour -= 1;
52 | }
53 |
54 | return (adjustedHour, adjustedMin, true);
55 | }
56 |
57 | @override
58 | (int hour, int min, bool hasMin, bool hasAm, bool hasPm) AdjustBySuffix(
59 | String suffix, int hour, int min, bool hasMin, bool hasAm, bool hasPm) {
60 | var deltaHour = 0;
61 | var match = RegExpComposer.matchExact(TimeSuffixFull(), suffix, true);
62 |
63 | bool adjustedHasAm = hasAm;
64 | bool adjustedHasPm = hasPm;
65 | bool adjustedHasMin = hasMin;
66 |
67 | int adjustedHour = hour;
68 | int adjustedMin = min;
69 |
70 | if (match != null) {
71 | var oclockStr = match.getGroup("oclock").value;
72 | if (oclockStr.isEmpty) {
73 | var matchAmStr = match.getGroup(DateTimeConstants.AmGroupName).value;
74 | if (matchAmStr.isNotEmpty) {
75 | if (hour >= DateTimeConstants.HalfDayHourCount) {
76 | deltaHour = -DateTimeConstants.HalfDayHourCount;
77 | } else {
78 | adjustedHasAm = true;
79 | }
80 | }
81 |
82 | var matchPmStr = match.getGroup(DateTimeConstants.PmGroupName).value;
83 | if (matchPmStr.isNotEmpty) {
84 | if (hour < DateTimeConstants.HalfDayHourCount) {
85 | deltaHour = DateTimeConstants.HalfDayHourCount;
86 | }
87 |
88 | if (LunchRegex().hasMatch(matchPmStr)) {
89 | if (hour >= 10 && hour <= DateTimeConstants.HalfDayHourCount) {
90 | deltaHour = 0;
91 | if (hour == DateTimeConstants.HalfDayHourCount) {
92 | adjustedHasPm = true;
93 | } else {
94 | adjustedHasAm = true;
95 | }
96 | } else {
97 | adjustedHasPm = true;
98 | }
99 | } else if (NightRegex().hasMatch(matchPmStr)) {
100 | if (hour <= 3 || hour == DateTimeConstants.HalfDayHourCount) {
101 | if (hour == DateTimeConstants.HalfDayHourCount) {
102 | adjustedHour = 0;
103 | }
104 |
105 | deltaHour = 0;
106 | adjustedHasAm = true;
107 | } else {
108 | adjustedHasPm = true;
109 | }
110 | } else {
111 | adjustedHasPm = true;
112 | }
113 | }
114 | }
115 | }
116 |
117 | adjustedHour = (adjustedHour + deltaHour) % 24;
118 |
119 | return (adjustedHour, adjustedMin, adjustedHasMin, adjustedHasAm, adjustedHasPm);
120 | }
121 |
122 | @override
123 | RegExp AtRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.AtRegex);
124 |
125 | @override
126 | Map Numbers() => config.numbers;
127 |
128 | @override
129 | List TimeRegexes() => EnglishTimeExtractorConfiguration(config).TimeRegexList();
130 |
131 | @override
132 | String TimeTokenPrefix() => EnglishDateTime.TimeTokenPrefix;
133 |
134 | @override
135 | IDateTimeParser TimeZoneParser() {
136 | // TODO: implement TimeZoneParser
137 | throw UnimplementedError();
138 | }
139 |
140 | @override
141 | IDateTimeUtilityConfiguration UtilityConfiguration() => EnglishDatetimeUtilityConfiguration();
142 |
143 | RegExp HalfTokenRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.HalfTokenRegex);
144 |
145 | RegExp QuarterTokenRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.QuarterTokenRegex);
146 |
147 | RegExp ThreeQuarterTokenRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.ThreeQuarterTokenRegex);
148 |
149 | RegExp ToTokenRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.ToTokenRegex);
150 |
151 | RegExp TimeSuffixFull() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeSuffixFull);
152 |
153 | RegExp LunchRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.LunchRegex);
154 |
155 | RegExp NightRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.NightRegex);
156 | }
157 |
--------------------------------------------------------------------------------
/lib/src/date_time/english/english_time_period_extractor_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/nlp.dart';
2 | import 'package:nlp/src/date_time/base_datetime_options_configuration.dart';
3 | import 'package:nlp/src/date_time/base_time_extractor.dart';
4 | import 'package:nlp/src/date_time/date_time_extraction.dart';
5 | import 'package:nlp/src/date_time/english/english_time_extractor_configuration.dart';
6 | import 'package:nlp/src/date_time/time_period_functions.dart';
7 |
8 | class EnglishTimePeriodExtractorConfiguration extends BaseDateTimeOptionsConfiguration
9 | implements ITimePeriodExtractorConfiguration {
10 | EnglishTimePeriodExtractorConfiguration(this.config, {super.options = DateTimeOptions.None});
11 |
12 | final ICommonDateTimeParserConfiguration config;
13 |
14 | @override
15 | List ApplyPotentialPeriodAmbiguityHotfix(String text, List timePeriodErs) =>
16 | TimePeriodFunctions.ApplyPotentialPeriodAmbiguityHotfix(text, timePeriodErs);
17 |
18 | @override
19 | bool CheckBothBeforeAfter() => EnglishDateTime.CheckBothBeforeAfter;
20 |
21 | @override
22 | RegExp GeneralEndingRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.GeneralEndingRegex);
23 |
24 | @override
25 | int? GetBetweenTokenIndex(String text) {
26 | if (text.endsWith("between")) {
27 | return text.lastIndexOf("between");
28 | }
29 |
30 | return null;
31 | }
32 |
33 | @override
34 | int? GetFromTokenIndex(String text) {
35 | if (text.endsWith("from")) {
36 | return text.lastIndexOf("from");
37 | }
38 |
39 | return null;
40 | }
41 |
42 | @override
43 | IExtractor integerExtractor() => IntegerExtractor.getInstance();
44 |
45 | @override
46 | bool IsConnectorToken(String text) {
47 | return text == "and";
48 | }
49 |
50 | @override
51 | List PureNumberRegex() => [PureNumFromTo(), PureNumBetweenAnd()];
52 |
53 | @override
54 | List SimpleCasesRegex() =>
55 | [PureNumFromTo(), PureNumBetweenAnd(), SpecificTimeFromTo(), SpecificTimeBetweenAnd()];
56 |
57 | @override
58 | IDateTimeExtractor SingleTimeExtractor() => BaseTimeExtractor(EnglishTimeExtractorConfiguration(config));
59 |
60 | @override
61 | RegExp TillRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TillRegex);
62 |
63 | @override
64 | RegExp TimeOfDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeOfDayRegex);
65 |
66 | @override
67 | IDateTimeExtractor TimeZoneExtractor() => BaseTimeZoneExtractor(EnglishTimeZoneExtractorConfiguration());
68 |
69 | @override
70 | String TokenBeforeDate() => EnglishDateTime.TokenBeforeDate;
71 |
72 | RegExp PureNumFromTo() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PureNumFromTo);
73 |
74 | RegExp PureNumBetweenAnd() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PureNumBetweenAnd);
75 |
76 | RegExp SpecificTimeFromTo() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SpecificTimeFromTo);
77 |
78 | RegExp SpecificTimeBetweenAnd() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SpecificTimeBetweenAnd);
79 | }
80 |
--------------------------------------------------------------------------------
/lib/src/date_time/english/english_time_period_parser_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 | import 'package:nlp/src/core/task_mode_processing.dart';
3 | import 'package:nlp/src/core/timex_utility.dart';
4 | import 'package:nlp/src/date_time/base_datetime_options_configuration.dart';
5 | import 'package:nlp/src/date_time/base_time_period_parser.dart';
6 | import 'package:nlp/src/date_time/constants.dart';
7 | import 'package:nlp/src/date_time/date_time_extraction.dart';
8 | import 'package:nlp/src/date_time/date_time_parsing.dart';
9 | import 'package:nlp/src/date_time/datetime_utility_configuration.dart';
10 | import 'package:nlp/src/date_time/english/english_date_time_utility_configuration.dart';
11 | import 'package:nlp/src/date_time/english_date_time.dart';
12 | import 'package:nlp/src/date_time/english_date_time_parser.dart';
13 | import 'package:nlp/src/duration/duration.dart';
14 | import 'package:nlp/src/numbers/numbers.dart';
15 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
16 |
17 | class EnglishTimePeriodParserConfiguration extends BaseDateTimeOptionsConfiguration
18 | implements ITimePeriodParserConfiguration {
19 | EnglishTimePeriodParserConfiguration(this.config, {super.options = DateTimeOptions.None});
20 |
21 | final ICommonDateTimeParserConfiguration config;
22 |
23 | @override
24 | RegExp GeneralEndingRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.GeneralEndingRegex);
25 |
26 | @override
27 | (String, int, int, int) GetMatchedTimeRange(String text) {
28 | var trimmedText = text.trim();
29 | if (trimmedText.endsWith("s")) {
30 | trimmedText = trimmedText.substring(0, trimmedText.length - 1);
31 | }
32 |
33 | int beginHour = 0;
34 | int endHour = 0;
35 | int endMin = 0;
36 | String timex = '';
37 |
38 | var timeOfDay = '';
39 | if (EnglishDateTime.MorningTermList.where((o) => trimmedText.endsWith(o)).isNotEmpty) {
40 | timeOfDay = DateTimeConstants.Morning;
41 | } else if (EnglishDateTime.AfternoonTermList.where((o) => trimmedText.endsWith(o)).isNotEmpty) {
42 | timeOfDay = DateTimeConstants.Afternoon;
43 | } else if (EnglishDateTime.EveningTermList.where((o) => trimmedText.endsWith(o)).isNotEmpty) {
44 | timeOfDay = DateTimeConstants.Evening;
45 | } else if (EnglishDateTime.DaytimeTermList.where((o) => trimmedText.endsWith(o)).isNotEmpty) {
46 | timeOfDay = DateTimeConstants.Daytime;
47 | } else if (EnglishDateTime.NighttimeTermList.where((o) => trimmedText.endsWith(o)).isNotEmpty) {
48 | timeOfDay = DateTimeConstants.Nighttime;
49 | } else if (EnglishDateTime.NightTermList.where((o) => trimmedText.endsWith(o)).isNotEmpty) {
50 | timeOfDay = DateTimeConstants.Night;
51 | } else if (EnglishDateTime.BusinessHourSplitStrings.where((o) => trimmedText.contains(o)).length ==
52 | EnglishDateTime.BusinessHourSplitStrings.length) {
53 | timeOfDay = DateTimeConstants.BusinessHour;
54 | } else if (EnglishDateTime.MealtimeBreakfastTermList.where((o) => trimmedText.contains(o)).isNotEmpty) {
55 | timeOfDay = DateTimeConstants.MealtimeBreakfast;
56 | } else if (EnglishDateTime.MealtimeBrunchTermList.where((o) => trimmedText.contains(o)).isNotEmpty) {
57 | timeOfDay = DateTimeConstants.MealtimeBrunch;
58 | } else if (EnglishDateTime.MealtimeLunchTermList.where((o) => trimmedText.contains(o)).isNotEmpty) {
59 | timeOfDay = DateTimeConstants.MealtimeLunch;
60 | } else if (EnglishDateTime.MealtimeDinnerTermList.where((o) => trimmedText.contains(o)).isNotEmpty) {
61 | timeOfDay = DateTimeConstants.MealtimeDinner;
62 | } else {
63 | return ('', 0, 0, 0);
64 | }
65 |
66 | var parseResult = TimexUtility.ResolveTimeOfDay(timeOfDay);
67 | timex = parseResult.Timex;
68 | beginHour = parseResult.BeginHour;
69 | endHour = parseResult.EndHour;
70 | endMin = parseResult.EndMin;
71 |
72 | if (options.match(DateTimeOptions.TasksMode)) {
73 | beginHour = 0;
74 | endHour = 0;
75 | endMin = 0;
76 | parseResult = TasksModeProcessing.TasksModeResolveTimeOfDay(timeOfDay);
77 | timex = parseResult.Timex;
78 | beginHour = parseResult.BeginHour;
79 | endHour = parseResult.EndHour;
80 | endMin = parseResult.EndMin;
81 | }
82 |
83 | return (timex, beginHour, endHour, endMin);
84 | }
85 |
86 | @override
87 | IExtractor integerExtractor() => IntegerExtractor.getInstance();
88 |
89 | @override
90 | Map Numbers() => config.numbers;
91 |
92 | @override
93 | RegExp PureNumberBetweenAndRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PureNumBetweenAnd);
94 |
95 | @override
96 | RegExp PureNumberFromToRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.PureNumFromTo);
97 |
98 | @override
99 | RegExp SpecificTimeBetweenAndRegex() =>
100 | RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SpecificTimeBetweenAnd);
101 |
102 | @override
103 | RegExp SpecificTimeFromToRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.SpecificTimeFromTo);
104 |
105 | @override
106 | RegExp TillRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TillRegex);
107 |
108 | @override
109 | IDateTimeExtractor TimeExtractor() => config.timeExtractor;
110 |
111 | @override
112 | RegExp TimeOfDayRegex() => RegExpComposer.sanitizeGroupsAndCompile(EnglishDateTime.TimeOfDayRegex);
113 |
114 | @override
115 | IDateTimeParser TimeParser() => config.timeParser;
116 |
117 | @override
118 | IDateTimeParser TimeZoneParser() {
119 | // TODO: implement TimeZoneParser
120 | throw UnimplementedError();
121 | }
122 |
123 | @override
124 | IDateTimeUtilityConfiguration UtilityConfiguration() => EnglishDatetimeUtilityConfiguration();
125 | }
126 |
--------------------------------------------------------------------------------
/lib/src/date_time/extract_result_extension.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 |
3 | class ExtractResultExtension {
4 | static List FilterAmbiguity(
5 | List extractResults, String text, Map ambiguityFiltersDict) {
6 | for (var regex in ambiguityFiltersDict.entries) {
7 | for (int i = extractResults.length - 1; i >= 0; i--) {
8 | var er = extractResults[i];
9 | if (regex.key.hasMatch(er.text)) {
10 | var matches = regex.value.allMatches(text);
11 | if (matches.any((match) => match.start < er.start + er.length && match.end > er.start)) {
12 | extractResults.removeAt(i);
13 | }
14 | }
15 | }
16 | }
17 |
18 | return extractResults;
19 | }
20 |
21 | static List MergeAllResults(List res) {
22 | var ret = [];
23 | final results = [...res];
24 |
25 | results.sort((a, b) => a.start - b.start);
26 | results.sort((a, b) => b.length - a.length);
27 |
28 | var mergedResults = [];
29 | for (var result in results) {
30 | if (result != null) {
31 | bool shouldAdd = true;
32 | var resStart = result.start;
33 | var resEnd = resStart + result.length;
34 | for (var index = 0; index < mergedResults.length && shouldAdd; index++) {
35 | var mergedStart = mergedResults[index].start;
36 | var mergedEnd = mergedStart + mergedResults[index].length;
37 |
38 | // It is included in one of the current results
39 | if (resStart >= mergedStart && resEnd <= mergedEnd) {
40 | shouldAdd = false;
41 | }
42 |
43 | // If it contains overlaps
44 | if (resStart > mergedStart && resStart < mergedEnd) {
45 | shouldAdd = false;
46 | }
47 |
48 | // It includes one of the results and should replace the included one
49 | if (resStart <= mergedStart && resEnd >= mergedEnd) {
50 | shouldAdd = false;
51 | mergedResults[index] = result;
52 | }
53 | }
54 |
55 | if (shouldAdd) {
56 | mergedResults.add(result);
57 | }
58 | }
59 | }
60 |
61 | return mergedResults;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/src/date_time/holiday_extractor_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/date_time/base_date_extractor.dart';
2 |
3 | abstract interface class IHolidayExtractorConfiguration extends IDateTimeOptionsConfiguration {
4 | List holidayRegexes();
5 | }
6 |
--------------------------------------------------------------------------------
/lib/src/date_time/mod_and_date_result.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/date_time/base_date_parser.dart';
2 | import 'package:nlp/src/date_time/constants.dart';
3 | import 'package:nlp/src/duration/duration.dart';
4 |
5 | class ModAndDateResult {
6 | static ModAndDateResult ModAndDateResultWithDateList(
7 | DateTime beginDate, DateTime endDate, String mod, List? dateList) {
8 | return ModAndDateResult(beginDate, endDate)
9 | ..Mod = mod
10 | ..DateList = dateList;
11 | }
12 |
13 | ModAndDateResult(DateTime beginDate, DateTime endDate) {
14 | this.BeginDate = beginDate;
15 | this.EndDate = endDate;
16 | this.Mod = '';
17 | this.DateList = [];
18 | }
19 |
20 | late DateTime BeginDate;
21 |
22 | late DateTime EndDate;
23 |
24 | late String Mod;
25 |
26 | List? DateList;
27 |
28 | static ModAndDateResult GetModAndDate(
29 | DateTime beginDate, DateTime endDate, DateTime referenceDate, String timex, bool future) {
30 | DateTime beginDateResult = beginDate;
31 | DateTime endDateResult = endDate;
32 | var isBusinessDay = timex.endsWith(DateTimeConstants.TimexBusinessDay);
33 | var businessDayCount = 0;
34 | List? dateList = null;
35 |
36 | if (isBusinessDay) {
37 | businessDayCount = int.parse(timex.substring(1, timex.length - 3));
38 | }
39 |
40 | if (future) {
41 | String mod = DateTimeConstants.AFTER_MOD;
42 |
43 | // For future the beginDate should add 1 first
44 | if (isBusinessDay) {
45 | beginDateResult = DurationParsingUtil.getNextBusinessDay(referenceDate);
46 | final nthBusinessDay = DurationParsingUtil.getNthBusinessDay(beginDateResult, businessDayCount - 1, true);
47 | dateList = nthBusinessDay.dateList;
48 | endDateResult = nthBusinessDay.result;
49 | endDateResult = endDateResult.AddDays(1);
50 | return ModAndDateResultWithDateList(beginDateResult, endDateResult, mod, dateList);
51 | } else {
52 | beginDateResult = referenceDate.AddDays(1);
53 | endDateResult = DurationParsingUtil.shiftDateTime(timex, beginDateResult, true);
54 | return ModAndDateResultWithDateList(beginDateResult, endDateResult, mod, null);
55 | }
56 | } else {
57 | const String mod = DateTimeConstants.BEFORE_MOD;
58 |
59 | if (isBusinessDay) {
60 | endDateResult = DurationParsingUtil.getNextBusinessDay(endDateResult, false);
61 | final nthBusinessDay = DurationParsingUtil.getNthBusinessDay(endDateResult, businessDayCount - 1, false);
62 | dateList = nthBusinessDay.dateList;
63 | beginDateResult = nthBusinessDay.result;
64 | endDateResult = endDateResult.AddDays(1);
65 | return ModAndDateResultWithDateList(beginDateResult, endDateResult, mod, dateList);
66 | } else {
67 | beginDateResult = DurationParsingUtil.shiftDateTime(timex, endDateResult, false);
68 | return ModAndDateResultWithDateList(beginDateResult, endDateResult, mod, null);
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/src/date_time/resolution.dart:
--------------------------------------------------------------------------------
1 | class Resolution {
2 | List Values = [];
3 | }
4 |
5 | class Entry {
6 | String Timex = '';
7 |
8 | String Type = '';
9 |
10 | String Value = '';
11 |
12 | String Start = '';
13 |
14 | String End = '';
15 | }
16 |
--------------------------------------------------------------------------------
/lib/src/date_time/set_handler.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/date_time/date_time_recognizer.dart';
2 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
3 |
4 | class SetHandler {
5 | static (String, int) WeekDayGroupMatchTuple(NlpMatch match) {
6 | String weekday = match.getGroup("weekday").value;
7 | int del = 1;
8 |
9 | return (weekday, del);
10 | }
11 |
12 | static String WeekDayGroupMatchString(NlpMatch match) {
13 | String weekday = match.getGroup("weekday").value;
14 |
15 | return weekday;
16 | }
17 |
18 | static DateTimeResolutionResult ResolveSet(DateTimeResolutionResult result, String innerTimex) {
19 | result.timex = innerTimex;
20 | result.futureValue = result.pastValue = "Set: $innerTimex";
21 | result.success = true;
22 |
23 | return result;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/src/date_time/time_of_day_resolution_result.dart:
--------------------------------------------------------------------------------
1 | class TimeOfDayResolutionResult {
2 | String Timex = '';
3 |
4 | int BeginHour = 0;
5 |
6 | int EndHour = 0;
7 |
8 | int EndMin = 0;
9 |
10 | int Swift = 0;
11 | }
12 |
--------------------------------------------------------------------------------
/lib/src/date_time/time_period_functions.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 |
3 | class TimePeriodFunctions {
4 | static List ApplyPotentialPeriodAmbiguityHotfix(String text, List timePeriodErs) {
5 | return timePeriodErs;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/lib/src/date_time/timex_regex.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/nlp.dart';
2 |
3 | class TimexRegex {
4 | static const String DateTimeCollectionName = "datetime";
5 | static const String DateCollectionName = "date";
6 | static const String TimeCollectionName = "time";
7 | static const String PeriodCollectionName = "period";
8 |
9 | static Map> timexRegex = >{
10 | DateCollectionName: [
11 | // date
12 | RegExp(r"^(XXXX|(?\d\d\d\d))-(?\d\d)(-(?\d\d))?"),
13 | RegExp(r"^XXXX-WXX-(?\d)"),
14 | RegExp(r"^XXXX-XX-(?\d\d)"),
15 |
16 | // daterange
17 | RegExp(r"^(?\d\d\d\d)"),
18 | RegExp(r"^(XXXX|(?\d\d\d\d))-(?\d\d)-W(?\d\d)"),
19 | RegExp(r"^(XXXX|(?\d\d\d\d))-(?\d\d)-WXX-(?\d{1,2})(-(?\d))?"),
20 | RegExp(r"^(?SP|SU|FA|WI)"),
21 | RegExp(r"^(XXXX|(?\d\d\d\d))-(?SP|SU|FA|WI)"),
22 | RegExp(r"^(XXXX|(?\d\d\d\d))-W(?\d\d)(-(?\d)|-(?WE))?"),
23 | ],
24 | TimeCollectionName: [
25 | // time
26 | RegExp(r"T(?\d\d)Z?$"),
27 | RegExp(r"T(?\d\d):(?\d\d)Z?$"),
28 | RegExp(r"T(?\d\d):(?\d\d):(?\d\d)Z?$"),
29 |
30 | // timerange
31 | RegExp(r"^T(?DT|NI|MO|AF|EV)$"),
32 | ],
33 | PeriodCollectionName: [
34 | RegExp(r"P(?\d*\.?\d+)(?Y|M|W|D|WE|WD)$"),
35 | RegExp(r"^PT(?\d*\.?\d+)H(\d*\.?\d+(M|S)){0,2}$"),
36 | RegExp(r"^PT(\d*\.?\d+H)?(?\d*\.?\d+)M(\d*\.?\d+S)?$"),
37 | RegExp(r"^PT(\d*\.?\d+(H|M)){0,2}(?\d*\.?\d+)S$"),
38 | ],
39 | };
40 |
41 | static bool Extract(String name, String timex, Map result) {
42 | var lowerName = name.toLowerCase();
43 | var nameGroup = '';
44 |
45 | if (lowerName == DateTimeCollectionName) {
46 | nameGroup += DateCollectionName;
47 | nameGroup += TimeCollectionName;
48 | } else {
49 | nameGroup += lowerName;
50 | }
51 |
52 | var anyTrue = false;
53 | //for (var nameItem in nameGroup)
54 | for (int i = 0; i < nameGroup.length; i++) {
55 | var nameItem = nameGroup[i];
56 | for (var entry in timexRegex[nameItem]!) {
57 | if (TryExtract(entry, timex, result)) {
58 | anyTrue = true;
59 | }
60 | }
61 | }
62 |
63 | return anyTrue;
64 | }
65 |
66 | static bool TryExtract(RegExp regex, String timex, Map result) {
67 | var regexResult = RegExpComposer.firstMatch(regex, timex);
68 | if (regexResult == null) {
69 | return false;
70 | }
71 |
72 | for (var groupName in regexResult.innerGroups.keys) {
73 | result[groupName] = regexResult.getGroup(groupName).value;
74 | }
75 |
76 | return true;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/lib/src/date_time/timezone_utility.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 | import 'package:nlp/src/core/string_matcher.dart';
3 | import 'package:nlp/src/date_time/constants.dart';
4 | import 'package:nlp/src/date_time/english_date_time.dart';
5 | import 'package:nlp/src/duration/duration.dart';
6 | import 'package:nlp/src/numbers/number_with_unit_tokenizer.dart';
7 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
8 |
9 | class TimeZoneUtility {
10 | // private const RegexOptions RegexFlags = RegexOptions.Singleline | RegexOptions.ExplicitCapture;
11 |
12 | // private static readonly Regex BracketRegex =
13 | // new Regex(BaseDateTime.BracketRegex, RegexFlags, RegexTimeOut);
14 |
15 | static RegExp BracketRegex = RegExpComposer.sanitizeGroupsAndCompile(BaseDateTime.BracketRegex);
16 |
17 | // private static TimeSpan RegexTimeOut => DateTimeRecognizer.GetTimeout(MethodBase.GetCurrentMethod().DeclaringType);
18 |
19 | static List MergeTimeZones(
20 | List originalErs, List timeZoneErs, String text) {
21 | for (var er in originalErs) {
22 | for (var timeZoneEr in timeZoneErs) {
23 | // Extend timezone extraction to include brackets if any.
24 | var tzEr = ExtendTimeZoneExtraction(timeZoneEr, text);
25 |
26 | var begin = er.start + er.length;
27 | var end = tzEr.start;
28 |
29 | if (begin < end) {
30 | var gapText = text.substring(begin, end);
31 |
32 | if (gapText.trim().isEmpty) {
33 | var newLength = (tzEr.start + tzEr.length - er.start);
34 |
35 | er.text = text.substring(er.start, er.start + newLength);
36 | er.length = newLength;
37 | er.data = {
38 | DateTimeConstants.SYS_DATETIME_TIMEZONE: timeZoneEr,
39 | };
40 | }
41 | }
42 |
43 | // Make sure timezone info propagates to longer span entity.
44 | if (er.isOverlap(timeZoneEr)) {
45 | er.data = {
46 | DateTimeConstants.SYS_DATETIME_TIMEZONE: timeZoneEr,
47 | };
48 | }
49 | }
50 | }
51 |
52 | return originalErs;
53 | }
54 |
55 | static bool ShouldResolveTimeZone(ExtractResult er, DateTimeOptions options) {
56 | var enablePreview = options.match(DateTimeOptions.EnablePreview);
57 | if (!enablePreview) {
58 | return false;
59 | }
60 |
61 | var hasTimeZoneData = false;
62 |
63 | if (er.data is Map) {
64 | var metaData = er.data as Map;
65 |
66 | if (metaData.containsKey(DateTimeConstants.SYS_DATETIME_TIMEZONE)) {
67 | hasTimeZoneData = true;
68 | }
69 | }
70 |
71 | return hasTimeZoneData;
72 | }
73 |
74 | static StringMatcher BuildMatcherFromLists(List> collections) {
75 | StringMatcher matcher = StringMatcher(strategy: MatchStrategy.TrieTree, tokenizer: NumberWithUnitTokenizer());
76 | List matcherList = [];
77 |
78 | for (List collection in collections) {
79 | for (final matcher in collection) {
80 | matcherList.add(matcher.trim().toLowerCase());
81 | }
82 | }
83 |
84 | matcherList = matcherList.toSet().toList();
85 |
86 | matcher.initFromValues(matcherList);
87 |
88 | return matcher;
89 | }
90 |
91 | static ExtractResult ExtendTimeZoneExtraction(ExtractResult timeZoneEr, String text) {
92 | var beforeStr = text.substring(0, timeZoneEr.start + 1);
93 | var afterStr = text.substring(timeZoneEr.start + timeZoneEr.length);
94 | var matchLeft = BracketRegex.firstMatch(beforeStr);
95 | var matchRight = BracketRegex.firstMatch(afterStr);
96 | if (matchLeft != null && matchRight != null) {
97 | timeZoneEr.start -= matchLeft.length;
98 | timeZoneEr.length += matchLeft.length + matchRight.length;
99 | }
100 |
101 | return timeZoneEr;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/lib/src/duration/english_duration_parser_configuration.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 | import 'package:nlp/src/core/parser.dart';
3 | import 'package:nlp/src/duration/base_duration_parser.dart';
4 | import 'package:nlp/src/date_time/english_date_time_parser.dart';
5 | import 'package:nlp/src/duration/duration.dart';
6 | import 'package:nlp/src/duration/duration_extractor.dart';
7 |
8 | class EnglishDurationParserConfiguration extends BaseOptionsConfiguration implements IDurationParserConfiguration {
9 | EnglishDurationParserConfiguration(
10 | ICommonDateTimeParserConfiguration config, [
11 | super.options = DateTimeOptions.None,
12 | super.dmyDateFormat = false,
13 | ]) {
14 | cardinalExtractor = config.cardinalExtractor;
15 | numberParser = config.numberParser;
16 | durationExtractor = DurationExtractor(config: EnglishDurationExtractorConfiguration(), merge: false);
17 | numberCombinedWithUnit = EnglishDurationExtractorConfiguration.NumberCombinedWithDurationUnit;
18 |
19 | anUnitRegex = EnglishDurationExtractorConfiguration.AnUnitRegex;
20 | duringRegex = EnglishDurationExtractorConfiguration.DuringRegex;
21 | allDateUnitRegex = EnglishDurationExtractorConfiguration.AllRegex;
22 | halfDateUnitRegex = EnglishDurationExtractorConfiguration.HalfRegex;
23 | suffixAndRegex = EnglishDurationExtractorConfiguration.SuffixAndRegex;
24 | followedUnit = EnglishDurationExtractorConfiguration.DurationFollowedUnit;
25 | conjunctionRegex = EnglishDurationExtractorConfiguration.ConjunctionRegex;
26 | inexactNumberRegex = EnglishDurationExtractorConfiguration.InexactNumberRegex;
27 | inexactNumberUnitRegex = EnglishDurationExtractorConfiguration.InexactNumberUnitRegex;
28 | durationUnitRegex = EnglishDurationExtractorConfiguration.DurationUnitRegex;
29 |
30 | unitMap = config.unitMap;
31 | unitValueMap = config.unitValueMap;
32 | doubleNumbers = config.doubleNumbers;
33 | }
34 |
35 | late final IExtractor cardinalExtractor;
36 | late final IExtractor durationExtractor;
37 | late final IParser numberParser;
38 |
39 | late final RegExp numberCombinedWithUnit;
40 | late final RegExp anUnitRegex;
41 | late final RegExp duringRegex;
42 | late final RegExp allDateUnitRegex;
43 | late final RegExp halfDateUnitRegex;
44 | late final RegExp suffixAndRegex;
45 | late final RegExp followedUnit;
46 | late final RegExp conjunctionRegex;
47 | late final RegExp inexactNumberRegex;
48 | late final RegExp inexactNumberUnitRegex;
49 | late final RegExp durationUnitRegex;
50 |
51 | late final Map unitMap;
52 | late final Map unitValueMap;
53 | late final Map doubleNumbers;
54 |
55 | @override
56 | IExtractor getCardinalExtractor() {
57 | return cardinalExtractor;
58 | }
59 |
60 | @override
61 | IExtractor getDurationExtractor() {
62 | return durationExtractor;
63 | }
64 |
65 | @override
66 | IParser? getNumberParser() {
67 | return numberParser;
68 | }
69 |
70 | @override
71 | RegExp getNumberCombinedWithUnit() {
72 | return numberCombinedWithUnit;
73 | }
74 |
75 | @override
76 | RegExp getAnUnitRegex() {
77 | return anUnitRegex;
78 | }
79 |
80 | @override
81 | RegExp getDuringRegex() {
82 | return duringRegex;
83 | }
84 |
85 | @override
86 | RegExp getAllDateUnitRegex() {
87 | return allDateUnitRegex;
88 | }
89 |
90 | @override
91 | RegExp getHalfDateUnitRegex() {
92 | return halfDateUnitRegex;
93 | }
94 |
95 | @override
96 | RegExp getSuffixAndRegex() {
97 | return suffixAndRegex;
98 | }
99 |
100 | @override
101 | RegExp getFollowedUnit() {
102 | return followedUnit;
103 | }
104 |
105 | @override
106 | RegExp getConjunctionRegex() {
107 | return conjunctionRegex;
108 | }
109 |
110 | @override
111 | RegExp getInexactNumberRegex() {
112 | return inexactNumberRegex;
113 | }
114 |
115 | @override
116 | RegExp getInexactNumberUnitRegex() {
117 | return inexactNumberUnitRegex;
118 | }
119 |
120 | @override
121 | RegExp getDurationUnitRegex() {
122 | return durationUnitRegex;
123 | }
124 |
125 | @override
126 | RegExp? getSpecialNumberUnitRegex() {
127 | return null;
128 | }
129 |
130 | @override
131 | Map getUnitMap() {
132 | return unitMap;
133 | }
134 |
135 | @override
136 | Map getUnitValueMap() {
137 | return unitValueMap;
138 | }
139 |
140 | @override
141 | Map getDoubleNumbers() {
142 | return doubleNumbers;
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/lib/src/numbers/number_constants.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: non_constant_identifier_names
2 |
3 | class Constants {
4 | static final SYS_NUM_CARDINAL = "builtin.num.cardinal";
5 | static final SYS_NUM_DOUBLE = "builtin.num.double";
6 | static final SYS_NUM_FRACTION = "builtin.num.fraction";
7 | static final SYS_NUM_INTEGER = "builtin.num.integer";
8 | static final SYS_NUM = "builtin.num";
9 | static final SYS_NUM_ORDINAL = "builtin.num.ordinal";
10 | static final SYS_NUM_PERCENTAGE = "builtin.num.percentage";
11 | static final SYS_NUMRANGE = "builtin.num.numberrange";
12 | static final SYS_NUMBER_ORDINAL = "builtin.num.ordinal";
13 |
14 | // Model type name
15 | static final MODEL_NUMBER = "number";
16 | static final MODEL_NUMBERRANGE = "numberrange";
17 | static final MODEL_ORDINAL = "ordinal";
18 | static final MODEL_PERCENTAGE = "percentage";
19 |
20 | // NARROW NO-BREAK SPACE
21 | static final NO_BREAK_SPACE = '\u202f';
22 |
23 | // Ordinal.relative attribute values
24 | static final RELATIVE_START = "start";
25 | static final RELATIVE_END = "end";
26 | static final RELATIVE_CURRENT = "current";
27 |
28 | static final AGO_LABEL = "ago";
29 | static final LATER_LABEL = "later";
30 | }
31 |
--------------------------------------------------------------------------------
/lib/src/numbers/number_with_unit_tokenizer.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/extraction.dart';
2 | import 'package:nlp/src/core/string_matcher.dart';
3 | import 'package:nlp/src/core/string_utility.dart';
4 | import 'package:nlp/src/regular_expressions/string_extensions.dart';
5 |
6 | class NumberWithUnitTokenizer extends SimpleTokenizer {
7 | static final specialTokenCharacters = {'\$'};
8 |
9 | /* The main difference between this strategy and SimpleTokenizer is for cases like
10 | * 'Bob's $ 100 cash'. 's' and '$' are independent tokens in SimpleTokenizer.
11 | * However, 's$' will return these two tokens too. Here, we let 's$' be a single
12 | * token to avoid the false positive.
13 | * Besides, letters and digits can't be mixed as a token. For cases like '200ml'.
14 | * '200ml' will be a token in SimpleTokenizer. Here, '200' and 'ml' are independent tokens.
15 | */
16 |
17 | @override
18 | List tokenize(String? input) {
19 | final tokens = [];
20 |
21 | if (StringUtility.isNullOrEmpty(input)) {
22 | return tokens;
23 | }
24 |
25 | bool inToken = false;
26 | int tokenStart = 0;
27 | for (int i = 0; i < input!.length; i++) {
28 | final c = input[i];
29 | if (c.isWhitespace) {
30 | if (inToken) {
31 | tokens.add(Token(tokenStart, i - tokenStart, input.substring(tokenStart, i)));
32 | inToken = false;
33 | }
34 | } else if ((!specialTokenCharacters.contains(c) && !c.isLetterOrDigit) || isChinese(c) || isJapanese(c)) {
35 | // Non-splittable currency units (as "$") are treated as regular letters. For instance, 'us$' should be a single token
36 | if (inToken) {
37 | tokens.add(Token(tokenStart, i - tokenStart, input.substring(tokenStart, i)));
38 | inToken = false;
39 | }
40 |
41 | tokens.add(Token(i, 1, input.substring(i, i + 1)));
42 | } else {
43 | if (inToken && i > 0) {
44 | final preChar = input[i - 1];
45 | if (isSplittableUnit(c, preChar)) {
46 | // Split if letters or non-splittable units are adjacent with digits.
47 | tokens.add(Token(tokenStart, i - tokenStart, input.substring(tokenStart, i)));
48 | tokenStart = i;
49 | }
50 | }
51 |
52 | if (!inToken) {
53 | tokenStart = i;
54 | inToken = true;
55 | }
56 | }
57 | }
58 |
59 | if (inToken) {
60 | tokens.add(Token(tokenStart, input.length - tokenStart, input.substring(tokenStart, input.length)));
61 | }
62 |
63 | return tokens;
64 | }
65 |
66 | bool isSplittableUnit(String curChar, String preChar) {
67 | // To handle cases like '200ml', digits and letters cannot be mixed as a single token. '200ml' will be tokenized to '200' and 'ml'.
68 | if ((curChar.isLetter && preChar.isDigit) || (curChar.isDigit && preChar.isLetter)) {
69 | return true;
70 | }
71 |
72 | // Non-splittable currency units can't be mixed with digits. For example, '$100' or '100$' will be tokenized to '$' and '100',
73 | // '1$50' will be tokenized to '1', '$', and '50'
74 | if ((curChar.isDigit && specialTokenCharacters.contains(preChar)) ||
75 | (specialTokenCharacters.contains(curChar) && preChar.isDigit)) {
76 | return true;
77 | }
78 |
79 | // Non-splittable currency units adjacent with letters are treated as regular token characters. For instance, 's$' or 'u$d' are single tokens.
80 | return false;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/lib/src/regular_expressions/js_regexp.dart:
--------------------------------------------------------------------------------
1 | import 'dart:js' as js;
2 |
3 | class JsRegExp {
4 | /// Creates a JavaScript Regular Expression based in the given [pattern],
5 | /// with the given flags.
6 | ///
7 | /// [d] - `true` to enable indices for capture groups.
8 | JsRegExp(
9 | String pattern, {
10 | bool d = false,
11 | }) {
12 | _jsRegExp = js.context['RegExp'].apply([
13 | pattern,
14 | if (d) 'd',
15 | ]);
16 | }
17 |
18 | late final js.JsObject _jsRegExp;
19 |
20 | JsRegExpResult? exec(String content) {
21 | final jsResult = _jsRegExp.callMethod('exec', [content]);
22 | return jsResult != null ? JsRegExpResult(jsResult) : null;
23 | }
24 | }
25 |
26 | // class JsRegExpMatch implements RegExpMatch {
27 | // JsRegExpMatch({
28 | // required String target,
29 | // required this.pattern,
30 | // required Map captureGroups,
31 | // }) : _target = target,
32 | // _captureGroups = captureGroups {
33 | // groupNames = captureGroups.keys;
34 | // }
35 | //
36 | // final String _target;
37 | //
38 | // final Map _captureGroups;
39 | //
40 | // @override
41 | // final RegExp pattern;
42 | //
43 | // @override
44 | // late final Iterable groupNames;
45 | //
46 | // @override
47 | // String? namedGroup(String groupName) => _target.substring(
48 | // _captureGroups[groupName]!.$1,
49 | // _captureGroups[groupName]!.$2,
50 | // );
51 | //
52 | // @override
53 | // String? operator [](int group) {
54 | // // TODO: implement []
55 | // throw UnimplementedError();
56 | // }
57 | //
58 | // @override
59 | // // TODO: implement end
60 | // int get end => throw UnimplementedError();
61 | //
62 | // @override
63 | // String? group(int group) {
64 | // // TODO: implement group
65 | // throw UnimplementedError();
66 | // }
67 | //
68 | // @override
69 | // // TODO: implement groupCount
70 | // int get groupCount => throw UnimplementedError();
71 | //
72 | // @override
73 | // List groups(List groupIndices) {
74 | // // TODO: implement groups
75 | // throw UnimplementedError();
76 | // }
77 | //
78 | // @override
79 | // // TODO: implement input
80 | // String get input => throw UnimplementedError();
81 | //
82 | // @override
83 | // // TODO: implement start
84 | // int get start => throw UnimplementedError();
85 | // }
86 |
87 | class JsRegExpResult {
88 | const JsRegExpResult(this._jsResult);
89 |
90 | final js.JsObject _jsResult;
91 |
92 | String get input => _jsResult['input'];
93 |
94 | int get index => _jsResult['index'];
95 |
96 | JsRegExpGroups get groups => JsRegExpGroups(_jsResult['groups']);
97 |
98 | /// Returns the name of each group in the RegExp that found a match within the [input].
99 | Iterable get matchingGroupNames => groups.names.where((name) => getGroupBounds(name) != null);
100 |
101 | String? getGroup(String groupName) {
102 | final bounds = getGroupBounds(groupName);
103 | if (bounds == null) {
104 | return null;
105 | }
106 |
107 | return input.substring(bounds.$1, bounds.$2);
108 | }
109 |
110 | (int, int)? getGroupBounds(String groupName) {
111 | final js.JsObject namedGroupBounds = _jsResult['indices']['groups'];
112 | final boundsForGroup = namedGroupBounds[groupName];
113 |
114 | if (boundsForGroup is js.JsArray && boundsForGroup.length == 2) {
115 | return (boundsForGroup[0], boundsForGroup[1]);
116 | }
117 |
118 | if (boundsForGroup is (int, int)) {
119 | return boundsForGroup;
120 | }
121 |
122 | return null;
123 | }
124 |
125 | List<(int, int)> get indices {
126 | final indices = <(int, int)>[];
127 |
128 | final jsIndices = _jsResult['indices'] as js.JsArray;
129 | for (int i = 0; i < jsIndices.length; i += 1) {
130 | final range = jsIndices[i];
131 |
132 | if (range is (int, int)) {
133 | indices.add((range.$1, range.$2));
134 | continue;
135 | }
136 |
137 | if (range is js.JsArray) {
138 | if (range.length != 2) {
139 | print("WARNING: Found a match range that doesn't have 2 bounds: $range");
140 | continue;
141 | }
142 | jsIndices.add((range[0], range[1]));
143 | continue;
144 | }
145 |
146 | print("WARNING: Expected a match range of type JsArray or (int, int) but it was: ${range.runtimeType}");
147 | }
148 |
149 | return indices;
150 | }
151 |
152 | @override
153 | String toString() => '''JsRegExpResult[
154 | - groups: $groups
155 | - indices: $indices
156 | ]''';
157 | }
158 |
159 | class JsRegExpGroups {
160 | const JsRegExpGroups(this._jsGroups);
161 |
162 | final js.JsObject? _jsGroups;
163 |
164 | Iterable get names => _jsGroups?.keys.whereType() ?? [];
165 |
166 | String operator [](dynamic key) => _jsGroups?[key] ?? '';
167 |
168 | @override
169 | String toString() => _jsGroups?.keys.join(", ") ?? '';
170 | }
171 |
172 | extension on js.JsObject {
173 | List get keys => (js.context['Object'].callMethod('getOwnPropertyNames', [this]) as js.JsArray)
174 | .map((key) => key as String)
175 | .toList();
176 | }
177 |
178 | void _jsExample() {
179 | final jsRegExp = js.context['RegExp'].apply(['^(?\\d+),(?.+)\$', 'd']);
180 | final result = jsRegExp.callMethod('exec', ["1560979912,Caroline"]);
181 |
182 | print("Result:");
183 | print("$result");
184 | print("");
185 |
186 | print("Groups type: ${result['groups'].runtimeType}");
187 |
188 | print("Timestamp group:");
189 | print("${result['groups']['timestamp']}");
190 | print("");
191 |
192 | print("Author group:");
193 | print("${result['groups']['author']}");
194 | print("");
195 |
196 | print("Indices:");
197 | print("${result['indices']}");
198 | print("");
199 | }
200 |
--------------------------------------------------------------------------------
/lib/src/regular_expressions/matching_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:nlp/src/core/string_matcher.dart';
2 | import 'package:nlp/src/date_time/english_date_time.dart';
3 | import 'package:nlp/src/regular_expressions/regular_expressions_extensions.dart';
4 |
5 | class MatchingUtil {
6 | static RegExp invalidDayNumberPrefix = RegExp(BaseDateTime.InvalidDayNumberPrefix);
7 |
8 | static List> removeSubMatches(Iterable> matchResults) {
9 | return matchResults
10 | .where(
11 | (item) => matchResults
12 | .where(
13 | (ritem) =>
14 | (ritem.start < item.start && (ritem.start + ritem.length) >= (item.start + item.length)) ||
15 | (ritem.start <= item.start && (ritem.start + ritem.length) > (item.start + item.length)),
16 | )
17 | .isNotEmpty,
18 | )
19 | .toList();
20 |
21 | // Original code
22 | // return StreamSupport.stream(matchResults.spliterator(), false)
23 | // .filter(item -> !StreamSupport.stream(matchResults.spliterator(), false)
24 | // .anyMatch(ritem -> (ritem.getStart() < item.getStart() && ritem.getEnd() >= item.getEnd()) ||
25 | // (ritem.getStart() <= item.getStart() && ritem.getEnd() > item.getEnd())))
26 | // .collect(Collectors.toCollection(ArrayList::new));
27 | }
28 |
29 | static int? GetAgoLaterIndex(String text, RegExp regex, bool inSuffix) {
30 | var match = inSuffix ? regex.matchBegin(text, true) : regex.matchEnd(text, true);
31 |
32 | if (match != null) {
33 | return match.start + (inSuffix ? match.length : 0);
34 | }
35 |
36 | return null;
37 | }
38 |
39 | static int? GetTermIndex(String text, RegExp regex) {
40 | var match = regex.firstMatch(text.trim().split(' ').last);
41 | if (match != null) {
42 | return text.length - text.lastIndexOf(match.input.substring(match.start, match.end));
43 | }
44 |
45 | return null;
46 | }
47 |
48 | static bool ContainsAgoLaterIndex(String text, RegExp regex, bool inSuffix) {
49 | return GetAgoLaterIndex(text, regex, inSuffix) != null;
50 | }
51 |
52 | static bool ContainsTermIndex(String text, RegExp regex) {
53 | return GetTermIndex(text, regex) != null;
54 | }
55 |
56 | static bool isInvalidDayNumberPrefix(String prefix) {
57 | return invalidDayNumberPrefix.hasMatch(prefix);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/lib/src/regular_expressions/string_extensions.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: constant_identifier_names
2 |
3 | extension NlpString on String {
4 | bool get isLetterOrDigit => isLetter || isDigit;
5 |
6 | bool get isLetter {
7 | if (length != 1) {
8 | return false;
9 | }
10 |
11 | final codePoint = this[0].codeUnitAt(0);
12 | return (_unicode_A <= codePoint && codePoint <= _unicode_Z) || (_unicode_a <= codePoint && codePoint <= _unicode_z);
13 | }
14 |
15 | bool get isDigit {
16 | if (length != 1) {
17 | return false;
18 | }
19 |
20 | final codePoint = this[0].codeUnitAt(0);
21 | return codePoint ^ 0x30 <= 9;
22 | }
23 |
24 | bool get isWhitespace {
25 | if (length != 1) {
26 | return false;
27 | }
28 |
29 | final codePoint = this[0].codeUnitAt(0);
30 | return (codePoint >= 0x0009 && codePoint <= 0x000D) ||
31 | codePoint == 0x0020 ||
32 | codePoint == 0x0085 ||
33 | codePoint == 0x00A0 ||
34 | codePoint == 0x1680 ||
35 | codePoint == 0x180E ||
36 | (codePoint >= 0x2000 && codePoint <= 0x200A) ||
37 | codePoint == 0x2028 ||
38 | codePoint == 0x2029 ||
39 | codePoint == 0x202F ||
40 | codePoint == 0x205F ||
41 | codePoint == 0x3000 ||
42 | codePoint == 0xFEFF;
43 | }
44 | }
45 |
46 | const _unicode_A = 65;
47 | const _unicode_Z = 90;
48 |
49 | const _unicode_a = 97;
50 | const _unicode_z = 122;
51 |
--------------------------------------------------------------------------------
/lib/src/time/english_time_zone_extractor.dart:
--------------------------------------------------------------------------------
1 | import 'package:diacritic/diacritic.dart';
2 | import 'package:nlp/src/core/string_matcher.dart';
3 | import 'package:nlp/src/duration/duration.dart';
4 | import 'package:nlp/src/duration/duration_extractor.dart';
5 | import 'package:nlp/src/numbers/number_with_unit_tokenizer.dart';
6 | import 'package:nlp/src/time/english_time_zone.dart';
7 | import 'package:nlp/src/time/time_zone_extractor.dart';
8 |
9 | class EnglishTimeZoneExtractorConfiguration extends BaseOptionsConfiguration
10 | implements ITimeZoneExtractorConfiguration {
11 | // These regexes do need to be case insensitive for them to work correctly
12 | static final RegExp DirectUtcRegex = RegExp(EnglishTimeZone.DirectUtcRegex, caseSensitive: false);
13 | static final List AbbreviationsList = EnglishTimeZone.AbbreviationsList;
14 | static final List FullNameList = EnglishTimeZone.FullNameList;
15 | static final RegExp LocationTimeSuffixRegex = RegExp(EnglishTimeZone.LocationTimeSuffixRegex, caseSensitive: false);
16 | static final StringMatcher LocationMatcher = StringMatcher();
17 | static final StringMatcher TimeZoneMatcher = buildMatcherFromLists([AbbreviationsList, FullNameList]);
18 |
19 | static final List AmbiguousTimezoneList = EnglishTimeZone.AmbiguousTimezoneList;
20 |
21 | EnglishTimeZoneExtractorConfiguration([super.options = DateTimeOptions.None]) {
22 | if (options.match(DateTimeOptions.EnablePreview)) {
23 | LocationMatcher.initFromValues(
24 | EnglishTimeZone.MajorLocations.map(
25 | (o) => removeDiacritics(o.toLowerCase()),
26 | ),
27 | );
28 | }
29 | }
30 |
31 | static StringMatcher buildMatcherFromLists(List> collections) {
32 | StringMatcher matcher = StringMatcher(
33 | strategy: MatchStrategy.TrieTree,
34 | tokenizer: NumberWithUnitTokenizer(),
35 | );
36 | final matcherList = [];
37 |
38 | // Collect all lower case versions of given list items.
39 | final nonLowerCaseList = [];
40 | for (List collection in collections) {
41 | for (String item in collection) {
42 | matcherList.add(item.toLowerCase());
43 |
44 | if (item != item.toLowerCase()) {
45 | nonLowerCaseList.add(item);
46 | }
47 | }
48 | }
49 |
50 | // For items that weren't originally lower case, add their original version.
51 | matcherList.addAll(nonLowerCaseList);
52 |
53 | matcher.initFromValues(matcherList);
54 |
55 | return matcher;
56 | }
57 |
58 | @override
59 | RegExp getDirectUtcRegex() {
60 | return DirectUtcRegex;
61 | }
62 |
63 | @override
64 | RegExp getLocationTimeSuffixRegex() {
65 | return LocationTimeSuffixRegex;
66 | }
67 |
68 | @override
69 | StringMatcher getLocationMatcher() {
70 | return LocationMatcher;
71 | }
72 |
73 | @override
74 | StringMatcher getTimeZoneMatcher() {
75 | return TimeZoneMatcher;
76 | }
77 |
78 | @override
79 | List getAmbiguousTimezoneList() {
80 | return AmbiguousTimezoneList;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/lib/src/time/time_zone_extractor.dart:
--------------------------------------------------------------------------------
1 | import 'package:diacritic/diacritic.dart';
2 | import 'package:nlp/nlp.dart';
3 | import 'package:nlp/src/core/string_matcher.dart';
4 | import 'package:nlp/src/date_time/date_time_extraction.dart';
5 | import 'package:nlp/src/regular_expressions/matching_util.dart';
6 |
7 | class BaseTimeZoneExtractor implements IDateTimeZoneExtractor {
8 | BaseTimeZoneExtractor(this._config);
9 |
10 | final ITimeZoneExtractorConfiguration _config;
11 |
12 | @override
13 | String getExtractorName() {
14 | return DateTimeConstants.SYS_DATETIME_TIMEZONE;
15 | }
16 |
17 | @override
18 | List extract(String input) {
19 | return extractTimeZone(input, DateTime.now());
20 | }
21 |
22 | @override
23 | List extractDateTime(String input, DateTime reference) {
24 | return extractTimeZone(input, reference);
25 | }
26 |
27 | List