diff --git a/.gitmodules b/.gitmodules index 05a2491880..0367e3ce26 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1238,6 +1238,9 @@ [submodule "vendor/grammars/vscode-TalonScript"] path = vendor/grammars/vscode-TalonScript url = https://github.com/mrob95/vscode-TalonScript.git +[submodule "vendor/grammars/vscode-aiken"] + path = vendor/grammars/vscode-aiken + url = https://github.com/aiken-lang/vscode-aiken.git [submodule "vendor/grammars/vscode-antlers-language-server"] path = vendor/grammars/vscode-antlers-language-server url = https://github.com/Stillat/vscode-antlers-language-server.git diff --git a/grammars.yml b/grammars.yml index 23c95ad2f7..71d75cd6e9 100644 --- a/grammars.yml +++ b/grammars.yml @@ -1109,6 +1109,9 @@ vendor/grammars/vsc-fennel: vendor/grammars/vscode-TalonScript: - markdown.talon.codeblock - source.talon +vendor/grammars/vscode-aiken: +- source.aiken +- source.uplc vendor/grammars/vscode-antlers-language-server: - text.html.statamic vendor/grammars/vscode-bitbake: diff --git a/lib/linguist/languages.yml b/lib/linguist/languages.yml index aae547bd83..538369eecb 100644 --- a/lib/linguist/languages.yml +++ b/lib/linguist/languages.yml @@ -261,6 +261,14 @@ Agda: tm_scope: source.agda ace_mode: text language_id: 12 +Aiken: + type: programming + color: "#640ff8" + ace_mode: text + extensions: + - ".ak" + tm_scope: source.aiken + language_id: 899409497 Alloy: type: programming color: "#64C800" @@ -7446,6 +7454,14 @@ UnrealScript: codemirror_mode: clike codemirror_mime_type: text/x-java language_id: 382 +Untyped Plutus Core: + type: programming + color: "#36adbd" + ace_mode: text + extensions: + - ".uplc" + tm_scope: source.uplc + language_id: 1061635506 UrWeb: type: programming color: "#ccccee" @@ -8515,4 +8531,4 @@ xBase: - ".prw" tm_scope: source.harbour ace_mode: text - language_id: 421 + language_id: 421 \ No newline at end of file diff --git a/samples/Aiken/multi.ak b/samples/Aiken/multi.ak new file mode 100644 index 0000000000..2d78aa345a --- /dev/null +++ b/samples/Aiken/multi.ak @@ -0,0 +1,162 @@ +use aiken/builtin +use aiken/bytearray +use aiken/dict +use aiken/hash.{blake2b_256} +use aiken/list +use aiken/transaction.{ + InlineDatum, Input, Output, ScriptContext, Spend, Transaction, +} as tx +use aiken/transaction/credential.{Address, PaymentCredential, ScriptCredential} +use aiken/transaction/value + +type Action { + Mint(Int) + Burn +} + +type SpendTokenName = + ByteArray + +validator(creator: ByteArray) { + fn redeem( + // Each spend input checks for a token name matching the datum being burned + datum: SpendTokenName, + _r: Data, + ctx: ScriptContext, + ) { + let ScriptContext { transaction, purpose } = ctx + + let Transaction { inputs, mint, .. } = transaction + + expect Spend(own_ref) = purpose + + expect Some(own_input) = + list.find(inputs, fn(input) { input.output_reference == own_ref }) + + let Input { + output: Output { address: Address { payment_credential, .. }, .. }, + .. + } = own_input + + expect ScriptCredential(own_validator_hash) = payment_credential + + ( + mint + |> value.from_minted_value + |> value.quantity_of(own_validator_hash, datum) + ) == -1 + } + + fn gift_card(rdmr: Action, ctx: ScriptContext) -> Bool { + // get values from transaction and purpose + let ScriptContext { transaction, purpose } = ctx + + expect tx.Mint(policy_id) = purpose + + let Transaction { inputs, mint, extra_signatories, outputs, .. } = + transaction + + let minted_assets = + mint + |> value.from_minted_value + |> value.tokens(policy_id) + |> dict.to_pairs() + + when rdmr is { + Mint(total) -> { + expect [input, ..] = inputs + // Base is created from serializing a utxo ref being spent. Thus this guarantees a unique base + let base = builtin.serialise_data(input.output_reference) + // Create a list of expected token names + let expected_minted_token_names = + create_expected_minted_nfts(base, total, []) + // Check contract creator is a signer of this tx + let signature_check = + list.any(extra_signatories, fn(n) { creator == n }) + // Support multiple gift card creation by allowing a + // 'number of tokens minted' == 'outputs with datum being token name' + signature_check && check_mint_and_outputs( + minted_assets, + outputs, + expected_minted_token_names, + ScriptCredential(policy_id), + ) + } + Burn -> + list.all( + minted_assets, + fn(asset) { + let Pair(_, amount) = asset + amount == -1 + }, + ) + } + } +} + +fn insert(self: List, e: a, compare: fn(a, a) -> Ordering) -> List { + when self is { + [] -> + [e] + [x, ..xs] -> + if compare(e, x) == Less { + [e, ..self] + } else { + [x, ..insert(xs, e, compare)] + } + } +} + +// Check each minted token name is in the expected list, has quantity of 1, +// and has a corresponding ouput with datum containing token name. +// Otherwise fail +fn check_mint_and_outputs( + minted_assets: Pairs, + outputs: List, + expected_assets: List, + validator_cred: PaymentCredential, +) -> Bool { + when minted_assets is { + [] -> True + [Pair(minted_asset_name, quantity), ..rest_assets] -> { + expect + list.any( + expected_assets, + fn(expected_asset) { expected_asset == minted_asset_name }, + ) + + expect + list.any( + outputs, + fn(output) { + let Output { address, datum, .. } = output + datum == InlineDatum(minted_asset_name) && address.payment_credential == validator_cred + }, + ) + + quantity == 1 && check_mint_and_outputs( + rest_assets, + outputs, + expected_assets, + validator_cred, + ) + } + } +} + +fn create_expected_minted_nfts( + base: ByteArray, + counter: Int, + accum: List, +) -> List { + if counter == 0 { + accum + } else { + let token_name = blake2b_256(bytearray.push(base, counter)) + + let accum = + [token_name, ..accum] + + create_expected_minted_nfts(base, counter - 1, accum) + } +} diff --git a/samples/Aiken/oneshot.ak b/samples/Aiken/oneshot.ak new file mode 100644 index 0000000000..a1fcc4fefa --- /dev/null +++ b/samples/Aiken/oneshot.ak @@ -0,0 +1,50 @@ +use aiken/dict +use aiken/list +use aiken/transaction.{OutputReference, ScriptContext, Transaction} as tx +use aiken/transaction/value + +type Action { + Mint + Burn +} + +validator(token_name: ByteArray, utxo_ref: OutputReference) { + fn gift_card(rdmr: Action, ctx: ScriptContext) -> Bool { + let ScriptContext { transaction, purpose } = ctx + + expect tx.Mint(policy_id) = purpose + + let Transaction { inputs, mint, .. } = transaction + + expect [Pair(asset_name, amount)] = + mint + |> value.from_minted_value + |> value.tokens(policy_id) + |> dict.to_pairs() + + when rdmr is { + Mint -> { + expect Some(_input) = + list.find(inputs, fn(input) { input.output_reference == utxo_ref }) + amount == 1 && asset_name == token_name + } + Burn -> amount == -1 && asset_name == token_name + } + } +} + +validator(token_name: ByteArray, policy_id: ByteArray) { + fn redeem(_d: Data, _r: Data, ctx: ScriptContext) -> Bool { + let ScriptContext { transaction, .. } = ctx + + let Transaction { mint, .. } = transaction + + expect [Pair(asset_name, amount)] = + mint + |> value.from_minted_value + |> value.tokens(policy_id) + |> dict.to_pairs() + + amount == -1 && asset_name == token_name + } +} diff --git a/samples/Aiken/tunav2.ak b/samples/Aiken/tunav2.ak new file mode 100644 index 0000000000..2850e746b6 --- /dev/null +++ b/samples/Aiken/tunav2.ak @@ -0,0 +1,279 @@ +use aiken/builtin +use aiken/bytearray +use aiken/dict +use aiken/hash.{blake2b_256, sha2_256} +use aiken/interval.{Finite, Interval, IntervalBound} +use aiken/list +use aiken/math.{pow2} +use aiken/merkle_patricia_forestry.{Proof, from_root, insert} +use aiken/transaction.{ + InlineDatum, Mint, Output, ScriptContext, Spend, Transaction, +} as tx +use aiken/transaction/credential.{Address, Inline, ScriptCredential} +use aiken/transaction/value.{from_minted_value} +use fortuna +use fortuna/parameters.{ + epoch_number, halving_number, initial_payout, miner_threshold, + miner_voting_period, supply_threshold, vote_threshold, +} +use fortuna/types.{ + BurnToken, FinalizeNomination, Genesis, MineTuna, Mining, NominateUpgrade, + Nominated, Redeem, Statev2, TunaAction, TunaUpgradeProcess, VotingToken, +} +use fortuna/utils.{get_inline_datum, integer_to_bytes, resolve_output_reference} +use fortunav2.{ + big_tuna_length, counter_length, expect_first, flip_hash, genesis_v2, + quantity_of, tokens, vote, voting_period, +} +use hardfork.{calculate_emission} +use hardfork/hftypes.{Lock, NftForkAction} + +type TunaSpendAction { + TokenVoteFor + TokenVoteAgainst + MinerVoteFor { output_index: Int, block_number: Int } + TransitionState { block_number: Int } +} + +type Miner { + Pkh(ByteArray, Data) + Nft { policy: ByteArray, name: ByteArray, output_index: Int, extra: Data } +} + +type MineAction { + MinePow(ByteArray, Miner, Proof) + Upgrade +} + +type TargetState { + nonce: ByteArray, + miner: ByteArray, + block_number: Int, + current_hash: ByteArray, + leading_zeros: Int, + target_number: Int, + epoch_time: Int, +} + +validator(tunav2_minting_policy: ByteArray) { + fn mine(datum: Statev2, redeemer: MineAction, ctx: ScriptContext) -> Bool { + when redeemer is { + MinePow(nonce, miner, merkle_proof_list) -> { + let Statev2 { + block_number, + current_hash, + leading_zeros, + target_number, + epoch_time, + current_posix_time, + merkle_root, + } = datum + + let ScriptContext { transaction, purpose } = ctx + + expect Spend(own_reference) = purpose + + let Transaction { inputs, outputs, validity_range, .. } = transaction + + let Output { address: in_address, value: in_value, .. } = + resolve_output_reference(inputs, own_reference) + + expect ScriptCredential(own_script_hash) = in_address.payment_credential + + // Spend(0) requirement: Contract has only one output with the master token going back to itself + expect Some(own_output) = + list.find( + outputs, + fn(output: Output) { output.address == in_address }, + ) + + let Output { datum: out_datum, value: out_value, .. } = own_output + + // Time Range Span is 3 minutes or less + // We have a constant expectation of the transaction time range + expect Interval { + upper_bound: IntervalBound { + bound_type: Finite(upper_range), + is_inclusive: upper_is_inclusive, + }, + lower_bound: IntervalBound { + bound_type: Finite(lower_range), + is_inclusive: lower_is_inclusive, + }, + } = validity_range + + let averaged_current_time = + ( upper_range - lower_range ) / 2 + lower_range + + let serialized_miner = builtin.serialise_data(miner) + + // Target state now includes a miner credential + let target = + TargetState { + nonce, + block_number, + epoch_time, + current_hash, + leading_zeros, + target_number, + miner: blake2b_256(serialized_miner), + } + + let found_bytearray = + target + |> builtin.serialise_data() + |> sha2_256() + |> sha2_256() + + let (found_target_number, found_leading_zeros) = + fortuna.format_found_bytearray(found_bytearray) + + // Check output datum contains correct epoch time, block number, hash, and leading zeros + // Check for every divisible by 2016 block: + // - Epoch time resets + // - leading zeros is adjusted based on percent of hardcoded target time for 2016 blocks vs epoch time + // Spend(8) requirement: Expect Output Datum to be of type Statev2 + expect Statev2 { + epoch_time: out_epoch_time, + block_number: out_block_number, + current_hash: out_current_hash, + leading_zeros: out_leading_zeros, + target_number: out_target_number, + current_posix_time: out_current_posix_time, + merkle_root: out_merkle, + }: Statev2 = get_inline_datum(out_datum) + + let block_number_as_bytes = integer_to_bytes(out_block_number, "") + + let expected_output_value = + value.from_asset( + tunav2_minting_policy, + bytearray.concat(fortunav2.big_tuna_prefix, own_script_hash), + 1, + ) + |> value.add( + tunav2_minting_policy, + bytearray.concat( + fortunav2.counter_prefix, + block_number_as_bytes, + ), + 1, + ) + + and { + // Posix time is in milliseconds + // Spend(1) requirement: Time range span is 3 minutes or less and inclusive + !upper_is_inclusive, + lower_is_inclusive, + upper_range - lower_range <= 180000, + // In case you are wondering here is what enables pools + // A miner can be a pkh or an nft + // Nfts can come from any input, even validators + // So any validator logic can be enforced to run along with fortuna + // Spend(2) requirement: Validate miner is made the tx + when miner is { + Pkh(signer, _) -> list.has(transaction.extra_signatories, signer) + Nft { policy: nft_policy, name: nft_name, output_index, .. } -> { + let quantity = + utils.list_at(outputs, output_index).value + |> value.to_dict + |> dict.to_pairs + |> quantity_of(nft_policy, nft_name) + + quantity == 1 + } + }, + // Mining Difficulty Met + // Spend(3) requirement: Found difficulty is less than or equal to the current difficulty + // We do this by checking the leading zeros and the difficulty number + or { + found_leading_zeros > leading_zeros, + and { + found_leading_zeros == leading_zeros, + found_target_number < target_number, + }, + }, + // Spend(4) requirement: Input has master token + quantity_of( + in_value |> value.to_dict |> dict.to_pairs, + tunav2_minting_policy, + bytearray.concat(fortunav2.big_tuna_prefix, own_script_hash), + ) == 1, + // Spend(7) requirement: Output has same tokens as input + expected_output_value == value.without_lovelace(out_value), + // Spend(9) requirement: Check output has correct difficulty number, leading zeros, and epoch time + if block_number % epoch_number == 0 && block_number > 0 { + // use total epoch time with target epoch time to get difficulty adjustment ratio + // ratio maxes out at 4/1 and mins to 1/4 + let total_epoch_time = + epoch_time + averaged_current_time - current_posix_time + let (adjustment_numerator, adjustment_denominator) = + fortuna.get_difficulty_adjustment(total_epoch_time) + // Now use ratio to find new leading zeros difficulty + let (new_difficulty, new_leading_zeroes) = + fortuna.get_new_difficulty( + target_number, + leading_zeros, + adjustment_numerator, + adjustment_denominator, + ) + + and { + new_leading_zeroes == out_leading_zeros, + new_difficulty == out_target_number, + 0 == out_epoch_time, + } + } else { + let new_epoch_time = + epoch_time + averaged_current_time - current_posix_time + + and { + leading_zeros == out_leading_zeros, + target_number == out_target_number, + new_epoch_time == out_epoch_time, + } + }, + // Spend(10) requirement: Output posix time is the averaged current time + out_current_posix_time == averaged_current_time, + // Spend(11) requirement: Output block number is the input block number + 1 + block_number + 1 == out_block_number, + // Spend(12) requirement: Output current hash is the found hash + out_current_hash == found_bytearray, + // Spend(13) requirement: Check output merkle is correct + insert( + merkle_root |> from_root, + found_bytearray |> blake2b_256, + found_bytearray, + merkle_proof_list, + ) == from_root(out_merkle), + // Spend(14) requirement: Data size doesn't exceed 1/4 tx size + serialized_miner + |> builtin.length_of_bytearray() + |> builtin.less_than_equals_integer(4096), + builtin.length_of_bytearray(nonce) <= 32, + } + } + + Upgrade -> { + let ScriptContext { transaction, purpose } = ctx + + expect Spend(own_reference) = purpose + + let Transaction { redeemers, inputs, .. } = transaction + + let Output { address, value, .. } = + resolve_output_reference(inputs, own_reference) + + let upgrade_rdmr = expect_first(redeemers, Mint(tunav2_minting_policy)) + + expect FinalizeNomination { .. }: TunaAction = upgrade_rdmr + + expect ScriptCredential(spend_hash) = address.payment_credential + + let name = bytearray.concat(fortunav2.big_tuna_prefix, spend_hash) + + value.quantity_of(value, tunav2_minting_policy, name) == 1 + } + } + } +} diff --git a/samples/Untyped Plutus Core/add_numbers.uplc b/samples/Untyped Plutus Core/add_numbers.uplc new file mode 100644 index 0000000000..6994f88b32 --- /dev/null +++ b/samples/Untyped Plutus Core/add_numbers.uplc @@ -0,0 +1,4 @@ +(program + 1.0.0 + [ [ (builtin addInteger) (con integer 1) ] (con integer 2) ] +) \ No newline at end of file diff --git a/samples/Untyped Plutus Core/always_true.uplc b/samples/Untyped Plutus Core/always_true.uplc new file mode 100644 index 0000000000..5934ebe7a1 --- /dev/null +++ b/samples/Untyped Plutus Core/always_true.uplc @@ -0,0 +1,120 @@ +(program + 1.0.0 + [ + (lam + i_0 + [ + (lam + i_1 + [ + (lam + i_2 + [ + (lam + i_3 + (lam + i_4 + [ + (lam + i_5 + (lam + i_6 + [ + (lam + i_7 + (lam + i_8 + (force + [ + [ + [ i_3 (con bool True) ] + (delay (con unit ())) + ] + (delay [ (error ) (force (error )) ]) + ] + ) + ) + ) + [ + (lam + i_9 + (force + [ + [ + [ + i_3 + [ + [ + (builtin equalsInteger) + (con integer 0) + ] + [ i_2 i_9 ] + ] + ] + (delay + (force + [ + [ + [ i_0 [ i_1 i_9 ] ] + (delay (con unit ())) + ] + (delay (error )) + ] + ) + ) + ] + (delay (error )) + ] + ) + ) + [ (builtin unConstrData) i_6 ] + ] + ] + ) + ) + [ + (lam + i_10 + (force + [ + [ + [ + i_3 + [ + [ (builtin equalsInteger) (con integer 0) ] + [ i_2 i_10 ] + ] + ] + (delay + (force + [ + [ + [ i_0 [ i_1 i_10 ] ] + (delay (con unit ())) + ] + (delay (error )) + ] + ) + ) + ] + (delay (error )) + ] + ) + ) + [ (builtin unConstrData) i_4 ] + ] + ] + ) + ) + (force (builtin ifThenElse)) + ] + ) + (force (force (builtin fstPair))) + ] + ) + (force (force (builtin sndPair))) + ] + ) + (force (force (builtin chooseList))) + ] +) \ No newline at end of file diff --git a/vendor/README.md b/vendor/README.md index bb42a892d3..44a82139e1 100644 --- a/vendor/README.md +++ b/vendor/README.md @@ -28,6 +28,7 @@ This is a list of grammars that Linguist selects to provide syntax highlighting - **Adblock Filter List:** [AdguardTeam/VscodeAdblockSyntax](https://github.com/AdguardTeam/VscodeAdblockSyntax) - **Adobe Font Metrics:** [Alhadis/language-fontforge](https://github.com/Alhadis/language-fontforge) - **Agda:** [agda/agda-github-syntax-highlighting](https://github.com/agda/agda-github-syntax-highlighting) +- **Aiken:** [aiken-lang/vscode-aiken](https://github.com/aiken-lang/vscode-aiken) - **Alloy:** [macekond/Alloy.tmbundle](https://github.com/macekond/Alloy.tmbundle) - **Alpine Abuild:** [atom/language-shellscript](https://github.com/atom/language-shellscript) - **Altium Designer:** [textmate/ini.tmbundle](https://github.com/textmate/ini.tmbundle) @@ -587,6 +588,7 @@ This is a list of grammars that Linguist selects to provide syntax highlighting - **Unix Assembly:** [calculuswhiz/Assembly-Syntax-Definition](https://github.com/calculuswhiz/Assembly-Syntax-Definition) - **Uno:** [dotnet/csharp-tmLanguage](https://github.com/dotnet/csharp-tmLanguage) - **UnrealScript:** [textmate/java.tmbundle](https://github.com/textmate/java.tmbundle) +- **Untyped Plutus Core:** [aiken-lang/vscode-aiken](https://github.com/aiken-lang/vscode-aiken) - **UrWeb:** [gwalborn/UrWeb-Language-Definition](https://github.com/gwalborn/UrWeb-Language-Definition) - **V:** [0x9ef/vscode-vlang](https://github.com/0x9ef/vscode-vlang) - **VBA:** [serkonda7/vscode-vba](https://github.com/serkonda7/vscode-vba) diff --git a/vendor/grammars/vscode-aiken b/vendor/grammars/vscode-aiken new file mode 160000 index 0000000000..cde3a4c1b3 --- /dev/null +++ b/vendor/grammars/vscode-aiken @@ -0,0 +1 @@ +Subproject commit cde3a4c1b335708f514843e902b400558fb8e29f diff --git a/vendor/licenses/git_submodule/vscode-aiken.dep.yml b/vendor/licenses/git_submodule/vscode-aiken.dep.yml new file mode 100644 index 0000000000..4b76f9bddc --- /dev/null +++ b/vendor/licenses/git_submodule/vscode-aiken.dep.yml @@ -0,0 +1,211 @@ +--- +name: vscode-aiken +version: cde3a4c1b335708f514843e902b400558fb8e29f +type: git_submodule +homepage: https://github.com/aiken-lang/vscode-aiken.git +license: apache-2.0 +licenses: +- sources: LICENSE + text: |2 + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Lucas Rosa + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +notices: []