forked from public/foundryvtt-swade-fr
Compare commits
36 Commits
Arnok_Upda
...
master
Author | SHA1 | Date | |
---|---|---|---|
b5221420ec | |||
5221d5ceed | |||
f4607cbd8d | |||
c3c8d3e5bc | |||
3a28de3e07 | |||
d5359dd174 | |||
4d95d6c914 | |||
5421017b7c | |||
ae3c632315 | |||
2a9a84c7f8 | |||
4375fa4d76 | |||
abf9e4577a | |||
83307b5739 | |||
15109ddea8 | |||
f46129e20f | |||
1cc3bca660 | |||
c0d94d1fb0 | |||
b63de0e7dd | |||
28e41b5371 | |||
ea99bbc226 | |||
cd44a6dab2 | |||
5f84fd08e8 | |||
67712c32cb | |||
dbba2040f6 | |||
422d47ed71 | |||
68ebd6133b | |||
57a1b51aa4 | |||
f901f25e89 | |||
acadff5a54 | |||
1c15ba9140 | |||
e922fd16f1 | |||
8e03faa61e | |||
51cc2803f7 | |||
35ec35bd2e | |||
cd64f10d14 | |||
884bdff45c |
20
changelog.md
20
changelog.md
@ -1,4 +1,22 @@
|
||||
v0.20.0.0 + v0.20.0.1
|
||||
# 2.0.0
|
||||
|
||||
SWADE v2 (Fvtt v10) + fix sur pilotage
|
||||
|
||||
# 0.21.0.0
|
||||
# 0.21.1.0
|
||||
|
||||
Alignement sur SWADE v1.1.X et traductions de clés manquantes
|
||||
|
||||
# 0.21.0.0
|
||||
|
||||
Alignement sur SWADE v1.1.X
|
||||
|
||||
# 0.20.0.3
|
||||
|
||||
Repository migration
|
||||
|
||||
|
||||
# 0.20.0.1
|
||||
|
||||
Migration of the the module,change of the module owner
|
||||
Corrections sur le manifest
|
||||
|
92
module.json
92
module.json
@ -1,69 +1,75 @@
|
||||
{
|
||||
"name": "swade-fr",
|
||||
"title": "SWADE - Traduction française",
|
||||
"description": "Ajoute le français (FRANCE) au système SWADE.<p> Une traduction Babele des compendiums est inclue mais optionnelle. (Encore en test, à utiliser à vos risques et périls !) </p> <p>*** Join the official Discord server: <a href='https://discord.gg/foundryvtt'> Official Discord</a></p><p>*** Rejoignez la communauté Francophone: <a href='https://discord.gg/pPSDNJk'>Discord francophone</a></p>",
|
||||
"version": "0.20.0.2",
|
||||
"minimumCoreVersion" : "0.7.9",
|
||||
"compatibleCoreVersion" : "9",
|
||||
"author": "BoboursToutCool, Gronyon, Kyane, LeRatierBretonnien, Sasmira, U~man,X.O. de Vorcen",
|
||||
"description": "Ajoute le français (FRANCE) au système SWADE.<p> <p>*** Join the official Discord server: <a href=\"https://discord.gg/foundryvtt\"> Official Discord</a></p><p>*** Rejoignez la communauté Francophone: <a href=\"https://discord.gg/pPSDNJk\">Discord francophone</a></p>",
|
||||
"authors": [
|
||||
{
|
||||
"name": "BoboursToutCool",
|
||||
"discord": "BoboursToutCool#9787"
|
||||
"name": "BoboursToutCool",
|
||||
"discord": "BoboursToutCool#9787",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"name": "Cyril - Gronyon- Ronseaux",
|
||||
"discord": "Gronyon#0843"
|
||||
"name": "Cyril - Gronyon- Ronseaux",
|
||||
"discord": "Gronyon#0843",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"name": "Kyane von Schnitzel",
|
||||
"discord": "Kyane von Schnitzel#8654"
|
||||
"name": "Kyane von Schnitzel",
|
||||
"discord": "Kyane von Schnitzel#8654",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"name": "LeRatierBretonnien",
|
||||
"discord": "LeRatierBretonnien#2065"
|
||||
"name": "LeRatierBretonnien",
|
||||
"discord": "LeRatierBretonnien#2065",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"name": "Sasmira",
|
||||
"discord": "Sasmira#4566"
|
||||
"name": "Sasmira",
|
||||
"discord": "Sasmira#4566",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"name": "U~man",
|
||||
"discord": "U~man#2374"
|
||||
"name": "U~man",
|
||||
"discord": "U~man#2374",
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"name" : "X.O. de Vorcen"
|
||||
"name": "X.O. de Vorcen",
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "swade",
|
||||
"type": "system"
|
||||
},
|
||||
{
|
||||
"name": "dice-so-nice",
|
||||
"type": "module",
|
||||
"manifest": "https://gitlab.com/riccisi/foundryvtt-dice-so-nice/-/raw/master/module/module.json"
|
||||
}
|
||||
],
|
||||
|
||||
"scripts": [],
|
||||
"styles": ["swade-fr.css"],
|
||||
"packs": [],
|
||||
],
|
||||
"url": "https://www.uberwald.me/gitea/public/foundryvtt-swade-fr",
|
||||
"version": "3.0.1",
|
||||
"esmodules": [
|
||||
"modules/swade-fr-init.js"
|
||||
],
|
||||
|
||||
"styles": [
|
||||
"swade-fr.css"
|
||||
],
|
||||
"languages": [
|
||||
{
|
||||
"lang": "fr",
|
||||
"name": "French (FRANCE)",
|
||||
"path": "fr.json"
|
||||
"path": "fr.json",
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
|
||||
"url": "https://gitlab.com/LeRatierBretonnien/foundryvtt-swade-fr",
|
||||
"manifest": "https://gitlab.com/LeRatierBretonnien/foundryvtt-swade-fr/-/raw/master/module.json",
|
||||
"download": "https://gitlab.com/LeRatierBretonnien/foundryvtt-swade-fr/-/archive/master/foundryvtt-swade-fr-master.zip"
|
||||
}
|
||||
"manifest": "https://www.uberwald.me/gitea/public/foundryvtt-swade-fr/raw/branch/master/module.json",
|
||||
"download": "https://www.uberwald.me/gitea/public/foundryvtt-swade-fr/archive/foundryvtt-swade-fr-3.0.1.zip",
|
||||
"name": "swade-fr",
|
||||
"id": "swade-fr",
|
||||
"relationships": {
|
||||
"requires": [
|
||||
],
|
||||
"systems": [
|
||||
{
|
||||
"id": "swade",
|
||||
"type": "system",
|
||||
"compatibility": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
"compatibility": {
|
||||
"minimum": "9",
|
||||
"verified": "11"
|
||||
}
|
||||
}
|
||||
|
@ -12,3 +12,10 @@ Hooks.once('init', () => {
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/************************************************************************************/
|
||||
Hooks.once('ready', () => {
|
||||
|
||||
CONFIG.SWADE.vehicles.opSkills = ['', 'Navigation', 'Conduite', 'Pilotage', 'Équitation']
|
||||
|
||||
})
|
||||
|
36
tools/detect_missing_strings.lua
Normal file
36
tools/detect_missing_strings.lua
Normal file
@ -0,0 +1,36 @@
|
||||
package.path = package.path .. ";luajson/?.lua"
|
||||
local JSON = require"json"
|
||||
|
||||
local enjsonf = "en.json"
|
||||
local frjsonf = "../fr.json"
|
||||
|
||||
local fp = io.open(enjsonf, "r")
|
||||
local entags = JSON.decode( fp:read("*a") )
|
||||
fp:close()
|
||||
entags = entags.SWADE
|
||||
|
||||
fp = io.open(frjsonf, "r")
|
||||
local frtags = JSON.decode( fp:read("*a") )
|
||||
fp:close()
|
||||
frtags = frtags.SWADE
|
||||
|
||||
local todisplay = {}
|
||||
for tag, value in pairs(entags) do
|
||||
if not frtags[tag] then
|
||||
if type(value) == "table" then
|
||||
value = JSON.encode(value)
|
||||
end
|
||||
todisplay[#todisplay+1] = { tag=tag, value = value }
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(todisplay, function (a, b)
|
||||
return a.tag < b.tag
|
||||
end
|
||||
)
|
||||
for _, tagDef in pairs(todisplay) do
|
||||
print("\"" .. tagDef.tag .."\":" .. tagDef.value)
|
||||
|
||||
end
|
||||
|
||||
|
677
tools/en.json
Normal file
677
tools/en.json
Normal file
@ -0,0 +1,677 @@
|
||||
{
|
||||
"SWADE": {
|
||||
"ButtonReset": "Reset",
|
||||
"ButtonSubmit": "Submit",
|
||||
"CommunityCharSheet": "Legacy Character Sheet",
|
||||
"CommunityNPCSheet": "Community NPC Sheet",
|
||||
"CommunityVicSheet": "Community Vehicle Sheet",
|
||||
"CommunityItemSheet": "Community Item Sheet",
|
||||
"ItemSheet": "Savage Worlds Item Sheet",
|
||||
"Expand": "Expand",
|
||||
"OfficialSheet": "Savage Worlds Official Sheet by Pinnacle",
|
||||
"ModHint": "Make sure your modifier includes a + or -",
|
||||
"ActionDeckPresetLight": "Action Deck (Light)",
|
||||
"ActionDeckPresetDark": "Action Deck (Dark)",
|
||||
"Name": "Name",
|
||||
"Tough": "Toughness",
|
||||
"Race": "Race",
|
||||
"Archetype": "Archetype",
|
||||
"Pace": "Pace",
|
||||
"Running": "Running",
|
||||
"RunningDie": "Running Die",
|
||||
"RollRun": "Click to run",
|
||||
"Adv": "Advances",
|
||||
"Rank": "Rank",
|
||||
"Inv": "Gear",
|
||||
"Summary": "Traits",
|
||||
"Pow": "Powers",
|
||||
"WildCard": "Wild Card",
|
||||
"Extra": "Extra",
|
||||
"Bennies": "Bennies",
|
||||
"TraitMod": "Trait Modifier",
|
||||
"SkillTest": "Skill Test",
|
||||
"AttributeTest": "Attribute Test",
|
||||
"Attributes": "Attributes",
|
||||
"Derived": "Derived Stats",
|
||||
"AttrAgi": "Agility",
|
||||
"AttrSma": "Smarts",
|
||||
"AnimalSmarts": "Animal Smarts",
|
||||
"AnimalSmartsMarker": "A",
|
||||
"AttrSpr": "Spirit",
|
||||
"AttrStr": "Strength",
|
||||
"AttrVig": "Vigor",
|
||||
"Tweaks": "Tweaks",
|
||||
"AttrAgiShort": "Agi",
|
||||
"AttrSmaShort": "Sma",
|
||||
"AttrSprShort": "Spi",
|
||||
"AttrStrShort": "Str",
|
||||
"AttrVigShort": "Vig",
|
||||
"Skills": "Skills",
|
||||
"Unskilled": "Unskilled",
|
||||
"CoreSkill": "Core Skill",
|
||||
"CoreSkills": "Core Skills",
|
||||
"LinkAttr": "Linked Attribute",
|
||||
"ArcaneSkill": "Arcane Skill",
|
||||
"ActivateArcaneDevice": "Activate Device",
|
||||
"Die": "Die",
|
||||
"WildDie": "Wild Die",
|
||||
"Modifier": "Modifier",
|
||||
"BenniesSpend": "Spend a Benny",
|
||||
"BenniesGive": "Give a Benny",
|
||||
"BenniesRefresh": "Refresh Bennies",
|
||||
"AllBenniesRefresh": "Refresh All Bennies",
|
||||
"BenniesRefreshMessage": "Bennies have been refreshed!",
|
||||
"BenniesRefreshAllMessage": "All Bennies have been refreshed!",
|
||||
"BenniesGMRefreshMessage": "GM Bennies have been refreshed!",
|
||||
"BenniesAddMessage": "was given a new Benny!",
|
||||
"BenniesGMAddMessage": "took an additional Benny!",
|
||||
"BenniesSpendMessage": "spends a Benny!",
|
||||
"BenniesMax": "Bennies Reset",
|
||||
"EditSkill": "Edit Skill",
|
||||
"EdEdge": "Edit Edge",
|
||||
"EdHind": "Edit Hindrance",
|
||||
"Ed": "Edit",
|
||||
"Del": "Delete",
|
||||
"Arcane": "Arcane",
|
||||
"All": "All",
|
||||
"HasPowers": "Has Powers",
|
||||
"Weapons": "Weapons",
|
||||
"MeleeWeapons": "Melee Weapons",
|
||||
"RangedWeapons": "Ranged Weapons",
|
||||
"Armors": "Armors",
|
||||
"WeaponsAndArmor": "Weapons & Armor",
|
||||
"Shields": "Shields",
|
||||
"Biography": "Biography",
|
||||
"Shaken": "Shaken",
|
||||
"Distr": "Distracted",
|
||||
"Vuln": "Vulnerable",
|
||||
"Stunned": "Stunned",
|
||||
"Entangled": "Entangled",
|
||||
"Bound": "Bound",
|
||||
"Aiming": "Aiming",
|
||||
"Berserk": "Berserk",
|
||||
"Defending": "Defending",
|
||||
"Flying": "Flying",
|
||||
"Follow": "Follow {name}",
|
||||
"Unfollow": "Unfollow {name}",
|
||||
"MakeGroupLeader": "Make Group Leader",
|
||||
"RemoveGroupLeader": "Remove Group Leader",
|
||||
"AddTokenFollowers": "Add Selected Tokens as Followers",
|
||||
"GroupByName": "Group by Name",
|
||||
"SetGroupColor": "Set Group Color",
|
||||
"SelectColor": "Select a color",
|
||||
"Holding": "Holding",
|
||||
"Hold": "Hold",
|
||||
"ActAfterCurrentCombatant": "Act After Current Combatant",
|
||||
"ActBeforeCurrentCombatant": "Act now",
|
||||
"LoseTurn": "Toggle Lose Turn",
|
||||
"Frightened": "Frightened",
|
||||
"Encumbered": "Encumbered",
|
||||
"Prone": "Prone",
|
||||
"BleedingOut": "Bleeding Out",
|
||||
"Diseased": "Diseased",
|
||||
"HeartAttack": "Heart Attack",
|
||||
"OnFire": "On Fire",
|
||||
"Poisoned": "Poisoned",
|
||||
"Reach": "Reach",
|
||||
"Torch": "Torch",
|
||||
"Invisible": "Invisible",
|
||||
"Smite": "Smite",
|
||||
"Armor": "Armor",
|
||||
"Protection": "Protection",
|
||||
"Dmg": "Damage",
|
||||
"DmgOver": "Action Damage",
|
||||
"Ap": "AP",
|
||||
"APLong": "Armor Piercing",
|
||||
"RoF": "RoF",
|
||||
"Mag": "Shots",
|
||||
"Parry": "Parry",
|
||||
"Notes": "Notes",
|
||||
"ActionsEffects": "Actions & Effects",
|
||||
"Actions": "Actions",
|
||||
"DmgModUni": "Damage Modifier",
|
||||
"SkillModUni": "Trait Modifier",
|
||||
"ShotsUsed": "Shots Used",
|
||||
"NaturalArmor": "Natural Armor",
|
||||
"HitLocations": "Hit Locations",
|
||||
"Head": "Head",
|
||||
"Torso": "Torso",
|
||||
"Arms": "Arms",
|
||||
"Legs": "Legs",
|
||||
"MinStrLong": "Minimum Strength",
|
||||
"MinStr": "Min. Strength",
|
||||
"MinStrShort": "Min.Str",
|
||||
"Desc": "Description",
|
||||
"Special": "Special Abilities",
|
||||
"Details": "Details",
|
||||
"Equippable": "Equippable",
|
||||
"ArcaneDevice": "Arcane Device",
|
||||
"Misc": "Misc",
|
||||
"QuickAccess": "Quick Access",
|
||||
"Effects": "Effects",
|
||||
"Hindrances": "Hindrances",
|
||||
"MajHind": "Major Hindrance",
|
||||
"Major": "(Major)",
|
||||
"Minor": "(Minor)",
|
||||
"Edges": "Edges",
|
||||
"ArcBack": "Arcane Background",
|
||||
"Req": "Requirements",
|
||||
"Conv": "Conviction",
|
||||
"UseConv": "Calls on their Conviction!",
|
||||
"Hesitant": "Hesitant",
|
||||
"LevelHeaded": "Level Headed",
|
||||
"ImprovedLevelHeaded": "Improved Level Headed",
|
||||
"AutoCalc": "Automatic Calculations",
|
||||
"InclArmor": "Automatically calculates Toughness, including armor",
|
||||
"Label": "Label",
|
||||
"HasMaxValue": "Has Max Value",
|
||||
"Enabled": "Enabled",
|
||||
"Currency": "Currency",
|
||||
"CarryWeight": "Encumbrance",
|
||||
"Eqd": "Eq'd",
|
||||
"Equipped": "Equipped",
|
||||
"Unequipped": "Unequipped",
|
||||
"Quantity": "Quantity",
|
||||
"Weight": "Weight",
|
||||
"Price": "Cost",
|
||||
"PP": "Power Points",
|
||||
"PPShort": "P. Points",
|
||||
"PPCost": "PP Cost",
|
||||
"CurPP": "Current Power Points",
|
||||
"MaxPP": "Maximum Power Points",
|
||||
"Dur": "Duration",
|
||||
"Trap": "Trapping",
|
||||
"Wound": "Wound",
|
||||
"Wounds": "Wounds",
|
||||
"WoundsMax": "Max Wounds",
|
||||
"Fatigue": "Fatigue",
|
||||
"FatigueMax": "Max Fatigue",
|
||||
"Incap": "Incapacitated",
|
||||
"Init": "Initiative",
|
||||
"DrawInit": "Draw Initiative",
|
||||
"ActionDeckReset": "Reshuffle Action Deck",
|
||||
"ActionDeckResetNotification": "Action Deck reshuffled",
|
||||
"InitDraw": "drew the...",
|
||||
"General": "General",
|
||||
"Addi": "Additional",
|
||||
"Status": "Status",
|
||||
"Handling": "Handling",
|
||||
"Topspeed": "Top Speed",
|
||||
"OutOfControl": "Out of Control",
|
||||
"Wrecked": "Wrecked",
|
||||
"Item": "Item",
|
||||
"Cargo": "Cargo",
|
||||
"MaxCargo": "Maximum Cargo Weight",
|
||||
"CargoWeight": "Cargo Weight",
|
||||
"Mods": "Mods",
|
||||
"ModSlots": "Mod Slots",
|
||||
"MaxMods": "Maximum Mods",
|
||||
"Operator": "Operator",
|
||||
"ManCheck": "Maneuver Check",
|
||||
"AltSkill": "Alternative Skill",
|
||||
"Class": "Classification",
|
||||
"Vehicular": "Vehicular Weapon",
|
||||
"VehicleMod": "Vehicle Mod",
|
||||
"OpSkill": "Operation Skill",
|
||||
"ShowBennyAnimation": "Show Benny Animation",
|
||||
"ShowBennyAnimationDesc": "Play an animation when spending a Benny. Only available if Dice So Nice! is installed",
|
||||
"WildDiePreset": "Wild Die Theme",
|
||||
"WildDiePresetDesc": "Select a DSN Theme for your Wild Die. Select 'Custom' if you want to create your own or 'None' if you don't want your Wild Die to look differently. Please keep in mind that self-created Wild Dies currently can only be seen by you. Other players will see them differently.",
|
||||
"SettingConf": "Setting Configurator",
|
||||
"SettingConfLabel": "Open Setting Configurator",
|
||||
"SettingConfDesc": "Fine tune the system to fit the setting you're playing",
|
||||
"DiceConf": "Dice Configuration",
|
||||
"DiceConfDesc": "Configure the settings for the SWADE/Dice So Nice integration",
|
||||
"DiceConfLabel": "Open Dice Configuration",
|
||||
"Formula": "Formula",
|
||||
"SitMod": "Situational Mod",
|
||||
"RollMode": "Roll Mode",
|
||||
"RollExample": "eg. +1d6x or -2",
|
||||
"Roll": "Roll",
|
||||
"RollRaise": "Roll Raise",
|
||||
"GroupRoll": "Group Roll",
|
||||
"Ok": "Ok",
|
||||
"Cancel": "Cancel",
|
||||
"Redraw": "Draw Card",
|
||||
"Add": "Add",
|
||||
"ConfigInit": "Configure Initiative",
|
||||
"IgnWounds": "Ignore Wounds",
|
||||
"Size": "Size",
|
||||
"Scale": "Scale",
|
||||
"PickACard": "Pick a card for {name}",
|
||||
"NoCardsLeft": "There are not enough cards left in the deck! Only {current} of {needed} needed cards are available",
|
||||
"OldCard": "Old card",
|
||||
"ActionCard": "Action Card",
|
||||
"ConvictionActive": "Conviction Active",
|
||||
"ConvictionActivate": "Calls upon their conviction!",
|
||||
"ConvictionExtend": "Do you want to extend your conviction?",
|
||||
"ConvictionEnd": "wavers in their conviction",
|
||||
"NoBennies": "You don't have enough bennies!",
|
||||
"SBT": "Small Blast Template",
|
||||
"MBT": "Medium Blast Template",
|
||||
"LBT": "Large Blast Template",
|
||||
"Cone": "Cone Template",
|
||||
"GM": "Game Master",
|
||||
"Vehicles": "Vehicles",
|
||||
"AddStats": "Additional Stats",
|
||||
"Actors": "Actors",
|
||||
"Items": "Items",
|
||||
"Max": "Max",
|
||||
"Curr": "Current",
|
||||
"DSNNone": "- None -",
|
||||
"Reload": "Reload",
|
||||
"Refresh": "Refresh this card",
|
||||
"Ammo": "Ammunition",
|
||||
"ReloadUnneeded": "The weapon is already fully loaded",
|
||||
"ReloadSuccess": "The weapon has been successfully reloaded",
|
||||
"NotEnoughAmmo": "You don't have enough ammunition for this action!",
|
||||
"NotEnoughAmmoToReload": "There's not enough ammo for a full reload",
|
||||
"AutoReload": "Doesn't need Reload action",
|
||||
"NoAmmoSet": "There is no ammunition set",
|
||||
"Trapping": "Trappings",
|
||||
"AddBenny": "Add a Benny",
|
||||
"CurrentBennies": "Current Bennies",
|
||||
"RerollWithBenny": "Reroll with Benny",
|
||||
"FreeReroll": "Free Reroll",
|
||||
"AutoCalcParry": "Automatically calculates Parry, including Shields",
|
||||
"Subtype": "Subtype",
|
||||
"SpecialAbility": "Special Ability",
|
||||
"SpecialAbilities": "Special Abilities",
|
||||
"RacialAbilities": "Racial Abilities",
|
||||
"ArchetypeAbilities": "Archetype Abilities",
|
||||
"About": "About",
|
||||
"EdgesHindrances": "Edges & Hindrances",
|
||||
"GrantsPowers": "Grants Powers",
|
||||
"Crew": "Required Crew",
|
||||
"Passengers": "Optional Passengers",
|
||||
"String": "Text",
|
||||
"Number": "Number",
|
||||
"Checkbox": "Checkbox",
|
||||
"WealthDie": {
|
||||
"Label": "Wealth Die",
|
||||
"Broke": {
|
||||
"Label": "Broke",
|
||||
"Hint": "You are Broke!"
|
||||
}
|
||||
},
|
||||
"TraitRolls": "Trait Rolls",
|
||||
"Trait": "Trait",
|
||||
"AltTrait": "Action Trait",
|
||||
"ClickSuggestions": "Click for suggestions",
|
||||
"Quick": "Quick",
|
||||
"BonusDamage": "Bonus Damage",
|
||||
"Joker": "Joker",
|
||||
"Subtract": "Subtract",
|
||||
"DmgRolls": "Damage Rolls",
|
||||
"SubtractPPDesc": "Subtract PP Cost from the available PP",
|
||||
"AddPPDesc": "Add PP Cost to the available PP",
|
||||
"RightClickZoom": "Right-Click to Zoom",
|
||||
"SysMigrationWarning": "Your SWADE system data is from too old a Foundry version and cannot be reliably migrated to the latest version. The process will be attempted, but errors may occur.",
|
||||
"DeckShuffled": "Card Deck automatically shuffled",
|
||||
"BasePace": "Base Pace",
|
||||
"WarningPackLocked": "You cannot edit a locked Compendium. Please unlock it first",
|
||||
"ActionCardEditor": "Action Card Editor",
|
||||
"CardFace": "Card Face",
|
||||
"CardValue": "Value",
|
||||
"CardSuit": "Suit",
|
||||
"IsJoker": "Is Joker",
|
||||
"OpenACEditor": "Open in Action Card Editor",
|
||||
"Yes": "Yes",
|
||||
"No": "No",
|
||||
"ShowCharacterSummary": "Show Character Summary",
|
||||
"CharacterSummary": "Character Summary",
|
||||
"CopyHtml": "Copy HTML",
|
||||
"CopyMarkdown": "Copy Markdown",
|
||||
"Imperial": "Imperial",
|
||||
"Metric": "Metric",
|
||||
"ChangeBennyWarning": "Changing these settings will require you to refresh the page!",
|
||||
"3DBennies": "3D Bennies",
|
||||
"WealthSelectionNoneOther": "None/Other",
|
||||
"Modifiers": "Modifiers",
|
||||
"ActionTraitMod": "Action Trait Modifier",
|
||||
"ActionDmgMod": "Action Damage Modifier",
|
||||
"ItemTraitMod": "Item Trait Modifier",
|
||||
"ItemDmgMod": "Item Damage Modifier",
|
||||
"Duplicates": "Duplicates Found",
|
||||
"DuplicateItemsBodyText": "While adding the {type} <strong>{name}</strong> to the Actor {target} the following duplicates (by name and type) have been found. The new items have been renamed to reflect their source.",
|
||||
"NoActionDeckFound": "No Action Deck found, creating one",
|
||||
"NoActionDeckDiscardPileFound": "No Action Deck Discard Pile found, creating one",
|
||||
"Wealth": "Wealth",
|
||||
"Characters": "Characters",
|
||||
"SettingRules": "Setting Rules",
|
||||
"WorldBasics": "World Basics",
|
||||
"MAPenalty": {
|
||||
"Label": "Multi-Action Penalty",
|
||||
"None": "None"
|
||||
},
|
||||
"Mod": {
|
||||
"Custom": {
|
||||
"Add": "Add Custom Modifier"
|
||||
},
|
||||
"Support": "Support",
|
||||
"Preset": {
|
||||
"Choose": "Choose a Preset...",
|
||||
"Add": "Add Preset"
|
||||
},
|
||||
"Value": "Value",
|
||||
"Ignore": "Ignore?"
|
||||
},
|
||||
"Illumination": {
|
||||
"_name": "Illumination",
|
||||
"Dim": "Dim Light",
|
||||
"Dark": "Darkness",
|
||||
"Pitch": "Pitch Black"
|
||||
},
|
||||
"Cover": {
|
||||
"_name": "Cover",
|
||||
"Shield": "Cover - Shield",
|
||||
"Light": "Light Cover",
|
||||
"Medium": "Medium Cover",
|
||||
"Heavy": "Heavy Cover",
|
||||
"Total": "Total Cover"
|
||||
},
|
||||
"Range": {
|
||||
"_name": "Range",
|
||||
"Medium": "Medium Range",
|
||||
"Long": "Long Range",
|
||||
"Extreme": "Extreme Range"
|
||||
},
|
||||
"ModOther": "Other Modifiers",
|
||||
"Snapfire": "Snapfire",
|
||||
"UnstablePlatform": "Unstable Platform",
|
||||
"CannotAddRaceToRace": "You cannot add a race/archetype to a race/archetype!",
|
||||
"LayOutChaseWithDeck": "Use Deck to lay out Chase",
|
||||
"ClearChaseCards": "Clear Chase cards from Scene",
|
||||
"NoSceneAvailable": "There is no scene available or you've disabled the canvas",
|
||||
"ChaseCardsCleared": "All chase cards have been removed from the scene.",
|
||||
"SetUpChase": "Set Up Chase",
|
||||
"NotADeckCompendium": "The compendium you selected doesn't appear to be a legacy deck",
|
||||
"ConvertToDeck": "Convert to Deck",
|
||||
"Expiration": {
|
||||
"TabLabel": "Turn Behavior",
|
||||
"Description": "<p>Effects expire on the target's <em>next turn or later</em>.</p>",
|
||||
"Behavior": "Expiration Behavior",
|
||||
"BeginAuto": "Start of Turn, Automatic",
|
||||
"BeginPrompt": "Start of Turn, Prompt",
|
||||
"EndAuto": "End of Turn, Automatic",
|
||||
"EndPrompt": "End of Turn, Prompt",
|
||||
"None": "None",
|
||||
"LooseTurnOnHold": "Lose Turn on Hold"
|
||||
},
|
||||
"RemoveEffectTitle": "Remove {label} ?",
|
||||
"RemoveEffectBody": "<p style=\"text-align: center;\">Remove <strong>{label}</strong> from <strong>{parent}</strong>?</p>",
|
||||
"PPAbbreviation": "PP",
|
||||
"ShowInChat": "Show in chat",
|
||||
"ExpandDescription": "Expand to show description",
|
||||
"Favorite": "Favorite",
|
||||
"Equip": "Equip",
|
||||
"RollDamage": "Roll Damage",
|
||||
"SystemLinks": {
|
||||
"Changelog": "Changelog",
|
||||
"Wiki": "Wiki",
|
||||
"ReportAnIssue": "Report an Issue"
|
||||
},
|
||||
"OpenOrigin": "Open origin",
|
||||
"EffectsTemporary": "Temporary Effects",
|
||||
"EffectsPermanent": "Permanent Effects",
|
||||
"Ranks": {
|
||||
"Novice": "Novice",
|
||||
"Seasoned": "Seasoned",
|
||||
"Veteran": "Veteran",
|
||||
"Heroic": "Heroic",
|
||||
"Legendary": "Legendary"
|
||||
},
|
||||
"Advances": {
|
||||
"EditorTitle": "Advance Editor",
|
||||
"Toggle": "Toggle Advance Planned Status",
|
||||
"Delete": "Delete Advance",
|
||||
"Planned": "Planned",
|
||||
"Modes": {
|
||||
"Legacy": "Legacy",
|
||||
"Expanded": "Expanded"
|
||||
},
|
||||
"Types": {
|
||||
"Edge": "New Edge",
|
||||
"SingleSkill": "Raise Single Skill",
|
||||
"TwoSkills": "Raise Two Skills",
|
||||
"Attribute": "Raise Attribute",
|
||||
"Hindrance": "Decrease Hindrance"
|
||||
},
|
||||
"Number": "Advance number"
|
||||
},
|
||||
"AddAdvance": "Add Advance",
|
||||
"Appearance": "Appearance",
|
||||
"CharacterGoals": "Goals",
|
||||
"Background": "Background",
|
||||
"ActiveEffects": {
|
||||
"Add": "Add Active Effect",
|
||||
"Source": "Source",
|
||||
"ResetDuration": "Reset Duration"
|
||||
},
|
||||
"Keybindings": {
|
||||
"OpenFavoriteCards": {
|
||||
"Name": "Open Favorite Card Hand",
|
||||
"UserConfigLabel": "Favorite Card Hand",
|
||||
"Hint": "You can set your favorite hand in the User Config",
|
||||
"NoCardsWarning": "You don't have a favorite card hand set!"
|
||||
},
|
||||
"Bennies": {
|
||||
"Name": "Spend/Receive a Benny",
|
||||
"Hint": "Press to spend a Benny, hold down Alt while you press the key to receive a Benny instead"
|
||||
}
|
||||
},
|
||||
"Settings": {
|
||||
"HighlightTemplate": {
|
||||
"Name": "Always Highlight Templates",
|
||||
"Hint": "When enabled, Measured Templates alway highlight themselves instead of the grid."
|
||||
},
|
||||
"FantasyCompanionEntangle": {
|
||||
"Name": "Fantasy Companion Entangle",
|
||||
"Hint": "Use the Fantasy Companion Entangle status instead of the Core version. Requires a reload to take effect"
|
||||
},
|
||||
"InitDiscardPile": {
|
||||
"Name": "Initiative Discard Pile"
|
||||
},
|
||||
"InitCardDeck": {
|
||||
"Name": "Card Deck to use for Initiative"
|
||||
},
|
||||
"ParryBase": {
|
||||
"Name": "Localized Fighting Skill Name",
|
||||
"Hint": "The name of the skill which will be used to calculate a characters Parry"
|
||||
},
|
||||
"UseAttributeShorts": {
|
||||
"Name": "Use shortened attribute names",
|
||||
"Hint": "Activate this option if you want to see abbreviated attribute names on the pc and npc sheets, i.e. Agi instead of Agility, Sma instead of Smarts, etc. You'll need to close and reopen all open sheets for this option to take effect."
|
||||
},
|
||||
"WeightUnit": {
|
||||
"Name": "Unit of weight",
|
||||
"Hint": "Sets whether weights are measured in pounds or kilograms. Changing this will NOT change the weights on items, only how the encumbrance is calculated"
|
||||
},
|
||||
"HideNpcItemChatCards": {
|
||||
"Name": "Hide NPC Item Chat Cards",
|
||||
"Hint": "When enabled, NPC Item Chat Cards are automatically sent as a whisper to all Gamemasters instead of being posted for all to see"
|
||||
},
|
||||
"EnableBennyNotify": {
|
||||
"Name": "Benny notifications",
|
||||
"Hint": "Displays chat messages when bennies are taken, given or refreshed."
|
||||
},
|
||||
"HideWC": {
|
||||
"Name": "Hide NPC Wild Cards",
|
||||
"Hint": "Do not show which NPCs are Wild Cards to players"
|
||||
},
|
||||
"CreateInitChat": {
|
||||
"Name": "Create Chat Message for Initiative"
|
||||
},
|
||||
"AutoInit": {
|
||||
"Name": "Automatic Initiative",
|
||||
"Hint": "Automatically draw Action Cards every round"
|
||||
},
|
||||
"Benny3DFront": {
|
||||
"Name": "Front Texture",
|
||||
"Hint": "Front texture of the 3D Benny"
|
||||
},
|
||||
"Benny3DBack": {
|
||||
"Name": "Back Texture",
|
||||
"Hint": "Back texture of the 3D Benny"
|
||||
},
|
||||
"Benny3DBackBump": {
|
||||
"Name": "Back Bump Map",
|
||||
"Hint": "Back bump map of the benny"
|
||||
},
|
||||
"Benny3DFrontBump": {
|
||||
"Hint": "Front bump map of the benny",
|
||||
"Name": "Front Bump Map"
|
||||
},
|
||||
"CoreSkillsList": {
|
||||
"Name": "List of Core Skills",
|
||||
"Hint": "Enter a comma-separated list of skill names to be defined as core skills for all actors"
|
||||
},
|
||||
"CoreSkillsPack": {
|
||||
"Name": "Core Skills Compendium",
|
||||
"Hint": "Choose from which Compendium the core skills will be drawn"
|
||||
},
|
||||
"WealthType": {
|
||||
"Name": "Wealth Type",
|
||||
"Hint": "Choose how to track character wealth and currency"
|
||||
},
|
||||
"CurrencyName": {
|
||||
"Name": "Currency Name",
|
||||
"Hint": "Name your currency"
|
||||
},
|
||||
"JokersWild": {
|
||||
"Name": "Joker's Wild",
|
||||
"Hint": "Gives a Benny to every player character when one of them is dealt a Joker"
|
||||
},
|
||||
"AmmoManagement": {
|
||||
"Name": "Ammunition Management",
|
||||
"Hint": "When turned on, and applicable, actions subtract ammunition from the magazine of the weapon. Item cards also gain a new reload button and you will not be able to roll an attack if not enough ammunition is available"
|
||||
},
|
||||
"PCAmmoFromInventory": {
|
||||
"Name": "PCs use Ammunition from Inventory",
|
||||
"Hint": "When activated the option to set a `Gear` Item can be set to be used as ammunition and will be used when reloading the weapon"
|
||||
},
|
||||
"NPCAmmoFromInventory": {
|
||||
"Name": "NPCs use ammunition from their inventory"
|
||||
},
|
||||
"VehicleAmmoFromInventory": {
|
||||
"Name": "Vehicles use ammunition from their inventory"
|
||||
},
|
||||
"EnableWoundPace": {
|
||||
"Name": "Adjust Pace with Wounds",
|
||||
"Hint": "When enabled, Pace is automatically affected by Wounds"
|
||||
},
|
||||
"EnableConv": {
|
||||
"Name": "Conviction",
|
||||
"Hint": "Enable the Conviction setting rule"
|
||||
},
|
||||
"NoPowerPoints": {
|
||||
"Name": "No Power Points",
|
||||
"Hint": "When enabled this option hides all Power Point interactions"
|
||||
},
|
||||
"ApplyEncumbrance": {
|
||||
"Name": "Apply Encumbrance Penalties",
|
||||
"Hint": "Apply encumbrance penalties. Please keep in mind that this currently does NOT SUPPORT {vigor} tests to resist {fatigue}!"
|
||||
},
|
||||
"GmBennies": {
|
||||
"Name": "Gamemaster Bennies",
|
||||
"Hint": "Sets how many Bennies the GM receives on a refresh"
|
||||
},
|
||||
"VehicleMods": {
|
||||
"Name": "Vehicles Mods",
|
||||
"Hint": "Vehicles use Mod-Slots"
|
||||
},
|
||||
"VehicleEdges": {
|
||||
"Name": "Vehicle Edges",
|
||||
"Hint": "Vehicles use Edges and Hindrances"
|
||||
},
|
||||
"BennyImageSheet": {
|
||||
"Name": "Benny",
|
||||
"Hint": "Select which image to use for Bennies on the Character Sheet"
|
||||
},
|
||||
"HardChoices": {
|
||||
"Name": "Hard Choices",
|
||||
"Hint": "Checking this will enable the Hard Choices rule"
|
||||
},
|
||||
"CardSound": {
|
||||
"Name": "Card Sound",
|
||||
"Hint": "Play a short card sound when dealing Initiative"
|
||||
}
|
||||
},
|
||||
"EffectCallbacks": {
|
||||
"Shaken": {
|
||||
"RollSpirit": "Roll Spirit",
|
||||
"Title": "Remove Shaken from {name}",
|
||||
"Flavor": "Spirit Test to remove Shaken",
|
||||
"UnshakeModifier": "Unshake Modifier",
|
||||
"Question": "What do you want to do?",
|
||||
"Success": "You are no longer Shaken"
|
||||
},
|
||||
"Stunned": {
|
||||
"Title": "Vigor Test to remove Stunned",
|
||||
"Fail": "You remain Stunned",
|
||||
"Success": "You are no longer Stunned but remain Vulnerable",
|
||||
"Raise": "You are no longer Stunned nor Vulnerable"
|
||||
},
|
||||
"BleedingOut": {
|
||||
"Title": "Vigor Test to resist Bleeding Out",
|
||||
"Fail": "You perish",
|
||||
"Success": "You hang on, for now.",
|
||||
"Raise": "You successfully stabilize"
|
||||
}
|
||||
},
|
||||
"BenniesSpendGM": "Spend a GM Benny",
|
||||
"DocumentLink": "Link",
|
||||
"DocumentTweaks": "Tweaks",
|
||||
"TargetVulnerable": "Target is Vulnerable",
|
||||
"WildAttack": "Wild Attack",
|
||||
"ItemType": "Item Type",
|
||||
"Source": "Source",
|
||||
"Properties": "Properties",
|
||||
"AddAction": "Add Action",
|
||||
"DeleteEmbeddedActionPrompt": "Are you sure you want to delete the action {action}",
|
||||
"DeleteEmbeddedPowerPrompt": "Are you sure you want to delete the power {power}?",
|
||||
"TrademarkWeapon": {
|
||||
"Label": "Trademark Weapon",
|
||||
"Regular": "Regular",
|
||||
"Improved": "Improved"
|
||||
},
|
||||
"OffHandPenalty": "Off-Hand Penalty",
|
||||
"ItemEquipStatus": {
|
||||
"Label": "Location",
|
||||
"Stored": "Stored",
|
||||
"Carried": "Carried",
|
||||
"Equipped": "Equipped",
|
||||
"OffHand": "Off-Hand",
|
||||
"MainHand": "Main Hand",
|
||||
"TwoHands": "Two Hands"
|
||||
},
|
||||
"DestroyOnEmpty": "Destroy when Empty",
|
||||
"Charges": "Charges",
|
||||
"Consumable": {
|
||||
"Use": "Use",
|
||||
"Consumables": "Consumables"
|
||||
},
|
||||
"EmptyRacialAbilitiesHint": "This {race} or {archetype} doesn't have any abilities yet. Drag&Drop something onto the sheet to get started."
|
||||
},
|
||||
"ACTOR": {
|
||||
"TypeCharacter": "Player Character",
|
||||
"TypeNpc": "NPC",
|
||||
"TypeVehicle": "Vehicle"
|
||||
},
|
||||
"ITEM": {
|
||||
"TypeWeapon": "Weapon",
|
||||
"TypeArmor": "Armor",
|
||||
"TypeShield": "Shield",
|
||||
"TypeGear": "Gear",
|
||||
"TypeEdge": "Edge",
|
||||
"TypeHindrance": "Hindrance",
|
||||
"TypeSkill": "Skill",
|
||||
"TypePower": "Power",
|
||||
"TypeAbility": "Ability",
|
||||
"TypeConsumable": "Consumable"
|
||||
},
|
||||
"CARD": {
|
||||
"TypeAdventure": "Adventure Card",
|
||||
"TypePoker": "Poker Card"
|
||||
}
|
||||
}
|
625
tools/lpeg/lpcap.lua
Normal file
625
tools/lpeg/lpcap.lua
Normal file
@ -0,0 +1,625 @@
|
||||
--[[
|
||||
LPEGLJ
|
||||
lpcap.lua
|
||||
Capture functions
|
||||
Copyright (C) 2014 Rostislav Sacek.
|
||||
based on LPeg v1.0 - PEG pattern matching for Lua
|
||||
Lua.org & PUC-Rio written by Roberto Ierusalimschy
|
||||
http://www.inf.puc-rio.br/~roberto/lpeg/
|
||||
|
||||
** Permission is hereby granted, free of charge, to any person obtaining
|
||||
** a copy of this software and associated documentation files (the
|
||||
** "Software"), to deal in the Software without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Software, and to
|
||||
** permit persons to whom the Software is furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be
|
||||
** included in all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
**
|
||||
** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
|
||||
--]]
|
||||
local ffi = require "ffi"
|
||||
|
||||
local Cclose = 0
|
||||
local Cposition = 1
|
||||
local Cconst = 2
|
||||
local Cbackref = 3
|
||||
local Carg = 4
|
||||
local Csimple = 5
|
||||
local Ctable = 6
|
||||
local Cfunction = 7
|
||||
local Cquery = 8
|
||||
local Cstring = 9
|
||||
local Cnum = 10
|
||||
local Csubst = 11
|
||||
local Cfold = 12
|
||||
local Cruntime = 13
|
||||
local Cgroup = 14
|
||||
|
||||
local MAXSTRCAPS = 10
|
||||
|
||||
local pushcapture
|
||||
local addonestring
|
||||
|
||||
|
||||
-- Goes back in a list of captures looking for an open capture
|
||||
-- corresponding to a close
|
||||
|
||||
local function findopen(cs, index)
|
||||
local n = 0; -- number of closes waiting an open
|
||||
while true do
|
||||
index = index - 1
|
||||
if cs.ocap[index].kind == Cclose then
|
||||
n = n + 1 -- one more open to skip
|
||||
elseif cs.ocap[index].siz == 0 then
|
||||
if n == 0 then
|
||||
return index
|
||||
end
|
||||
n = n - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function checknextcap(cs, captop)
|
||||
local cap = cs.cap;
|
||||
-- not a single capture? ((cap)->siz != 0)
|
||||
if cs.ocap[cap].siz == 0 then
|
||||
local n = 0; -- number of opens waiting a close
|
||||
-- look for corresponding close
|
||||
while true do
|
||||
cap = cap + 1
|
||||
if cap > captop then return end
|
||||
if cs.ocap[cap].kind == Cclose then
|
||||
n = n - 1
|
||||
if n + 1 == 0 then
|
||||
break;
|
||||
end
|
||||
elseif cs.ocap[cap].siz == 0 then
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
cap = cap + 1; -- + 1 to skip last close (or entire single capture)
|
||||
if cap > captop then return end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
-- Go to the next capture
|
||||
|
||||
local function nextcap(cs)
|
||||
local cap = cs.cap;
|
||||
-- not a single capture? ((cap)->siz != 0)
|
||||
if cs.ocap[cap].siz == 0 then
|
||||
local n = 0; -- number of opens waiting a close
|
||||
-- look for corresponding close
|
||||
while true do
|
||||
cap = cap + 1
|
||||
if cs.ocap[cap].kind == Cclose then
|
||||
n = n - 1
|
||||
if n + 1 == 0 then
|
||||
break;
|
||||
end
|
||||
elseif cs.ocap[cap].siz == 0 then
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
cs.cap = cap + 1; -- + 1 to skip last close (or entire single capture)
|
||||
end
|
||||
|
||||
|
||||
-- Push on the Lua stack all values generated by nested captures inside
|
||||
-- the current capture. Returns number of values pushed. 'addextra'
|
||||
-- makes it push the entire match after all captured values. The
|
||||
-- entire match is pushed also if there are no other nested values,
|
||||
-- so the function never returns zero.
|
||||
|
||||
local function pushnestedvalues(cs, addextra, out, valuetable)
|
||||
local co = cs.cap
|
||||
cs.cap = cs.cap + 1
|
||||
-- no nested captures?
|
||||
if cs.ocap[cs.cap - 1].siz ~= 0 then
|
||||
local st = cs.ocap[co].s
|
||||
local l = cs.ocap[co].siz - 1
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = cs.s and cs.s:sub(st, st + l - 1) or cs.stream(st, st + l - 1)
|
||||
return 1; -- that is it
|
||||
else
|
||||
local n = 0;
|
||||
while cs.ocap[cs.cap].kind ~= Cclose do -- repeat for all nested patterns
|
||||
n = n + pushcapture(cs, out, valuetable);
|
||||
end
|
||||
-- need extra?
|
||||
if addextra or n == 0 then
|
||||
local st = cs.ocap[co].s
|
||||
local l = cs.ocap[cs.cap].s - cs.ocap[co].s
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = cs.s and cs.s:sub(st, st + l - 1) or cs.stream(st, st + l - 1)
|
||||
n = n + 1
|
||||
end
|
||||
cs.cap = cs.cap + 1 -- skip close entry
|
||||
return n;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Push only the first value generated by nested captures
|
||||
|
||||
local function pushonenestedvalue(cs, out, valuetable)
|
||||
local n = pushnestedvalues(cs, false, out, valuetable)
|
||||
for i = n, 2, -1 do
|
||||
out.out[out.outindex] = nil
|
||||
out.outindex = out.outindex - 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Try to find a named group capture with the name given at the top of
|
||||
-- the stack; goes backward from 'cap'.
|
||||
|
||||
local function findback(cs, cap, name, valuetable)
|
||||
-- repeat until end of list
|
||||
while cap > 0 do
|
||||
cap = cap - 1
|
||||
local continue
|
||||
if cs.ocap[cap].kind == Cclose then
|
||||
cap = findopen(cs, cap); -- skip nested captures
|
||||
elseif cs.ocap[cap].siz == 0 then
|
||||
continue = true -- opening an enclosing capture: skip and get previous
|
||||
end
|
||||
if not continue and cs.ocap[cap].kind == Cgroup and cs.ocap[cap].idx ~= 0 then
|
||||
local gname = valuetable[cs.ocap[cap].idx] -- get group name
|
||||
-- right group?
|
||||
if name == gname then
|
||||
return cap;
|
||||
end
|
||||
end
|
||||
end
|
||||
error(("back reference '%s' not found"):format(name), 0)
|
||||
end
|
||||
|
||||
|
||||
-- Back-reference capture. Return number of values pushed.
|
||||
|
||||
local function backrefcap(cs, out, valuetable)
|
||||
local curr = cs.cap;
|
||||
local name = valuetable[cs.ocap[cs.cap].idx] -- reference name
|
||||
cs.cap = findback(cs, curr, name, valuetable) -- find corresponding group
|
||||
local n = pushnestedvalues(cs, false, out, valuetable); -- push group's values
|
||||
cs.cap = curr + 1;
|
||||
return n;
|
||||
end
|
||||
|
||||
|
||||
-- Table capture: creates a new table and populates it with nested
|
||||
-- captures.
|
||||
|
||||
local function tablecap(cs, out, valuetable)
|
||||
local n = 0;
|
||||
local t = {}
|
||||
cs.cap = cs.cap + 1
|
||||
-- table is empty
|
||||
if cs.ocap[cs.cap - 1].siz == 0 then
|
||||
while cs.ocap[cs.cap].kind ~= Cclose do
|
||||
local subout = { outindex = 0, out = {} }
|
||||
-- named group?
|
||||
if cs.ocap[cs.cap].kind == Cgroup and cs.ocap[cs.cap].idx ~= 0 then
|
||||
local groupname = valuetable[cs.ocap[cs.cap].idx] -- push group name
|
||||
pushonenestedvalue(cs, subout, valuetable)
|
||||
t[groupname] = subout.out[1]
|
||||
else
|
||||
-- not a named group
|
||||
local k = pushcapture(cs, subout, valuetable)
|
||||
-- store all values into table
|
||||
for i = 1, subout.outindex do
|
||||
t[i + n] = subout.out[i]
|
||||
end
|
||||
n = n + k;
|
||||
end
|
||||
end
|
||||
cs.cap = cs.cap + 1 -- skip close entry
|
||||
end
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = t
|
||||
return 1; -- number of values pushed (only the table)
|
||||
end
|
||||
|
||||
|
||||
-- Table-query capture
|
||||
|
||||
local function querycap(cs, out, valuetable)
|
||||
local table = valuetable[cs.ocap[cs.cap].idx]
|
||||
local subout = { outindex = 0, out = {} }
|
||||
pushonenestedvalue(cs, subout, valuetable) -- get nested capture
|
||||
-- query cap. value at table
|
||||
if table[subout.out[1]] ~= nil then
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = table[subout.out[1]]
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
-- Fold capture
|
||||
|
||||
local function foldcap(cs, out, valuetable)
|
||||
local fce = valuetable[cs.ocap[cs.cap].idx]
|
||||
cs.cap = cs.cap + 1
|
||||
-- no nested captures?
|
||||
-- or no nested captures (large subject)?
|
||||
if cs.ocap[cs.cap - 1].siz ~= 0 or
|
||||
cs.ocap[cs.cap].kind == Cclose then
|
||||
error("no initial value for fold capture", 0);
|
||||
end
|
||||
local subout = { outindex = 0; out = {} }
|
||||
local n = pushcapture(cs, subout, valuetable) -- nested captures with no values?
|
||||
if n == 0 then
|
||||
error("no initial value for fold capture", 0);
|
||||
end
|
||||
local acumulator = subout.out[1] -- leave only one result for accumulator
|
||||
while cs.ocap[cs.cap].kind ~= Cclose do
|
||||
local subout = { outindex = 0; out = {} }
|
||||
n = pushcapture(cs, subout, valuetable); -- get next capture's values
|
||||
acumulator = fce(acumulator, unpack(subout.out, 1, subout.outindex)) -- call folding function
|
||||
end
|
||||
cs.cap = cs.cap + 1; -- skip close entry
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = acumulator
|
||||
return 1; -- only accumulator left on the stack
|
||||
end
|
||||
|
||||
|
||||
local function retcount(...)
|
||||
return select('#', ...), { ... }
|
||||
end
|
||||
|
||||
|
||||
-- Function capture
|
||||
|
||||
local function functioncap(cs, out, valuetable)
|
||||
local fce = valuetable[cs.ocap[cs.cap].idx] -- push function
|
||||
local subout = { outindex = 0, out = {} }
|
||||
local n = pushnestedvalues(cs, false, subout, valuetable); -- push nested captures
|
||||
local count, ret = retcount(fce(unpack(subout.out, 1, n))) -- call function
|
||||
for i = 1, count do
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = ret[i]
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
|
||||
-- Select capture
|
||||
|
||||
local function numcap(cs, out, valuetable)
|
||||
local idx = valuetable[cs.ocap[cs.cap].idx] -- value to select
|
||||
-- no values?
|
||||
if idx == 0 then
|
||||
nextcap(cs); -- skip entire capture
|
||||
return 0; -- no value produced
|
||||
else
|
||||
local subout = { outindex = 0, out = {} }
|
||||
local n = pushnestedvalues(cs, false, subout, valuetable)
|
||||
-- invalid index?
|
||||
if n < idx then
|
||||
error(("no capture '%d'"):format(idx), 0)
|
||||
else
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = subout.out[idx] -- get selected capture
|
||||
return 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Calls a runtime capture. Returns number of captures removed by
|
||||
-- the call, including the initial Cgroup. (Captures to be added are
|
||||
-- on the Lua stack.)
|
||||
|
||||
local function runtimecap(cs, close, s, out, valuetable)
|
||||
local open = findopen(cs, close)
|
||||
assert(cs.ocap[open].kind == Cgroup)
|
||||
cs.ocap[close].kind = Cclose; -- closes the group
|
||||
cs.ocap[close].s = s;
|
||||
cs.cap = open;
|
||||
local fce = valuetable[cs.ocap[cs.cap].idx] -- push function to be called
|
||||
local subout = { outindex = 0, out = {} }
|
||||
local n = pushnestedvalues(cs, false, subout, valuetable); -- push nested captures
|
||||
local count, ret = retcount(fce(cs.s or cs.stream, s, unpack(subout.out, 1, n))) -- call dynamic function
|
||||
for i = 1, count do
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = ret[i]
|
||||
end
|
||||
return close - open -- number of captures of all kinds removed
|
||||
end
|
||||
|
||||
-- Collect values from current capture into array 'cps'. Current
|
||||
-- capture must be Cstring (first call) or Csimple (recursive calls).
|
||||
-- (In first call, fills %0 with whole match for Cstring.)
|
||||
-- Returns number of elements in the array that were filled.
|
||||
|
||||
local function getstrcaps(cs, cps, n)
|
||||
local k = n
|
||||
n = n + 1
|
||||
cps[k + 1].isstring = true; -- get string value
|
||||
cps[k + 1].startstr = cs.ocap[cs.cap].s; -- starts here
|
||||
cs.cap = cs.cap + 1
|
||||
-- nested captures?
|
||||
if cs.ocap[cs.cap - 1].siz == 0 then
|
||||
-- traverse them
|
||||
while cs.ocap[cs.cap].kind ~= Cclose do
|
||||
-- too many captures?
|
||||
if n >= MAXSTRCAPS then
|
||||
nextcap(cs); -- skip extra captures (will not need them)
|
||||
elseif cs.ocap[cs.cap].kind == Csimple then
|
||||
-- string?
|
||||
n = getstrcaps(cs, cps, n); -- put info. into array
|
||||
else
|
||||
cps[n + 1].isstring = false; -- not a string
|
||||
cps[n + 1].origcap = cs.cap; -- keep original capture
|
||||
nextcap(cs);
|
||||
n = n + 1;
|
||||
end
|
||||
end
|
||||
cs.cap = cs.cap + 1 -- skip close
|
||||
end
|
||||
cps[k + 1].endstr = cs.ocap[cs.cap - 1].s + cs.ocap[cs.cap - 1].siz - 1 -- ends here
|
||||
return n;
|
||||
end
|
||||
|
||||
|
||||
-- add next capture value (which should be a string) to buffer 'b'
|
||||
|
||||
-- String capture: add result to buffer 'b' (instead of pushing
|
||||
-- it into the stack)
|
||||
|
||||
local function stringcap(cs, b, valuetable)
|
||||
local cps = {}
|
||||
for i = 1, MAXSTRCAPS do
|
||||
cps[#cps + 1] = {}
|
||||
end
|
||||
local fmt = valuetable[cs.ocap[cs.cap].idx]
|
||||
local n = getstrcaps(cs, cps, 0) - 1; -- collect nested captures
|
||||
local i = 1
|
||||
-- traverse them
|
||||
while i <= #fmt do
|
||||
local c = fmt:sub(i, i)
|
||||
-- not an escape?
|
||||
if c ~= '%' then
|
||||
b[#b + 1] = c -- add it to buffer
|
||||
elseif fmt:sub(i + 1, i + 1) < '0' or fmt:sub(i + 1, i + 1) > '9' then
|
||||
-- not followed by a digit?
|
||||
i = i + 1
|
||||
b[#b + 1] = fmt:sub(i, i)
|
||||
else
|
||||
i = i + 1
|
||||
local l = fmt:sub(i, i) - '0'; -- capture index
|
||||
if l > n then
|
||||
error(("invalid capture index (%d)"):format(l), 0)
|
||||
elseif cps[l + 1].isstring then
|
||||
b[#b + 1] = cs.s and cs.s:sub(cps[l + 1].startstr, cps[l + 1].endstr - cps[l + 1].startstr + cps[l + 1].startstr - 1) or
|
||||
cs.stream(cps[l + 1].startstr, cps[l + 1].endstr - cps[l + 1].startstr + cps[l + 1].startstr - 1)
|
||||
else
|
||||
local curr = cs.cap;
|
||||
cs.cap = cps[l + 1].origcap; -- go back to evaluate that nested capture
|
||||
if not addonestring(cs, b, "capture", valuetable) then
|
||||
error(("no values in capture index %d"):format(l), 0)
|
||||
end
|
||||
cs.cap = curr; -- continue from where it stopped
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Substitution capture: add result to buffer 'b'
|
||||
|
||||
local function substcap(cs, b, valuetable)
|
||||
local curr = cs.ocap[cs.cap].s;
|
||||
-- no nested captures?
|
||||
if cs.ocap[cs.cap].siz ~= 0 then
|
||||
-- keep original text
|
||||
b[#b + 1] = cs.s and cs.s:sub(curr, cs.ocap[cs.cap].siz - 1 + curr - 1) or
|
||||
cs.stream(curr, cs.ocap[cs.cap].siz - 1 + curr - 1)
|
||||
else
|
||||
cs.cap = cs.cap + 1 -- skip open entry
|
||||
-- traverse nested captures
|
||||
while cs.ocap[cs.cap].kind ~= Cclose do
|
||||
local next = cs.ocap[cs.cap].s;
|
||||
b[#b + 1] = cs.s and cs.s:sub(curr, next - curr + curr - 1) or
|
||||
cs.stream(curr, next - curr + curr - 1) -- add text up to capture
|
||||
if addonestring(cs, b, "replacement", valuetable) then
|
||||
curr = cs.ocap[cs.cap - 1].s + cs.ocap[cs.cap - 1].siz - 1; -- continue after match
|
||||
else
|
||||
-- no capture value
|
||||
curr = next; -- keep original text in final result
|
||||
end
|
||||
end
|
||||
b[#b + 1] = cs.s and cs.s:sub(curr, curr + cs.ocap[cs.cap].s - curr - 1) or
|
||||
cs.stream(curr, curr + cs.ocap[cs.cap].s - curr - 1) -- add last piece of text
|
||||
end
|
||||
cs.cap = cs.cap + 1 -- go to next capture
|
||||
end
|
||||
|
||||
|
||||
-- Evaluates a capture and adds its first value to buffer 'b'; returns
|
||||
-- whether there was a value
|
||||
|
||||
function addonestring(cs, b, what, valuetable)
|
||||
local tag = cs.ocap[cs.cap].kind
|
||||
if tag == Cstring then
|
||||
stringcap(cs, b, valuetable); -- add capture directly to buffer
|
||||
return 1
|
||||
elseif tag == Csubst then
|
||||
substcap(cs, b, valuetable); -- add capture directly to buffer
|
||||
return 1
|
||||
else
|
||||
local subout = { outindex = 0, out = {} }
|
||||
local n = pushcapture(cs, subout, valuetable);
|
||||
if n > 0 then
|
||||
if type(subout.out[1]) ~= 'string' and type(subout.out[1]) ~= 'number' then
|
||||
error(("invalid %s value (a %s)"):format(what, type(subout.out[1])), 0)
|
||||
end
|
||||
b[#b + 1] = subout.out[1]
|
||||
return n
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Push all values of the current capture into the stack; returns
|
||||
-- number of values pushed
|
||||
|
||||
function pushcapture(cs, out, valuetable)
|
||||
local type = cs.ocap[cs.cap].kind
|
||||
if type == Cposition then
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = cs.ocap[cs.cap].s
|
||||
cs.cap = cs.cap + 1;
|
||||
return 1;
|
||||
elseif type == Cconst then
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = valuetable[cs.ocap[cs.cap].idx]
|
||||
cs.cap = cs.cap + 1
|
||||
return 1;
|
||||
elseif type == Carg then
|
||||
local arg = valuetable[cs.ocap[cs.cap].idx]
|
||||
cs.cap = cs.cap + 1
|
||||
if arg > cs.ptopcount then
|
||||
error(("reference to absent extra argument #%d"):format(arg), 0)
|
||||
end
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = cs.ptop[arg]
|
||||
return 1;
|
||||
elseif type == Csimple then
|
||||
local k = pushnestedvalues(cs, true, out, valuetable)
|
||||
local index = out.outindex
|
||||
table.insert(out.out, index - k + 1, out.out[index])
|
||||
out[index + 1] = nil
|
||||
return k;
|
||||
elseif type == Cruntime then
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = valuetable[cs.ocap[cs.cap].idx]
|
||||
cs.cap = cs.cap + 1;
|
||||
return 1;
|
||||
elseif type == Cstring then
|
||||
local b = {}
|
||||
stringcap(cs, b, valuetable)
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = table.concat(b)
|
||||
return 1;
|
||||
elseif type == Csubst then
|
||||
local b = {}
|
||||
substcap(cs, b, valuetable);
|
||||
out.outindex = out.outindex + 1
|
||||
out.out[out.outindex] = table.concat(b)
|
||||
return 1;
|
||||
elseif type == Cgroup then
|
||||
-- anonymous group?
|
||||
if cs.ocap[cs.cap].idx == 0 then
|
||||
return pushnestedvalues(cs, false, out, valuetable); -- add all nested values
|
||||
else
|
||||
-- named group: add no values
|
||||
nextcap(cs); -- skip capture
|
||||
return 0
|
||||
end
|
||||
elseif type == Cbackref then
|
||||
return backrefcap(cs, out, valuetable)
|
||||
elseif type == Ctable then
|
||||
return tablecap(cs, out, valuetable)
|
||||
elseif type == Cfunction then
|
||||
return functioncap(cs, out, valuetable)
|
||||
elseif type == Cnum then
|
||||
return numcap(cs, out, valuetable)
|
||||
elseif type == Cquery then
|
||||
return querycap(cs, out, valuetable)
|
||||
elseif type == Cfold then
|
||||
return foldcap(cs, out, valuetable)
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Prepare a CapState structure and traverse the entire list of
|
||||
-- captures in the stack pushing its results. 's' is the subject
|
||||
-- string, 'r' is the final position of the match, and 'ptop'
|
||||
-- the index in the stack where some useful values were pushed.
|
||||
-- Returns the number of results pushed. (If the list produces no
|
||||
-- results, push the final position of the match.)
|
||||
|
||||
local function getcaptures(capture, s, stream, r, valuetable, ...)
|
||||
local n = 0;
|
||||
local cs = { cap = 0 }
|
||||
local out = { outindex = 0; out = {} }
|
||||
-- is there any capture?
|
||||
if capture[cs.cap].kind ~= Cclose then
|
||||
cs.ocap = capture
|
||||
cs.s = s;
|
||||
cs.stream = stream
|
||||
cs.ptopcount, cs.ptop = retcount(...)
|
||||
repeat -- collect their values
|
||||
n = n + pushcapture(cs, out, valuetable)
|
||||
until cs.ocap[cs.cap].kind == Cclose
|
||||
end
|
||||
-- no capture values?
|
||||
if n == 0 then
|
||||
if not r then
|
||||
return
|
||||
else
|
||||
return r
|
||||
end
|
||||
end
|
||||
assert(out.outindex < 7998, "(too many captures)")
|
||||
return unpack(out.out, 1, out.outindex)
|
||||
end
|
||||
|
||||
local function getcapturesruntime(capture, s, stream, notdelete, min, max, captop, valuetable, ...)
|
||||
local n = 0;
|
||||
local cs = { cap = min }
|
||||
local out = { outindex = 0; out = {} }
|
||||
cs.ocap = capture
|
||||
cs.s = s
|
||||
cs.stream = stream
|
||||
cs.ptopcount, cs.ptop = retcount(...)
|
||||
local start = 0
|
||||
repeat -- collect their values
|
||||
if not checknextcap(cs, max) then break end
|
||||
local notdelete = notdelete or capture[cs.cap].kind == Cgroup and capture[cs.cap].idx ~= 0 and capture[cs.cap].candelete == 0
|
||||
pushcapture(cs, out, valuetable)
|
||||
if notdelete then
|
||||
start = cs.cap
|
||||
else
|
||||
n = n + cs.cap - start
|
||||
for i = 0, captop - cs.cap - 1 do
|
||||
ffi.copy(capture + start + i, capture + cs.cap + i, ffi.sizeof('CAPTURE'))
|
||||
end
|
||||
max = max - (cs.cap - start)
|
||||
captop = captop - (cs.cap - start)
|
||||
cs.cap = start
|
||||
end
|
||||
until cs.cap == max
|
||||
assert(out.outindex < 7998, "(too many captures)")
|
||||
return n, out.out, out.outindex
|
||||
end
|
||||
|
||||
return {
|
||||
getcaptures = getcaptures,
|
||||
runtimecap = runtimecap,
|
||||
getcapturesruntime = getcapturesruntime,
|
||||
}
|
||||
|
1057
tools/lpeg/lpcode.lua
Normal file
1057
tools/lpeg/lpcode.lua
Normal file
File diff suppressed because it is too large
Load Diff
1373
tools/lpeg/lpeg.lua
Normal file
1373
tools/lpeg/lpeg.lua
Normal file
File diff suppressed because it is too large
Load Diff
356
tools/lpeg/lpprint.lua
Normal file
356
tools/lpeg/lpprint.lua
Normal file
@ -0,0 +1,356 @@
|
||||
--[[
|
||||
LPEGLJ
|
||||
lpprint.lua
|
||||
Tree, code and debug print function (only for debuging)
|
||||
Copyright (C) 2014 Rostislav Sacek.
|
||||
based on LPeg v1.0 - PEG pattern matching for Lua
|
||||
Lua.org & PUC-Rio written by Roberto Ierusalimschy
|
||||
http://www.inf.puc-rio.br/~roberto/lpeg/
|
||||
|
||||
** Permission is hereby granted, free of charge, to any person obtaining
|
||||
** a copy of this software and associated documentation files (the
|
||||
** "Software"), to deal in the Software without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Software, and to
|
||||
** permit persons to whom the Software is furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be
|
||||
** included in all copies or substantial portions of the Software.
|
||||
**
|
||||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
**
|
||||
** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
|
||||
--]]
|
||||
|
||||
local ffi = require"ffi"
|
||||
local band, rshift, lshift = bit.band, bit.rshift, bit.lshift
|
||||
|
||||
ffi.cdef[[
|
||||
int isprint ( int c );
|
||||
]]
|
||||
|
||||
local RuleLR = 0x10000
|
||||
local Ruleused = 0x20000
|
||||
|
||||
-- {======================================================
|
||||
-- Printing patterns (for debugging)
|
||||
-- =======================================================
|
||||
|
||||
local TChar = 0
|
||||
local TSet = 1
|
||||
local TAny = 2 -- standard PEG elements
|
||||
local TTrue = 3
|
||||
local TFalse = 4
|
||||
local TRep = 5
|
||||
local TSeq = 6
|
||||
local TChoice = 7
|
||||
local TNot = 8
|
||||
local TAnd = 9
|
||||
local TCall = 10
|
||||
local TOpenCall = 11
|
||||
local TRule = 12 -- sib1 is rule's pattern, sib2 is 'next' rule
|
||||
local TGrammar = 13 -- sib1 is initial (and first) rule
|
||||
local TBehind = 14 -- match behind
|
||||
local TCapture = 15 -- regular capture
|
||||
local TRunTime = 16 -- run-time capture
|
||||
|
||||
local IAny = 0 -- if no char, fail
|
||||
local IChar = 1 -- if char != aux, fail
|
||||
local ISet = 2 -- if char not in val, fail
|
||||
local ITestAny = 3 -- in no char, jump to 'offset'
|
||||
local ITestChar = 4 -- if char != aux, jump to 'offset'
|
||||
local ITestSet = 5 -- if char not in val, jump to 'offset'
|
||||
local ISpan = 6 -- read a span of chars in val
|
||||
local IBehind = 7 -- walk back 'aux' characters (fail if not possible)
|
||||
local IRet = 8 -- return from a rule
|
||||
local IEnd = 9 -- end of pattern
|
||||
local IChoice = 10 -- stack a choice; next fail will jump to 'offset'
|
||||
local IJmp = 11 -- jump to 'offset'
|
||||
local ICall = 12 -- call rule at 'offset'
|
||||
local IOpenCall = 13 -- call rule number 'offset' (must be closed to a ICall)
|
||||
local ICommit = 14 -- pop choice and jump to 'offset'
|
||||
local IPartialCommit = 15 -- update top choice to current position and jump
|
||||
local IBackCommit = 16 -- "fails" but jump to its own 'offset'
|
||||
local IFailTwice = 17 -- pop one choice and then fail
|
||||
local IFail = 18 -- go back to saved state on choice and jump to saved offset
|
||||
local IGiveup = 19 -- internal use
|
||||
local IFullCapture = 20 -- complete capture of last 'off' chars
|
||||
local IOpenCapture = 21 -- start a capture
|
||||
local ICloseCapture = 22
|
||||
local ICloseRunTime = 23
|
||||
|
||||
local Cclose = 0
|
||||
local Cposition = 1
|
||||
local Cconst = 2
|
||||
local Cbackref = 3
|
||||
local Carg = 4
|
||||
local Csimple = 5
|
||||
local Ctable = 6
|
||||
local Cfunction = 7
|
||||
local Cquery = 8
|
||||
local Cstring = 9
|
||||
local Cnum = 10
|
||||
local Csubst = 11
|
||||
local Cfold = 12
|
||||
local Cruntime = 13
|
||||
local Cgroup = 14
|
||||
|
||||
|
||||
-- number of siblings for each tree
|
||||
local numsiblings = {
|
||||
[TRep] = 1,
|
||||
[TSeq] = 2,
|
||||
[TChoice] = 2,
|
||||
[TNot] = 1,
|
||||
[TAnd] = 1,
|
||||
[TRule] = 2,
|
||||
[TGrammar] = 1,
|
||||
[TBehind] = 1,
|
||||
[TCapture] = 1,
|
||||
[TRunTime] = 1,
|
||||
}
|
||||
local names = {
|
||||
[IAny] = "any",
|
||||
[IChar] = "char",
|
||||
[ISet] = "set",
|
||||
[ITestAny] = "testany",
|
||||
[ITestChar] = "testchar",
|
||||
[ITestSet] = "testset",
|
||||
[ISpan] = "span",
|
||||
[IBehind] = "behind",
|
||||
[IRet] = "ret",
|
||||
[IEnd] = "end",
|
||||
[IChoice] = "choice",
|
||||
[IJmp] = "jmp",
|
||||
[ICall] = "call",
|
||||
[IOpenCall] = "open_call",
|
||||
[ICommit] = "commit",
|
||||
[IPartialCommit] = "partial_commit",
|
||||
[IBackCommit] = "back_commit",
|
||||
[IFailTwice] = "failtwice",
|
||||
[IFail] = "fail",
|
||||
[IGiveup] = "giveup",
|
||||
[IFullCapture] = "fullcapture",
|
||||
[IOpenCapture] = "opencapture",
|
||||
[ICloseCapture] = "closecapture",
|
||||
[ICloseRunTime] = "closeruntime"
|
||||
}
|
||||
|
||||
local function printcharset(st)
|
||||
io.write("[");
|
||||
local i = 0
|
||||
while i <= 255 do
|
||||
local first = i;
|
||||
while band(st[rshift(i, 5)], lshift(1, band(i, 31))) ~= 0 and i <= 255 do
|
||||
i = i + 1
|
||||
end
|
||||
if i - 1 == first then -- unary range?
|
||||
io.write(("(%02x)"):format(first))
|
||||
elseif i - 1 > first then -- non-empty range?
|
||||
io.write(("(%02x-%02x)"):format(first, i - 1))
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
io.write("]")
|
||||
end
|
||||
|
||||
local modes = {
|
||||
[Cclose] = "close",
|
||||
[Cposition] = "position",
|
||||
[Cconst] = "constant",
|
||||
[Cbackref] = "backref",
|
||||
[Carg] = "argument",
|
||||
[Csimple] = "simple",
|
||||
[Ctable] = "table",
|
||||
[Cfunction] = "function",
|
||||
[Cquery] = "query",
|
||||
[Cstring] = "string",
|
||||
[Cnum] = "num",
|
||||
[Csubst] = "substitution",
|
||||
[Cfold] = "fold",
|
||||
[Cruntime] = "runtime",
|
||||
[Cgroup] = "group"
|
||||
}
|
||||
|
||||
local function printcapkind(kind)
|
||||
io.write(("%s"):format(modes[kind]))
|
||||
end
|
||||
|
||||
local function printjmp(p, index)
|
||||
io.write(("-> %d"):format(index + p[index].offset))
|
||||
end
|
||||
|
||||
local function printrulename(p, index, rulenames)
|
||||
if rulenames and rulenames[index + p[index].offset] then
|
||||
io.write(' ', rulenames[index + p[index].offset])
|
||||
end
|
||||
end
|
||||
|
||||
local function printinst(p, index, valuetable, rulenames)
|
||||
local code = p[index].code
|
||||
if rulenames and rulenames[index] then
|
||||
io.write(rulenames[index], '\n')
|
||||
end
|
||||
io.write(("%04d: %s "):format(index, names[code]))
|
||||
if code == IChar then
|
||||
io.write(("'%s'"):format(string.char(p[index].val)))
|
||||
elseif code == ITestChar then
|
||||
io.write(("'%s'"):format(string.char(p[index].val)))
|
||||
printjmp(p, index)
|
||||
printrulename(p, index, rulenames)
|
||||
elseif code == IFullCapture then
|
||||
printcapkind(band(p[index].val, 0x0f));
|
||||
io.write((" (size = %d) (idx = %s)"):format(band(rshift(p[index].val, 4), 0xF), tostring(valuetable[p[index].offset])))
|
||||
elseif code == IOpenCapture then
|
||||
printcapkind(band(p[index].val, 0x0f))
|
||||
io.write((" (idx = %s)"):format(tostring(valuetable[p[index].offset])))
|
||||
elseif code == ISet then
|
||||
printcharset(valuetable[p[index].val]);
|
||||
elseif code == ITestSet then
|
||||
printcharset(valuetable[p[index].val])
|
||||
printjmp(p, index);
|
||||
printrulename(p, index, rulenames)
|
||||
elseif code == ISpan then
|
||||
printcharset(valuetable[p[index].val]);
|
||||
elseif code == IOpenCall then
|
||||
io.write(("-> %d"):format(p[index].offset))
|
||||
elseif code == IBehind then
|
||||
io.write(("%d"):format(p[index].val))
|
||||
elseif code == IJmp or code == ICall or code == ICommit or code == IChoice or
|
||||
code == IPartialCommit or code == IBackCommit or code == ITestAny then
|
||||
printjmp(p, index);
|
||||
if (code == ICall or code == IJmp) and p[index].aux > 0 then
|
||||
io.write(' ', valuetable[p[index].aux])
|
||||
else
|
||||
printrulename(p, index, rulenames)
|
||||
end
|
||||
end
|
||||
io.write("\n")
|
||||
end
|
||||
|
||||
|
||||
local function printpatt(p, valuetable)
|
||||
local ruleNames = {}
|
||||
for i = 0, p.size - 1 do
|
||||
local code = p.p[i].code
|
||||
if (code == ICall or code == IJmp) and p.p[i].aux > 0 then
|
||||
local index = i + p.p[i].offset
|
||||
ruleNames[index] = valuetable[p.p[i].aux]
|
||||
end
|
||||
end
|
||||
for i = 0, p.size - 1 do
|
||||
printinst(p.p, i, valuetable, ruleNames)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function printcap(cap, index, valuetable)
|
||||
printcapkind(cap[index].kind)
|
||||
io.write((" (idx: %s - size: %d) -> %d\n"):format(valuetable[cap[index].idx], cap[index].siz, cap[index].s))
|
||||
end
|
||||
|
||||
|
||||
local function printcaplist(cap, limit, valuetable)
|
||||
io.write(">======\n")
|
||||
local index = 0
|
||||
while cap[index].s and index < limit do
|
||||
printcap(cap, index, valuetable)
|
||||
index = index + 1
|
||||
end
|
||||
io.write("=======\n")
|
||||
end
|
||||
|
||||
-- ======================================================
|
||||
|
||||
|
||||
|
||||
-- {======================================================
|
||||
-- Printing trees (for debugging)
|
||||
-- =======================================================
|
||||
|
||||
local tagnames = {
|
||||
[TChar] = "char",
|
||||
[TSet] = "set",
|
||||
[TAny] = "any",
|
||||
[TTrue] = "true",
|
||||
[TFalse] = "false",
|
||||
[TRep] = "rep",
|
||||
[TSeq] = "seq",
|
||||
[TChoice] = "choice",
|
||||
[TNot] = "not",
|
||||
[TAnd] = "and",
|
||||
[TCall] = "call",
|
||||
[TOpenCall] = "opencall",
|
||||
[TRule] = "rule",
|
||||
[TGrammar] = "grammar",
|
||||
[TBehind] = "behind",
|
||||
[TCapture] = "capture",
|
||||
[TRunTime] = "run-time"
|
||||
}
|
||||
|
||||
|
||||
local function printtree(tree, ident, index, valuetable)
|
||||
for i = 1, ident do
|
||||
io.write(" ")
|
||||
end
|
||||
local tag = tree[index].tag
|
||||
io.write(("%s"):format(tagnames[tag]))
|
||||
if tag == TChar then
|
||||
local c = tree[index].val
|
||||
if ffi.C.isprint(c) then
|
||||
io.write((" '%c'\n"):format(c))
|
||||
else
|
||||
io.write((" (%02X)\n"):format(c))
|
||||
end
|
||||
elseif tag == TSet then
|
||||
printcharset(valuetable[tree[index].val]);
|
||||
io.write("\n")
|
||||
elseif tag == TOpenCall or tag == TCall then
|
||||
io.write((" key: %s\n"):format(tostring(valuetable[tree[index].val])))
|
||||
elseif tag == TBehind then
|
||||
io.write((" %d\n"):format(tree[index].val))
|
||||
printtree(tree, ident + 2, index + 1, valuetable);
|
||||
elseif tag == TCapture then
|
||||
io.write((" cap: %s n: %s\n"):format(modes[bit.band(tree[index].cap, 0xffff)], valuetable[tree[index].val]))
|
||||
printtree(tree, ident + 2, index + 1, valuetable);
|
||||
elseif tag == TRule then
|
||||
local extra = bit.band(tree[index].cap, RuleLR) == RuleLR and ' left recursive' or ''
|
||||
extra = extra .. (bit.band(tree[index].cap, Ruleused) ~= Ruleused and ' not used' or '')
|
||||
io.write((" n: %d key: %s%s\n"):format(bit.band(tree[index].cap, 0xffff) - 1, valuetable[tree[index].val], extra))
|
||||
printtree(tree, ident + 2, index + 1, valuetable);
|
||||
-- do not print next rule as a sibling
|
||||
elseif tag == TGrammar then
|
||||
local ruleindex = index + 1
|
||||
io.write((" %d\n"):format(tree[index].val)) -- number of rules
|
||||
for i = 1, tree[index].val do
|
||||
printtree(tree, ident + 2, ruleindex, valuetable);
|
||||
ruleindex = ruleindex + tree[ruleindex].ps
|
||||
end
|
||||
assert(tree[ruleindex].tag == TTrue); -- sentinel
|
||||
else
|
||||
local sibs = numsiblings[tree[index].tag] or 0
|
||||
io.write("\n")
|
||||
if sibs >= 1 then
|
||||
printtree(tree, ident + 2, index + 1, valuetable);
|
||||
if sibs >= 2 then
|
||||
printtree(tree, ident + 2, index + tree[index].ps, valuetable)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- }====================================================== */
|
||||
|
||||
return {
|
||||
printtree = printtree,
|
||||
printpatt = printpatt,
|
||||
printcaplist = printcaplist,
|
||||
printinst = printinst
|
||||
}
|
1041
tools/lpeg/lpvm.lua
Normal file
1041
tools/lpeg/lpvm.lua
Normal file
File diff suppressed because it is too large
Load Diff
286
tools/lpeg/re.lua
Normal file
286
tools/lpeg/re.lua
Normal file
@ -0,0 +1,286 @@
|
||||
-- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $
|
||||
-- 2014/08/15 changes rostislav
|
||||
|
||||
-- imported functions and modules
|
||||
local tonumber, print, error = tonumber, print, error
|
||||
local setmetatable = setmetatable
|
||||
local m = require"lpeglj"
|
||||
|
||||
-- 'm' will be used to parse expressions, and 'mm' will be used to
|
||||
-- create expressions; that is, 're' runs on 'm', creating patterns
|
||||
-- on 'mm'
|
||||
local mm = m
|
||||
|
||||
-- pattern's metatable
|
||||
local mt = getmetatable(mm.P(0))
|
||||
mt = m.version() == "1.0.0.0LJ" and m or mt
|
||||
|
||||
|
||||
|
||||
-- No more global accesses after this point
|
||||
local version = _VERSION
|
||||
if version == "Lua 5.2" then _ENV = nil end
|
||||
|
||||
|
||||
local any = m.P(1)
|
||||
|
||||
|
||||
-- Pre-defined names
|
||||
local Predef = { nl = m.P"\n" }
|
||||
|
||||
|
||||
local mem
|
||||
local fmem
|
||||
local gmem
|
||||
|
||||
|
||||
local function updatelocale ()
|
||||
mm.locale(Predef)
|
||||
Predef.a = Predef.alpha
|
||||
Predef.c = Predef.cntrl
|
||||
Predef.d = Predef.digit
|
||||
Predef.g = Predef.graph
|
||||
Predef.l = Predef.lower
|
||||
Predef.p = Predef.punct
|
||||
Predef.s = Predef.space
|
||||
Predef.u = Predef.upper
|
||||
Predef.w = Predef.alnum
|
||||
Predef.x = Predef.xdigit
|
||||
Predef.A = any - Predef.a
|
||||
Predef.C = any - Predef.c
|
||||
Predef.D = any - Predef.d
|
||||
Predef.G = any - Predef.g
|
||||
Predef.L = any - Predef.l
|
||||
Predef.P = any - Predef.p
|
||||
Predef.S = any - Predef.s
|
||||
Predef.U = any - Predef.u
|
||||
Predef.W = any - Predef.w
|
||||
Predef.X = any - Predef.x
|
||||
mem = {} -- restart memoization
|
||||
fmem = {}
|
||||
gmem = {}
|
||||
local mt = {__mode = "v"}
|
||||
setmetatable(mem, mt)
|
||||
setmetatable(fmem, mt)
|
||||
setmetatable(gmem, mt)
|
||||
end
|
||||
|
||||
|
||||
updatelocale()
|
||||
|
||||
|
||||
|
||||
local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
|
||||
|
||||
|
||||
local function getdef (id, defs)
|
||||
local c = defs and defs[id]
|
||||
if not c then error("undefined name: " .. id) end
|
||||
return c
|
||||
end
|
||||
|
||||
|
||||
local function patt_error (s, i)
|
||||
local msg = (#s < i + 20) and s:sub(i)
|
||||
or s:sub(i,i+20) .. "..."
|
||||
msg = ("pattern error near '%s'"):format(msg)
|
||||
error(msg, 2)
|
||||
end
|
||||
|
||||
local function mult (p, n)
|
||||
local np = mm.P(true)
|
||||
while n >= 1 do
|
||||
if n%2 >= 1 then np = np * p end
|
||||
p = p * p
|
||||
n = n/2
|
||||
end
|
||||
return np
|
||||
end
|
||||
|
||||
local function equalcap (s, i, c)
|
||||
if type(c) ~= "string" then return nil end
|
||||
local e = #c + i
|
||||
if type(s) == 'function' then -- stream mode
|
||||
if s(i, e - 1) == c then return e else return nil end
|
||||
else
|
||||
if s:sub(i, e - 1) == c then return e else return nil end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
|
||||
|
||||
local name = m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0
|
||||
|
||||
local arrow = S * "<-"
|
||||
|
||||
local seq_follow = m.P"/" + ")" + "}" + ":}" + "~}" + "|}" + (name * arrow) + -1
|
||||
|
||||
name = m.C(name)
|
||||
|
||||
|
||||
-- a defined name only have meaning in a given environment
|
||||
local Def = name * m.Carg(1)
|
||||
|
||||
local num = m.C(m.R"09"^1) * S / tonumber
|
||||
|
||||
local String = "'" * m.C((any - "'")^0) * "'" +
|
||||
'"' * m.C((any - '"')^0) * '"'
|
||||
|
||||
|
||||
local defined = "%" * Def / function (c,Defs)
|
||||
local cat = Defs and Defs[c] or Predef[c]
|
||||
if not cat then error ("name '" .. c .. "' undefined") end
|
||||
return cat
|
||||
end
|
||||
|
||||
local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
|
||||
|
||||
local item = defined + Range + m.C(any)
|
||||
|
||||
local Class =
|
||||
"["
|
||||
* (m.C(m.P"^"^-1)) -- optional complement symbol
|
||||
* m.Cf(item * (item - "]")^0, mt.__add) /
|
||||
function (c, p) return c == "^" and any - p or p end
|
||||
* "]"
|
||||
|
||||
local function adddef (t, k, exp)
|
||||
if t[k] then
|
||||
error("'"..k.."' already defined as a rule")
|
||||
else
|
||||
t[k] = exp
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function firstdef (n, r) return adddef({n}, n, r) end
|
||||
|
||||
|
||||
local function NT (n, b, p)
|
||||
if not b then
|
||||
error("rule '"..n.."' used outside a grammar")
|
||||
else return mm.V(n, p or 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local exp = m.P{ "Exp",
|
||||
Exp = S * ( m.V"Grammar"
|
||||
+ m.Cf(m.V"Seq" * ("/" * S * m.V"Seq")^0, mt.__add) );
|
||||
Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix"^0 , mt.__mul)
|
||||
* (#seq_follow + patt_error);
|
||||
Prefix = "&" * S * m.V"Prefix" / mt.__len
|
||||
+ "!" * S * m.V"Prefix" / mt.__unm
|
||||
+ m.V"Suffix";
|
||||
Suffix = m.Cf(m.V"Primary" * S *
|
||||
( ( m.P"+" * m.Cc(1, mt.__pow)
|
||||
+ m.P"*" * m.Cc(0, mt.__pow)
|
||||
+ m.P"?" * m.Cc(-1, mt.__pow)
|
||||
+ "^" * ( m.Cg(num * m.Cc(mult))
|
||||
+ m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow))
|
||||
)
|
||||
+ "->" * S * ( m.Cg((String + num) * m.Cc(mt.__div))
|
||||
+ m.P"{}" * m.Cc(nil, m.Ct)
|
||||
+ m.Cg(Def / getdef * m.Cc(mt.__div))
|
||||
)
|
||||
+ "=>" * S * m.Cg(Def / getdef * m.Cc(m.Cmt))
|
||||
) * S
|
||||
)^0, function (a,b,f) return f(a,b) end );
|
||||
Primary = "(" * m.V"Exp" * ")"
|
||||
+ String / mm.P
|
||||
+ Class
|
||||
+ defined
|
||||
+ "{:" * (name * ":" + m.Cc(nil)) * m.V"Exp" * ":}" /
|
||||
function (n, p) return mm.Cg(p, n) end
|
||||
+ "=" * name / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
|
||||
+ m.P"{}" / mm.Cp
|
||||
+ "{~" * m.V"Exp" * "~}" / mm.Cs
|
||||
+ "{|" * m.V"Exp" * "|}" / mm.Ct
|
||||
+ "{" * m.V"Exp" * "}" / mm.C
|
||||
+ m.P"." * m.Cc(any)
|
||||
+ (name * m.Cb("G") * (S * ":" * S * num)^-1 * -arrow + "<" * name * m.Cb("G") * (S * ":" * S * num)^-1 * ">") / NT;
|
||||
Definition = name * arrow * m.V"Exp";
|
||||
Grammar = m.Cg(m.Cc(true), "G") *
|
||||
m.Cf(m.V"Definition" / firstdef * m.Cg(m.V"Definition")^0,
|
||||
adddef) / mm.P
|
||||
}
|
||||
|
||||
local pattern = S * m.Cg(m.Cc(false), "G") * exp / mm.P * (-any + patt_error)
|
||||
|
||||
|
||||
local function compile (p, defs)
|
||||
if mm.type(p) == "pattern" then return p end -- already compiled
|
||||
local cp = pattern:match(p, 1, defs)
|
||||
if not cp then error("incorrect pattern", 3) end
|
||||
return cp
|
||||
end
|
||||
|
||||
local function match (s, p, i)
|
||||
local cp = mem[p]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
mem[p] = cp
|
||||
end
|
||||
return cp:match(s, i or 1)
|
||||
end
|
||||
|
||||
local function streammatch (p, i)
|
||||
local cp = mem[p]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
mem[p] = cp
|
||||
end
|
||||
return cp:streammatch(i or 1)
|
||||
end
|
||||
|
||||
-- Only for testing purpose
|
||||
local function emulatestreammatch(s, p, i)
|
||||
local cp = mem[p]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
mem[p] = cp
|
||||
end
|
||||
return cp:emulatestreammatch(s, i or 1)
|
||||
end
|
||||
|
||||
local function find (s, p, i)
|
||||
local cp = fmem[p]
|
||||
if not cp then
|
||||
cp = compile(p) / 0
|
||||
cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
|
||||
fmem[p] = cp
|
||||
end
|
||||
local i, e = cp:match(s, i or 1)
|
||||
if i then return i, e - 1
|
||||
else return i
|
||||
end
|
||||
end
|
||||
|
||||
local function gsub (s, p, rep)
|
||||
local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
|
||||
gmem[p] = g
|
||||
local cp = g[rep]
|
||||
if not cp then
|
||||
cp = compile(p)
|
||||
cp = mm.Cs((cp / rep + 1)^0)
|
||||
g[rep] = cp
|
||||
end
|
||||
return cp:match(s)
|
||||
end
|
||||
|
||||
|
||||
-- exported names
|
||||
local re = {
|
||||
compile = compile,
|
||||
match = match,
|
||||
streammatch = streammatch,
|
||||
emulatestreammatch = emulatestreammatch,
|
||||
find = find,
|
||||
gsub = gsub,
|
||||
updatelocale = updatelocale,
|
||||
}
|
||||
|
||||
if version == "Lua 5.1" then _G.re = re end
|
||||
|
||||
return re
|
25
tools/luajson/json.lua
Normal file
25
tools/luajson/json.lua
Normal file
@ -0,0 +1,25 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
package.path = package.path .. ";lpeg/?.lua"
|
||||
local decode = require("json.decode")
|
||||
local encode = require("json.encode")
|
||||
local util = require("json.util")
|
||||
|
||||
local _G = _G
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local json = {
|
||||
_VERSION = "1.3.4",
|
||||
_DESCRIPTION = "LuaJSON : customizable JSON decoder/encoder",
|
||||
_COPYRIGHT = "Copyright (c) 2007-2014 Thomas Harning Jr. <harningt@gmail.com>",
|
||||
decode = decode,
|
||||
encode = encode,
|
||||
util = util
|
||||
}
|
||||
|
||||
_G.json = json
|
||||
|
||||
return json
|
171
tools/luajson/json/decode.lua
Normal file
171
tools/luajson/json/decode.lua
Normal file
@ -0,0 +1,171 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local lpeg = require("lpeg")
|
||||
|
||||
local error = error
|
||||
local pcall = pcall
|
||||
|
||||
local jsonutil = require("json.util")
|
||||
local merge = jsonutil.merge
|
||||
local util = require("json.decode.util")
|
||||
|
||||
local decode_state = require("json.decode.state")
|
||||
|
||||
local setmetatable, getmetatable = setmetatable, getmetatable
|
||||
local assert = assert
|
||||
local ipairs, pairs = ipairs, pairs
|
||||
local string_char = require("string").char
|
||||
|
||||
local type = type
|
||||
|
||||
local require = require
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local modulesToLoad = {
|
||||
"composite",
|
||||
"strings",
|
||||
"number",
|
||||
"others"
|
||||
}
|
||||
local loadedModules = {
|
||||
}
|
||||
|
||||
local json_decode = {}
|
||||
|
||||
json_decode.default = {
|
||||
unicodeWhitespace = true,
|
||||
initialObject = false,
|
||||
nothrow = false
|
||||
}
|
||||
|
||||
local modes_defined = { "default", "strict", "simple" }
|
||||
|
||||
json_decode.simple = {}
|
||||
|
||||
json_decode.strict = {
|
||||
unicodeWhitespace = true,
|
||||
initialObject = true,
|
||||
nothrow = false
|
||||
}
|
||||
|
||||
for _,name in ipairs(modulesToLoad) do
|
||||
local mod = require("json.decode." .. name)
|
||||
if mod.mergeOptions then
|
||||
for _, mode in pairs(modes_defined) do
|
||||
mod.mergeOptions(json_decode[mode], mode)
|
||||
end
|
||||
end
|
||||
loadedModules[#loadedModules + 1] = mod
|
||||
end
|
||||
|
||||
-- Shift over default into defaultOptions to permit build optimization
|
||||
local defaultOptions = json_decode.default
|
||||
json_decode.default = nil
|
||||
|
||||
local function generateDecoder(lexer, options)
|
||||
-- Marker to permit detection of final end
|
||||
local marker = {}
|
||||
local parser = lpeg.Ct((options.ignored * lexer)^0 * lpeg.Cc(marker)) * options.ignored * (lpeg.P(-1) + util.unexpected())
|
||||
local decoder = function(data)
|
||||
local state = decode_state.create(options)
|
||||
local parsed = parser:match(data)
|
||||
assert(parsed, "Invalid JSON data")
|
||||
local i = 0
|
||||
while true do
|
||||
i = i + 1
|
||||
local item = parsed[i]
|
||||
if item == marker then break end
|
||||
if type(item) == 'function' and item ~= jsonutil.undefined and item ~= jsonutil.null then
|
||||
item(state)
|
||||
else
|
||||
state:set_value(item)
|
||||
end
|
||||
end
|
||||
if options.initialObject then
|
||||
assert(type(state.previous) == 'table', "Initial value not an object or array")
|
||||
end
|
||||
-- Make sure stack is empty
|
||||
assert(state.i == 0, "Unclosed elements present")
|
||||
return state.previous
|
||||
end
|
||||
if options.nothrow then
|
||||
return function(data)
|
||||
local status, rv = pcall(decoder, data)
|
||||
if status then
|
||||
return rv
|
||||
else
|
||||
return nil, rv
|
||||
end
|
||||
end
|
||||
end
|
||||
return decoder
|
||||
end
|
||||
|
||||
local function buildDecoder(mode)
|
||||
mode = mode and merge({}, defaultOptions, mode) or defaultOptions
|
||||
for _, mod in ipairs(loadedModules) do
|
||||
if mod.mergeOptions then
|
||||
mod.mergeOptions(mode)
|
||||
end
|
||||
end
|
||||
local ignored = mode.unicodeWhitespace and util.unicode_ignored or util.ascii_ignored
|
||||
-- Store 'ignored' in the global options table
|
||||
mode.ignored = ignored
|
||||
|
||||
--local grammar = {
|
||||
-- [1] = mode.initialObject and (ignored * (object_type + array_type)) or value_type
|
||||
--}
|
||||
local lexer
|
||||
for _, mod in ipairs(loadedModules) do
|
||||
local new_lexer = mod.generateLexer(mode)
|
||||
lexer = lexer and lexer + new_lexer or new_lexer
|
||||
end
|
||||
return generateDecoder(lexer, mode)
|
||||
end
|
||||
|
||||
-- Since 'default' is nil, we cannot take map it
|
||||
local defaultDecoder = buildDecoder(json_decode.default)
|
||||
local prebuilt_decoders = {}
|
||||
for _, mode in pairs(modes_defined) do
|
||||
if json_decode[mode] ~= nil then
|
||||
prebuilt_decoders[json_decode[mode]] = buildDecoder(json_decode[mode])
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Options:
|
||||
number => number decode options
|
||||
string => string decode options
|
||||
array => array decode options
|
||||
object => object decode options
|
||||
initialObject => whether or not to require the initial object to be a table/array
|
||||
allowUndefined => whether or not to allow undefined values
|
||||
]]
|
||||
local function getDecoder(mode)
|
||||
mode = mode == true and json_decode.strict or mode or json_decode.default
|
||||
local decoder = mode == nil and defaultDecoder or prebuilt_decoders[mode]
|
||||
if decoder then
|
||||
return decoder
|
||||
end
|
||||
return buildDecoder(mode)
|
||||
end
|
||||
|
||||
local function decode(data, mode)
|
||||
local decoder = getDecoder(mode)
|
||||
return decoder(data)
|
||||
end
|
||||
|
||||
local mt = {}
|
||||
mt.__call = function(self, ...)
|
||||
return decode(...)
|
||||
end
|
||||
|
||||
json_decode.getDecoder = getDecoder
|
||||
json_decode.decode = decode
|
||||
json_decode.util = util
|
||||
setmetatable(json_decode, mt)
|
||||
|
||||
return json_decode
|
190
tools/luajson/json/decode/composite.lua
Normal file
190
tools/luajson/json/decode/composite.lua
Normal file
@ -0,0 +1,190 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local pairs = pairs
|
||||
local type = type
|
||||
|
||||
local lpeg = require("lpeg")
|
||||
|
||||
local util = require("json.decode.util")
|
||||
local jsonutil = require("json.util")
|
||||
|
||||
local rawset = rawset
|
||||
|
||||
local assert = assert
|
||||
local tostring = tostring
|
||||
|
||||
local error = error
|
||||
local getmetatable = getmetatable
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local defaultOptions = {
|
||||
array = {
|
||||
trailingComma = true
|
||||
},
|
||||
object = {
|
||||
trailingComma = true,
|
||||
number = true,
|
||||
identifier = true,
|
||||
setObjectKey = rawset
|
||||
},
|
||||
calls = {
|
||||
defs = nil,
|
||||
-- By default, do not allow undefined calls to be de-serialized as call objects
|
||||
allowUndefined = false
|
||||
}
|
||||
}
|
||||
|
||||
local modeOptions = {
|
||||
default = nil,
|
||||
strict = {
|
||||
array = {
|
||||
trailingComma = false
|
||||
},
|
||||
object = {
|
||||
trailingComma = false,
|
||||
number = false,
|
||||
identifier = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local function BEGIN_ARRAY(state)
|
||||
state:push()
|
||||
state:new_array()
|
||||
end
|
||||
local function END_ARRAY(state)
|
||||
state:end_array()
|
||||
state:pop()
|
||||
end
|
||||
|
||||
local function BEGIN_OBJECT(state)
|
||||
state:push()
|
||||
state:new_object()
|
||||
end
|
||||
local function END_OBJECT(state)
|
||||
state:end_object()
|
||||
state:pop()
|
||||
end
|
||||
|
||||
local function END_CALL(state)
|
||||
state:end_call()
|
||||
state:pop()
|
||||
end
|
||||
|
||||
local function SET_KEY(state)
|
||||
state:set_key()
|
||||
end
|
||||
|
||||
local function NEXT_VALUE(state)
|
||||
state:put_value()
|
||||
end
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, true, 'array', defaultOptions, mode and modeOptions[mode])
|
||||
jsonutil.doOptionMerge(options, true, 'object', defaultOptions, mode and modeOptions[mode])
|
||||
jsonutil.doOptionMerge(options, true, 'calls', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
|
||||
local isPattern
|
||||
if lpeg.type then
|
||||
function isPattern(value)
|
||||
return lpeg.type(value) == 'pattern'
|
||||
end
|
||||
else
|
||||
local metaAdd = getmetatable(lpeg.P("")).__add
|
||||
function isPattern(value)
|
||||
return getmetatable(value).__add == metaAdd
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function generateSingleCallLexer(name, func)
|
||||
if type(name) ~= 'string' and not isPattern(name) then
|
||||
error("Invalid functionCalls name: " .. tostring(name) .. " not a string or LPEG pattern")
|
||||
end
|
||||
-- Allow boolean or function to match up w/ encoding permissions
|
||||
if type(func) ~= 'boolean' and type(func) ~= 'function' then
|
||||
error("Invalid functionCalls item: " .. name .. " not a function")
|
||||
end
|
||||
local function buildCallCapture(name)
|
||||
return function(state)
|
||||
if func == false then
|
||||
error("Function call on '" .. name .. "' not permitted")
|
||||
end
|
||||
state:push()
|
||||
state:new_call(name, func)
|
||||
end
|
||||
end
|
||||
local nameCallCapture
|
||||
if type(name) == 'string' then
|
||||
nameCallCapture = lpeg.P(name .. "(") * lpeg.Cc(name) / buildCallCapture
|
||||
else
|
||||
-- Name matcher expected to produce a capture
|
||||
nameCallCapture = name * "(" / buildCallCapture
|
||||
end
|
||||
-- Call func over nameCallCapture and value to permit function receiving name
|
||||
return nameCallCapture
|
||||
end
|
||||
|
||||
local function generateNamedCallLexers(options)
|
||||
if not options.calls or not options.calls.defs then
|
||||
return
|
||||
end
|
||||
local callCapture
|
||||
for name, func in pairs(options.calls.defs) do
|
||||
local newCapture = generateSingleCallLexer(name, func)
|
||||
if not callCapture then
|
||||
callCapture = newCapture
|
||||
else
|
||||
callCapture = callCapture + newCapture
|
||||
end
|
||||
end
|
||||
return callCapture
|
||||
end
|
||||
|
||||
local function generateCallLexer(options)
|
||||
local lexer
|
||||
local namedCapture = generateNamedCallLexers(options)
|
||||
if options.calls and options.calls.allowUndefined then
|
||||
lexer = generateSingleCallLexer(lpeg.C(util.identifier), true)
|
||||
end
|
||||
if namedCapture then
|
||||
lexer = lexer and lexer + namedCapture or namedCapture
|
||||
end
|
||||
if lexer then
|
||||
lexer = lexer + lpeg.P(")") * lpeg.Cc(END_CALL)
|
||||
end
|
||||
return lexer
|
||||
end
|
||||
|
||||
local function generateLexer(options)
|
||||
local ignored = options.ignored
|
||||
local array_options, object_options = options.array, options.object
|
||||
local lexer =
|
||||
lpeg.P("[") * lpeg.Cc(BEGIN_ARRAY)
|
||||
+ lpeg.P("]") * lpeg.Cc(END_ARRAY)
|
||||
+ lpeg.P("{") * lpeg.Cc(BEGIN_OBJECT)
|
||||
+ lpeg.P("}") * lpeg.Cc(END_OBJECT)
|
||||
+ lpeg.P(":") * lpeg.Cc(SET_KEY)
|
||||
+ lpeg.P(",") * lpeg.Cc(NEXT_VALUE)
|
||||
if object_options.identifier then
|
||||
-- Add identifier match w/ validation check that it is in key
|
||||
lexer = lexer + lpeg.C(util.identifier) * ignored * lpeg.P(":") * lpeg.Cc(SET_KEY)
|
||||
end
|
||||
local callLexers = generateCallLexer(options)
|
||||
if callLexers then
|
||||
lexer = lexer + callLexers
|
||||
end
|
||||
return lexer
|
||||
end
|
||||
|
||||
local composite = {
|
||||
mergeOptions = mergeOptions,
|
||||
generateLexer = generateLexer
|
||||
}
|
||||
|
||||
return composite
|
100
tools/luajson/json/decode/number.lua
Normal file
100
tools/luajson/json/decode/number.lua
Normal file
@ -0,0 +1,100 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local lpeg = require("lpeg")
|
||||
local tonumber = tonumber
|
||||
local jsonutil = require("json.util")
|
||||
local merge = jsonutil.merge
|
||||
local util = require("json.decode.util")
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local digit = lpeg.R("09")
|
||||
local digits = digit^1
|
||||
|
||||
-- Illegal octal declaration
|
||||
local illegal_octal_detect = #(lpeg.P('0') * digits) * util.denied("Octal numbers")
|
||||
|
||||
local int = (lpeg.P('-') + 0) * (lpeg.R("19") * digits + illegal_octal_detect + digit)
|
||||
|
||||
local frac = lpeg.P('.') * digits
|
||||
|
||||
local exp = lpeg.S("Ee") * (lpeg.S("-+") + 0) * digits
|
||||
|
||||
local nan = lpeg.S("Nn") * lpeg.S("Aa") * lpeg.S("Nn")
|
||||
local inf = lpeg.S("Ii") * lpeg.P("nfinity")
|
||||
local ninf = lpeg.P('-') * lpeg.S("Ii") * lpeg.P("nfinity")
|
||||
local hex = (lpeg.P("0x") + lpeg.P("0X")) * lpeg.R("09","AF","af")^1
|
||||
|
||||
local defaultOptions = {
|
||||
nan = true,
|
||||
inf = true,
|
||||
frac = true,
|
||||
exp = true,
|
||||
hex = false
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
modeOptions.strict = {
|
||||
nan = false,
|
||||
inf = false
|
||||
}
|
||||
|
||||
local nan_value = 0/0
|
||||
local inf_value = 1/0
|
||||
local ninf_value = -1/0
|
||||
|
||||
--[[
|
||||
Options: configuration options for number rules
|
||||
nan: match NaN
|
||||
inf: match Infinity
|
||||
frac: match fraction portion (.0)
|
||||
exp: match exponent portion (e1)
|
||||
DEFAULT: nan, inf, frac, exp
|
||||
]]
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'number', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
local function generateLexer(options)
|
||||
options = options.number
|
||||
local ret = int
|
||||
if options.frac then
|
||||
ret = ret * (frac + 0)
|
||||
else
|
||||
ret = ret * (#frac * util.denied("Fractions", "number.frac") + 0)
|
||||
end
|
||||
if options.exp then
|
||||
ret = ret * (exp + 0)
|
||||
else
|
||||
ret = ret * (#exp * util.denied("Exponents", "number.exp") + 0)
|
||||
end
|
||||
if options.hex then
|
||||
ret = hex + ret
|
||||
else
|
||||
ret = #hex * util.denied("Hexadecimal", "number.hex") + ret
|
||||
end
|
||||
-- Capture number now
|
||||
ret = ret / tonumber
|
||||
if options.nan then
|
||||
ret = ret + nan / function() return nan_value end
|
||||
else
|
||||
ret = ret + #nan * util.denied("NaN", "number.nan")
|
||||
end
|
||||
if options.inf then
|
||||
ret = ret + ninf / function() return ninf_value end + inf / function() return inf_value end
|
||||
else
|
||||
ret = ret + (#ninf + #inf) * util.denied("+/-Inf", "number.inf")
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local number = {
|
||||
int = int,
|
||||
mergeOptions = mergeOptions,
|
||||
generateLexer = generateLexer
|
||||
}
|
||||
|
||||
return number
|
62
tools/luajson/json/decode/others.lua
Normal file
62
tools/luajson/json/decode/others.lua
Normal file
@ -0,0 +1,62 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local lpeg = require("lpeg")
|
||||
local jsonutil = require("json.util")
|
||||
local merge = jsonutil.merge
|
||||
local util = require("json.decode.util")
|
||||
|
||||
-- Container module for other JavaScript types (bool, null, undefined)
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
-- For null and undefined, use the util.null value to preserve null-ness
|
||||
local booleanCapture =
|
||||
lpeg.P("true") * lpeg.Cc(true)
|
||||
+ lpeg.P("false") * lpeg.Cc(false)
|
||||
|
||||
local nullCapture = lpeg.P("null")
|
||||
local undefinedCapture = lpeg.P("undefined")
|
||||
|
||||
local defaultOptions = {
|
||||
allowUndefined = true,
|
||||
null = jsonutil.null,
|
||||
undefined = jsonutil.undefined
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
modeOptions.simple = {
|
||||
null = false, -- Mapped to nil
|
||||
undefined = false -- Mapped to nil
|
||||
}
|
||||
modeOptions.strict = {
|
||||
allowUndefined = false
|
||||
}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'others', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
local function generateLexer(options)
|
||||
-- The 'or nil' clause allows false to map to a nil value since 'nil' cannot be merged
|
||||
options = options.others
|
||||
local valueCapture = (
|
||||
booleanCapture
|
||||
+ nullCapture * lpeg.Cc(options.null or nil)
|
||||
)
|
||||
if options.allowUndefined then
|
||||
valueCapture = valueCapture + undefinedCapture * lpeg.Cc(options.undefined or nil)
|
||||
else
|
||||
valueCapture = valueCapture + #undefinedCapture * util.denied("undefined", "others.allowUndefined")
|
||||
end
|
||||
return valueCapture
|
||||
end
|
||||
|
||||
local others = {
|
||||
mergeOptions = mergeOptions,
|
||||
generateLexer = generateLexer
|
||||
}
|
||||
|
||||
return others
|
189
tools/luajson/json/decode/state.lua
Normal file
189
tools/luajson/json/decode/state.lua
Normal file
@ -0,0 +1,189 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
|
||||
local setmetatable = setmetatable
|
||||
local jsonutil = require("json.util")
|
||||
local assert = assert
|
||||
local type = type
|
||||
local next = next
|
||||
local unpack = require("table").unpack or unpack
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local state_ops = {}
|
||||
local state_mt = {
|
||||
__index = state_ops
|
||||
}
|
||||
|
||||
function state_ops.pop(self)
|
||||
self.previous_set = true
|
||||
self.previous = self.active
|
||||
local i = self.i
|
||||
-- Load in this array into the active item
|
||||
self.active = self.stack[i]
|
||||
self.active_state = self.state_stack[i]
|
||||
self.active_key = self.key_stack[i]
|
||||
self.stack[i] = nil
|
||||
self.state_stack[i] = nil
|
||||
self.key_stack[i] = nil
|
||||
|
||||
self.i = i - 1
|
||||
end
|
||||
|
||||
function state_ops.push(self)
|
||||
local i = self.i + 1
|
||||
self.i = i
|
||||
|
||||
self.stack[i] = self.active
|
||||
self.state_stack[i] = self.active_state
|
||||
self.key_stack[i] = self.active_key
|
||||
end
|
||||
|
||||
function state_ops.put_object_value(self, trailing)
|
||||
local object_options = self.options.object
|
||||
if trailing and object_options.trailingComma then
|
||||
if not self.active_key then
|
||||
return
|
||||
end
|
||||
end
|
||||
assert(self.active_key, "Missing key value")
|
||||
object_options.setObjectKey(self.active, self.active_key, self:grab_value())
|
||||
self.active_key = nil
|
||||
end
|
||||
|
||||
function state_ops.put_array_value(self, trailing)
|
||||
-- Safety check
|
||||
if trailing and not self.previous_set and self.options.array.trailingComma then
|
||||
return
|
||||
end
|
||||
local new_index = self.active_state + 1
|
||||
self.active_state = new_index
|
||||
self.active[new_index] = self:grab_value()
|
||||
end
|
||||
|
||||
function state_ops.put_value(self, trailing)
|
||||
if self.active_state == 'object' then
|
||||
self:put_object_value(trailing)
|
||||
else
|
||||
self:put_array_value(trailing)
|
||||
end
|
||||
end
|
||||
|
||||
function state_ops.new_array(self)
|
||||
local new_array = {}
|
||||
if jsonutil.InitArray then
|
||||
new_array = jsonutil.InitArray(new_array) or new_array
|
||||
end
|
||||
self.active = new_array
|
||||
self.active_state = 0
|
||||
self.active_key = nil
|
||||
self:unset_value()
|
||||
end
|
||||
|
||||
function state_ops.end_array(self)
|
||||
if self.previous_set or self.active_state ~= 0 then
|
||||
-- Not an empty array
|
||||
self:put_value(true)
|
||||
end
|
||||
if self.active_state ~= #self.active then
|
||||
-- Store the length in
|
||||
self.active.n = self.active_state
|
||||
end
|
||||
end
|
||||
|
||||
function state_ops.new_object(self)
|
||||
local new_object = {}
|
||||
self.active = new_object
|
||||
self.active_state = 'object'
|
||||
self.active_key = nil
|
||||
self:unset_value()
|
||||
end
|
||||
|
||||
function state_ops.end_object(self)
|
||||
if self.previous_set or next(self.active) then
|
||||
-- Not an empty object
|
||||
self:put_value(true)
|
||||
end
|
||||
end
|
||||
|
||||
function state_ops.new_call(self, name, func)
|
||||
-- TODO setup properly
|
||||
local new_call = {}
|
||||
new_call.name = name
|
||||
new_call.func = func
|
||||
self.active = new_call
|
||||
self.active_state = 0
|
||||
self.active_key = nil
|
||||
self:unset_value()
|
||||
end
|
||||
|
||||
function state_ops.end_call(self)
|
||||
if self.previous_set or self.active_state ~= 0 then
|
||||
-- Not an empty array
|
||||
self:put_value(true)
|
||||
end
|
||||
if self.active_state ~= #self.active then
|
||||
-- Store the length in
|
||||
self.active.n = self.active_state
|
||||
end
|
||||
local func = self.active.func
|
||||
if func == true then
|
||||
func = jsonutil.buildCall
|
||||
end
|
||||
self.active = func(self.active.name, unpack(self.active, 1, self.active.n or #self.active))
|
||||
end
|
||||
|
||||
|
||||
function state_ops.unset_value(self)
|
||||
self.previous_set = false
|
||||
self.previous = nil
|
||||
end
|
||||
|
||||
function state_ops.grab_value(self)
|
||||
assert(self.previous_set, "Previous value not set")
|
||||
self.previous_set = false
|
||||
return self.previous
|
||||
end
|
||||
|
||||
function state_ops.set_value(self, value)
|
||||
assert(not self.previous_set, "Value set when one already in slot")
|
||||
self.previous_set = true
|
||||
self.previous = value
|
||||
end
|
||||
|
||||
function state_ops.set_key(self)
|
||||
assert(self.active_state == 'object', "Cannot set key on array")
|
||||
local value = self:grab_value()
|
||||
local value_type = type(value)
|
||||
if self.options.object.number then
|
||||
assert(value_type == 'string' or value_type == 'number', "As configured, a key must be a number or string")
|
||||
else
|
||||
assert(value_type == 'string', "As configured, a key must be a string")
|
||||
end
|
||||
self.active_key = value
|
||||
end
|
||||
|
||||
|
||||
local function create(options)
|
||||
local ret = {
|
||||
options = options,
|
||||
stack = {},
|
||||
state_stack = {},
|
||||
key_stack = {},
|
||||
i = 0,
|
||||
active = nil,
|
||||
active_key = nil,
|
||||
previous = nil,
|
||||
active_state = nil
|
||||
|
||||
}
|
||||
return setmetatable(ret, state_mt)
|
||||
end
|
||||
|
||||
local state = {
|
||||
create = create
|
||||
}
|
||||
|
||||
return state
|
133
tools/luajson/json/decode/strings.lua
Normal file
133
tools/luajson/json/decode/strings.lua
Normal file
@ -0,0 +1,133 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local lpeg = require("lpeg")
|
||||
local jsonutil = require("json.util")
|
||||
local util = require("json.decode.util")
|
||||
local merge = jsonutil.merge
|
||||
|
||||
local tonumber = tonumber
|
||||
local string_char = require("string").char
|
||||
local floor = require("math").floor
|
||||
local table_concat = require("table").concat
|
||||
|
||||
local error = error
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local function get_error(item)
|
||||
local fmt_string = item .. " in string [%q] @ %i:%i"
|
||||
return lpeg.P(function(data, index)
|
||||
local line, line_index, bad_char, last_line = util.get_invalid_character_info(data, index)
|
||||
local err = fmt_string:format(bad_char, line, line_index)
|
||||
error(err)
|
||||
end) * 1
|
||||
end
|
||||
|
||||
local bad_unicode = get_error("Illegal unicode escape")
|
||||
local bad_hex = get_error("Illegal hex escape")
|
||||
local bad_character = get_error("Illegal character")
|
||||
local bad_escape = get_error("Illegal escape")
|
||||
|
||||
local knownReplacements = {
|
||||
["'"] = "'",
|
||||
['"'] = '"',
|
||||
['\\'] = '\\',
|
||||
['/'] = '/',
|
||||
b = '\b',
|
||||
f = '\f',
|
||||
n = '\n',
|
||||
r = '\r',
|
||||
t = '\t',
|
||||
v = '\v',
|
||||
z = '\z'
|
||||
}
|
||||
|
||||
-- according to the table at http://da.wikipedia.org/wiki/UTF-8
|
||||
local function utf8DecodeUnicode(code1, code2)
|
||||
code1, code2 = tonumber(code1, 16), tonumber(code2, 16)
|
||||
if code1 == 0 and code2 < 0x80 then
|
||||
return string_char(code2)
|
||||
end
|
||||
if code1 < 0x08 then
|
||||
return string_char(
|
||||
0xC0 + code1 * 4 + floor(code2 / 64),
|
||||
0x80 + code2 % 64)
|
||||
end
|
||||
return string_char(
|
||||
0xE0 + floor(code1 / 16),
|
||||
0x80 + (code1 % 16) * 4 + floor(code2 / 64),
|
||||
0x80 + code2 % 64)
|
||||
end
|
||||
|
||||
local function decodeX(code)
|
||||
code = tonumber(code, 16)
|
||||
return string_char(code)
|
||||
end
|
||||
|
||||
local doSimpleSub = lpeg.C(lpeg.S("'\"\\/bfnrtvz")) / knownReplacements
|
||||
local doUniSub = lpeg.P('u') * (lpeg.C(util.hexpair) * lpeg.C(util.hexpair) + bad_unicode)
|
||||
local doXSub = lpeg.P('x') * (lpeg.C(util.hexpair) + bad_hex)
|
||||
|
||||
local defaultOptions = {
|
||||
badChars = '',
|
||||
additionalEscapes = false, -- disallow untranslated escapes
|
||||
escapeCheck = #lpeg.S('bfnrtv/\\"xu\'z'), -- no check on valid characters
|
||||
decodeUnicode = utf8DecodeUnicode,
|
||||
strict_quotes = false
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
modeOptions.strict = {
|
||||
badChars = '\b\f\n\r\t\v',
|
||||
additionalEscapes = false, -- no additional escapes
|
||||
escapeCheck = #lpeg.S('bfnrtv/\\"u'), --only these chars are allowed to be escaped
|
||||
strict_quotes = true
|
||||
}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'strings', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
local function buildCaptureString(quote, badChars, escapeMatch)
|
||||
local captureChar = (1 - lpeg.S("\\" .. badChars .. quote)) + (lpeg.P("\\") / "" * escapeMatch)
|
||||
-- During error, force end
|
||||
local captureString = captureChar^0 + (-#lpeg.P(quote) * bad_character + -1)
|
||||
return lpeg.P(quote) * lpeg.Cs(captureString) * lpeg.P(quote)
|
||||
end
|
||||
|
||||
local function generateLexer(options)
|
||||
options = options.strings
|
||||
local quotes = { '"' }
|
||||
if not options.strict_quotes then
|
||||
quotes[#quotes + 1] = "'"
|
||||
end
|
||||
local escapeMatch = doSimpleSub
|
||||
escapeMatch = escapeMatch + doXSub / decodeX
|
||||
escapeMatch = escapeMatch + doUniSub / options.decodeUnicode
|
||||
if options.escapeCheck then
|
||||
escapeMatch = options.escapeCheck * escapeMatch + bad_escape
|
||||
end
|
||||
if options.additionalEscapes then
|
||||
escapeMatch = options.additionalEscapes + escapeMatch
|
||||
end
|
||||
local captureString
|
||||
for i = 1, #quotes do
|
||||
local cap = buildCaptureString(quotes[i], options.badChars, escapeMatch)
|
||||
if captureString == nil then
|
||||
captureString = cap
|
||||
else
|
||||
captureString = captureString + cap
|
||||
end
|
||||
end
|
||||
return captureString
|
||||
end
|
||||
|
||||
local strings = {
|
||||
mergeOptions = mergeOptions,
|
||||
generateLexer = generateLexer
|
||||
}
|
||||
|
||||
return strings
|
121
tools/luajson/json/decode/util.lua
Normal file
121
tools/luajson/json/decode/util.lua
Normal file
@ -0,0 +1,121 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local lpeg = require("lpeg")
|
||||
local select = select
|
||||
local pairs, ipairs = pairs, ipairs
|
||||
local tonumber = tonumber
|
||||
local string_char = require("string").char
|
||||
local rawset = rawset
|
||||
local jsonutil = require("json.util")
|
||||
|
||||
local error = error
|
||||
local setmetatable = setmetatable
|
||||
|
||||
local table_concat = require("table").concat
|
||||
|
||||
local merge = require("json.util").merge
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local function get_invalid_character_info(input, index)
|
||||
local parsed = input:sub(1, index)
|
||||
local bad_character = input:sub(index, index)
|
||||
local _, line_number = parsed:gsub('\n',{})
|
||||
local last_line = parsed:match("\n([^\n]+.)$") or parsed
|
||||
return line_number, #last_line, bad_character, last_line
|
||||
end
|
||||
|
||||
local function build_report(msg)
|
||||
local fmt = msg:gsub("%%", "%%%%") .. " @ character: %i %i:%i [%s] line:\n%s"
|
||||
return lpeg.P(function(data, pos)
|
||||
local line, line_index, bad_char, last_line = get_invalid_character_info(data, pos)
|
||||
local text = fmt:format(pos, line, line_index, bad_char, last_line)
|
||||
error(text)
|
||||
end) * 1
|
||||
end
|
||||
local function unexpected()
|
||||
local msg = "unexpected character"
|
||||
return build_report(msg)
|
||||
end
|
||||
local function denied(item, option)
|
||||
local msg
|
||||
if option then
|
||||
msg = ("'%s' denied by option set '%s'"):format(item, option)
|
||||
else
|
||||
msg = ("'%s' denied"):format(item)
|
||||
end
|
||||
return build_report(msg)
|
||||
end
|
||||
|
||||
-- 09, 0A, 0B, 0C, 0D, 20
|
||||
local ascii_space = lpeg.S("\t\n\v\f\r ")
|
||||
local unicode_space
|
||||
do
|
||||
local chr = string_char
|
||||
local u_space = ascii_space
|
||||
-- \u0085 \u00A0
|
||||
u_space = u_space + lpeg.P(chr(0xC2)) * lpeg.S(chr(0x85) .. chr(0xA0))
|
||||
-- \u1680 \u180E
|
||||
u_space = u_space + lpeg.P(chr(0xE1)) * (lpeg.P(chr(0x9A, 0x80)) + chr(0xA0, 0x8E))
|
||||
-- \u2000 - \u200A, also 200B
|
||||
local spacing_end = ""
|
||||
for i = 0x80,0x8b do
|
||||
spacing_end = spacing_end .. chr(i)
|
||||
end
|
||||
-- \u2028 \u2029 \u202F
|
||||
spacing_end = spacing_end .. chr(0xA8) .. chr(0xA9) .. chr(0xAF)
|
||||
u_space = u_space + lpeg.P(chr(0xE2, 0x80)) * lpeg.S(spacing_end)
|
||||
-- \u205F
|
||||
u_space = u_space + lpeg.P(chr(0xE2, 0x81, 0x9F))
|
||||
-- \u3000
|
||||
u_space = u_space + lpeg.P(chr(0xE3, 0x80, 0x80))
|
||||
-- BOM \uFEFF
|
||||
u_space = u_space + lpeg.P(chr(0xEF, 0xBB, 0xBF))
|
||||
unicode_space = u_space
|
||||
end
|
||||
|
||||
local identifier = lpeg.R("AZ","az","__") * lpeg.R("AZ","az", "__", "09") ^0
|
||||
|
||||
local hex = lpeg.R("09","AF","af")
|
||||
local hexpair = hex * hex
|
||||
|
||||
local comments = {
|
||||
cpp = lpeg.P("//") * (1 - lpeg.P("\n"))^0 * lpeg.P("\n"),
|
||||
c = lpeg.P("/*") * (1 - lpeg.P("*/"))^0 * lpeg.P("*/")
|
||||
}
|
||||
|
||||
local comment = comments.cpp + comments.c
|
||||
|
||||
local ascii_ignored = (ascii_space + comment)^0
|
||||
|
||||
local unicode_ignored = (unicode_space + comment)^0
|
||||
|
||||
-- Parse the lpeg version skipping patch-values
|
||||
-- LPEG <= 0.7 have no version value... so 0.7 is value
|
||||
local DecimalLpegVersion = lpeg.version and tonumber(lpeg.version():match("^(%d+%.%d+)")) or 0.7
|
||||
|
||||
local function setObjectKeyForceNumber(t, key, value)
|
||||
key = tonumber(key) or key
|
||||
return rawset(t, key, value)
|
||||
end
|
||||
|
||||
local util = {
|
||||
unexpected = unexpected,
|
||||
denied = denied,
|
||||
ascii_space = ascii_space,
|
||||
unicode_space = unicode_space,
|
||||
identifier = identifier,
|
||||
hex = hex,
|
||||
hexpair = hexpair,
|
||||
comments = comments,
|
||||
comment = comment,
|
||||
ascii_ignored = ascii_ignored,
|
||||
unicode_ignored = unicode_ignored,
|
||||
DecimalLpegVersion = DecimalLpegVersion,
|
||||
get_invalid_character_info = get_invalid_character_info,
|
||||
setObjectKeyForceNumber = setObjectKeyForceNumber
|
||||
}
|
||||
|
||||
return util
|
161
tools/luajson/json/encode.lua
Normal file
161
tools/luajson/json/encode.lua
Normal file
@ -0,0 +1,161 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local type = type
|
||||
local assert, error = assert, error
|
||||
local getmetatable, setmetatable = getmetatable, setmetatable
|
||||
|
||||
local ipairs, pairs = ipairs, pairs
|
||||
local require = require
|
||||
|
||||
local output = require("json.encode.output")
|
||||
|
||||
local util = require("json.util")
|
||||
local util_merge, isCall = util.merge, util.isCall
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
--[[
|
||||
List of encoding modules to load.
|
||||
Loaded in sequence such that earlier encoders get priority when
|
||||
duplicate type-handlers exist.
|
||||
]]
|
||||
local modulesToLoad = {
|
||||
"strings",
|
||||
"number",
|
||||
"calls",
|
||||
"others",
|
||||
"array",
|
||||
"object"
|
||||
}
|
||||
-- Modules that have been loaded
|
||||
local loadedModules = {}
|
||||
|
||||
local json_encode = {}
|
||||
|
||||
-- Configuration bases for client apps
|
||||
local modes_defined = { "default", "strict" }
|
||||
|
||||
json_encode.default = {}
|
||||
json_encode.strict = {
|
||||
initialObject = true -- Require an object at the root
|
||||
}
|
||||
|
||||
-- For each module, load it and its defaults
|
||||
for _,name in ipairs(modulesToLoad) do
|
||||
local mod = require("json.encode." .. name)
|
||||
if mod.mergeOptions then
|
||||
for _, mode in pairs(modes_defined) do
|
||||
mod.mergeOptions(json_encode[mode], mode)
|
||||
end
|
||||
end
|
||||
loadedModules[name] = mod
|
||||
end
|
||||
|
||||
-- NOTE: Nested not found, so assume unsupported until use case arises
|
||||
local function flattenOutput(out, value)
|
||||
assert(type(value) ~= 'table')
|
||||
out = out or {}
|
||||
out[#out + 1] = value
|
||||
return out
|
||||
end
|
||||
|
||||
-- Prepares the encoding map from the already provided modules and new config
|
||||
local function prepareEncodeMap(options)
|
||||
local map = {}
|
||||
for _, name in ipairs(modulesToLoad) do
|
||||
local encodermap = loadedModules[name].getEncoder(options[name])
|
||||
for valueType, encoderSet in pairs(encodermap) do
|
||||
map[valueType] = flattenOutput(map[valueType], encoderSet)
|
||||
end
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
--[[
|
||||
Encode a value with a given encoding map and state
|
||||
]]
|
||||
local function encodeWithMap(value, map, state, isObjectKey)
|
||||
local t = type(value)
|
||||
local encoderList = assert(map[t], "Failed to encode value, unhandled type: " .. t)
|
||||
for _, encoder in ipairs(encoderList) do
|
||||
local ret = encoder(value, state, isObjectKey)
|
||||
if false ~= ret then
|
||||
return ret
|
||||
end
|
||||
end
|
||||
error("Failed to encode value, encoders for " .. t .. " deny encoding")
|
||||
end
|
||||
|
||||
|
||||
local function getBaseEncoder(options)
|
||||
local encoderMap = prepareEncodeMap(options)
|
||||
if options.preProcess then
|
||||
local preProcess = options.preProcess
|
||||
return function(value, state, isObjectKey)
|
||||
local ret = preProcess(value, isObjectKey or false)
|
||||
if nil ~= ret then
|
||||
value = ret
|
||||
end
|
||||
return encodeWithMap(value, encoderMap, state)
|
||||
end
|
||||
end
|
||||
return function(value, state, isObjectKey)
|
||||
return encodeWithMap(value, encoderMap, state)
|
||||
end
|
||||
end
|
||||
--[[
|
||||
Retreive an initial encoder instance based on provided options
|
||||
the initial encoder is responsible for initializing state
|
||||
State has at least these values configured: encode, check_unique, already_encoded
|
||||
]]
|
||||
function json_encode.getEncoder(options)
|
||||
options = options and util_merge({}, json_encode.default, options) or json_encode.default
|
||||
local encode = getBaseEncoder(options)
|
||||
|
||||
local function initialEncode(value)
|
||||
if options.initialObject then
|
||||
local errorMessage = "Invalid arguments: expects a JSON Object or Array at the root"
|
||||
assert(type(value) == 'table' and not isCall(value, options), errorMessage)
|
||||
end
|
||||
|
||||
local alreadyEncoded = {}
|
||||
local function check_unique(value)
|
||||
assert(not alreadyEncoded[value], "Recursive encoding of value")
|
||||
alreadyEncoded[value] = true
|
||||
end
|
||||
|
||||
local outputEncoder = options.output and options.output() or output.getDefault()
|
||||
local state = {
|
||||
encode = encode,
|
||||
check_unique = check_unique,
|
||||
already_encoded = alreadyEncoded, -- To unmark encoding when moving up stack
|
||||
outputEncoder = outputEncoder
|
||||
}
|
||||
local ret = encode(value, state)
|
||||
if nil ~= ret then
|
||||
return outputEncoder.simple and outputEncoder.simple(ret) or ret
|
||||
end
|
||||
end
|
||||
return initialEncode
|
||||
end
|
||||
|
||||
-- CONSTRUCT STATE WITH FOLLOWING (at least)
|
||||
--[[
|
||||
encoder
|
||||
check_unique -- used by inner encoders to make sure value is unique
|
||||
already_encoded -- used to unmark a value as unique
|
||||
]]
|
||||
function json_encode.encode(data, options)
|
||||
return json_encode.getEncoder(options)(data)
|
||||
end
|
||||
|
||||
local mt = {}
|
||||
mt.__call = function(self, ...)
|
||||
return json_encode.encode(...)
|
||||
end
|
||||
|
||||
setmetatable(json_encode, mt)
|
||||
|
||||
return json_encode
|
110
tools/luajson/json/encode/array.lua
Normal file
110
tools/luajson/json/encode/array.lua
Normal file
@ -0,0 +1,110 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local jsonutil = require("json.util")
|
||||
|
||||
local type = type
|
||||
local pairs = pairs
|
||||
local assert = assert
|
||||
|
||||
local table = require("table")
|
||||
local math = require("math")
|
||||
local table_concat = table.concat
|
||||
local math_floor, math_modf = math.floor, math.modf
|
||||
|
||||
local jsonutil = require("json.util")
|
||||
local util_IsArray = jsonutil.IsArray
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local defaultOptions = {
|
||||
isArray = util_IsArray
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'array', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
--[[
|
||||
Utility function to determine whether a table is an array or not.
|
||||
Criteria for it being an array:
|
||||
* ExternalIsArray returns true (or false directly reports not-array)
|
||||
* If the table has an 'n' value that is an integer >= 1 then it
|
||||
is an array... may result in false positives (should check some values
|
||||
before it)
|
||||
* It is a contiguous list of values with zero string-based keys
|
||||
]]
|
||||
local function isArray(val, options)
|
||||
local externalIsArray = options and options.isArray
|
||||
|
||||
if externalIsArray then
|
||||
local ret = externalIsArray(val)
|
||||
if ret == true or ret == false then
|
||||
return ret
|
||||
end
|
||||
end
|
||||
-- Use the 'n' element if it's a number
|
||||
if type(val.n) == 'number' and math_floor(val.n) == val.n and val.n >= 1 then
|
||||
return true
|
||||
end
|
||||
local len = #val
|
||||
for k,v in pairs(val) do
|
||||
if type(k) ~= 'number' then
|
||||
return false
|
||||
end
|
||||
local _, decim = math_modf(k)
|
||||
if not (decim == 0 and 1<=k) then
|
||||
return false
|
||||
end
|
||||
if k > len then -- Use Lua's length as absolute determiner
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
Cleanup function to unmark a value as in the encoding process and return
|
||||
trailing results
|
||||
]]
|
||||
local function unmarkAfterEncode(tab, state, ...)
|
||||
state.already_encoded[tab] = nil
|
||||
return ...
|
||||
end
|
||||
local function getEncoder(options)
|
||||
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
||||
local function encodeArray(tab, state)
|
||||
if not isArray(tab, options) then
|
||||
return false
|
||||
end
|
||||
-- Make sure this value hasn't been encoded yet
|
||||
state.check_unique(tab)
|
||||
local encode = state.encode
|
||||
local compositeEncoder = state.outputEncoder.composite
|
||||
local valueEncoder = [[
|
||||
for i = 1, (composite.n or #composite) do
|
||||
local val = composite[i]
|
||||
PUTINNER(i ~= 1)
|
||||
val = encode(val, state)
|
||||
val = val or ''
|
||||
if val then
|
||||
PUTVALUE(val)
|
||||
end
|
||||
end
|
||||
]]
|
||||
return unmarkAfterEncode(tab, state, compositeEncoder(valueEncoder, '[', ']', ',', tab, encode, state))
|
||||
end
|
||||
return { table = encodeArray }
|
||||
end
|
||||
|
||||
local array = {
|
||||
mergeOptions = mergeOptions,
|
||||
isArray = isArray,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return array
|
68
tools/luajson/json/encode/calls.lua
Normal file
68
tools/luajson/json/encode/calls.lua
Normal file
@ -0,0 +1,68 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local table = require("table")
|
||||
local table_concat = table.concat
|
||||
|
||||
local select = select
|
||||
local getmetatable, setmetatable = getmetatable, setmetatable
|
||||
local assert = assert
|
||||
|
||||
local jsonutil = require("json.util")
|
||||
|
||||
local isCall, decodeCall = jsonutil.isCall, jsonutil.decodeCall
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local defaultOptions = {
|
||||
}
|
||||
|
||||
-- No real default-option handling needed...
|
||||
local modeOptions = {}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'calls', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
Encodes 'value' as a function call
|
||||
Must have parameters in the 'callData' field of the metatable
|
||||
name == name of the function call
|
||||
parameters == array of parameters to encode
|
||||
]]
|
||||
local function getEncoder(options)
|
||||
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
||||
local function encodeCall(value, state)
|
||||
if not isCall(value) then
|
||||
return false
|
||||
end
|
||||
local encode = state.encode
|
||||
local name, params = decodeCall(value)
|
||||
local compositeEncoder = state.outputEncoder.composite
|
||||
local valueEncoder = [[
|
||||
for i = 1, (composite.n or #composite) do
|
||||
local val = composite[i]
|
||||
PUTINNER(i ~= 1)
|
||||
val = encode(val, state)
|
||||
val = val or ''
|
||||
if val then
|
||||
PUTVALUE(val)
|
||||
end
|
||||
end
|
||||
]]
|
||||
return compositeEncoder(valueEncoder, name .. '(', ')', ',', params, encode, state)
|
||||
end
|
||||
return {
|
||||
table = encodeCall,
|
||||
['function'] = encodeCall
|
||||
}
|
||||
end
|
||||
|
||||
local calls = {
|
||||
mergeOptions = mergeOptions,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return calls
|
58
tools/luajson/json/encode/number.lua
Normal file
58
tools/luajson/json/encode/number.lua
Normal file
@ -0,0 +1,58 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local tostring = tostring
|
||||
local assert = assert
|
||||
local jsonutil = require("json.util")
|
||||
local huge = require("math").huge
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local defaultOptions = {
|
||||
nan = true,
|
||||
inf = true
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
modeOptions.strict = {
|
||||
nan = false,
|
||||
inf = false
|
||||
}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'number', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
|
||||
local function encodeNumber(number, options)
|
||||
if number ~= number then
|
||||
assert(options.nan, "Invalid number: NaN not enabled")
|
||||
return "NaN"
|
||||
end
|
||||
if number == huge then
|
||||
assert(options.inf, "Invalid number: Infinity not enabled")
|
||||
return "Infinity"
|
||||
end
|
||||
if number == -huge then
|
||||
assert(options.inf, "Invalid number: Infinity not enabled")
|
||||
return "-Infinity"
|
||||
end
|
||||
return tostring(number)
|
||||
end
|
||||
|
||||
local function getEncoder(options)
|
||||
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
||||
return {
|
||||
number = function(number, state)
|
||||
return encodeNumber(number, options)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
local number = {
|
||||
mergeOptions = mergeOptions,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return number
|
77
tools/luajson/json/encode/object.lua
Normal file
77
tools/luajson/json/encode/object.lua
Normal file
@ -0,0 +1,77 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local pairs = pairs
|
||||
local assert = assert
|
||||
|
||||
local type = type
|
||||
local tostring = tostring
|
||||
|
||||
local table_concat = require("table").concat
|
||||
local jsonutil = require("json.util")
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local defaultOptions = {
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'object', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
--[[
|
||||
Cleanup function to unmark a value as in the encoding process and return
|
||||
trailing results
|
||||
]]
|
||||
local function unmarkAfterEncode(tab, state, ...)
|
||||
state.already_encoded[tab] = nil
|
||||
return ...
|
||||
end
|
||||
--[[
|
||||
Encode a table as a JSON Object ( keys = strings, values = anything else )
|
||||
]]
|
||||
local function encodeTable(tab, options, state)
|
||||
-- Make sure this value hasn't been encoded yet
|
||||
state.check_unique(tab)
|
||||
local encode = state.encode
|
||||
local compositeEncoder = state.outputEncoder.composite
|
||||
local valueEncoder = [[
|
||||
local first = true
|
||||
for k, v in pairs(composite) do
|
||||
local ti = type(k)
|
||||
assert(ti == 'string' or ti == 'number' or ti == 'boolean', "Invalid object index type: " .. ti)
|
||||
local name = encode(tostring(k), state, true)
|
||||
if first then
|
||||
first = false
|
||||
else
|
||||
name = ',' .. name
|
||||
end
|
||||
PUTVALUE(name .. ':')
|
||||
local val = encode(v, state)
|
||||
val = val or ''
|
||||
if val then
|
||||
PUTVALUE(val)
|
||||
end
|
||||
end
|
||||
]]
|
||||
return unmarkAfterEncode(tab, state, compositeEncoder(valueEncoder, '{', '}', nil, tab, encode, state))
|
||||
end
|
||||
|
||||
local function getEncoder(options)
|
||||
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
||||
return {
|
||||
table = function(tab, state)
|
||||
return encodeTable(tab, options, state)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
local object = {
|
||||
mergeOptions = mergeOptions,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return object
|
66
tools/luajson/json/encode/others.lua
Normal file
66
tools/luajson/json/encode/others.lua
Normal file
@ -0,0 +1,66 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local tostring = tostring
|
||||
|
||||
local assert = assert
|
||||
local jsonutil = require("json.util")
|
||||
local type = type
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
-- Shortcut that works
|
||||
local encodeBoolean = tostring
|
||||
|
||||
local defaultOptions = {
|
||||
allowUndefined = true,
|
||||
null = jsonutil.null,
|
||||
undefined = jsonutil.undefined
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
modeOptions.strict = {
|
||||
allowUndefined = false
|
||||
}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'others', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
local function getEncoder(options)
|
||||
options = options and jsonutil.merge({}, defaultOptions, options) or defaultOptions
|
||||
local function encodeOthers(value, state)
|
||||
if value == options.null then
|
||||
return 'null'
|
||||
elseif value == options.undefined then
|
||||
assert(options.allowUndefined, "Invalid value: Unsupported 'Undefined' parameter")
|
||||
return 'undefined'
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
local function encodeBoolean(value, state)
|
||||
return value and 'true' or 'false'
|
||||
end
|
||||
local nullType = type(options.null)
|
||||
local undefinedType = options.undefined and type(options.undefined)
|
||||
-- Make sure that all of the types handled here are handled
|
||||
local ret = {
|
||||
boolean = encodeBoolean,
|
||||
['nil'] = function() return 'null' end,
|
||||
[nullType] = encodeOthers
|
||||
}
|
||||
if undefinedType then
|
||||
ret[undefinedType] = encodeOthers
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local others = {
|
||||
encodeBoolean = encodeBoolean,
|
||||
mergeOptions = mergeOptions,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return others
|
91
tools/luajson/json/encode/output.lua
Normal file
91
tools/luajson/json/encode/output.lua
Normal file
@ -0,0 +1,91 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local type = type
|
||||
local assert, error = assert, error
|
||||
local table_concat = require("table").concat
|
||||
local loadstring = loadstring or load
|
||||
|
||||
local io = require("io")
|
||||
|
||||
local setmetatable = setmetatable
|
||||
|
||||
local output_utility = require("json.encode.output_utility")
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local tableCompositeCache = setmetatable({}, {__mode = 'v'})
|
||||
|
||||
local TABLE_VALUE_WRITER = [[
|
||||
ret[#ret + 1] = %VALUE%
|
||||
]]
|
||||
|
||||
local TABLE_INNER_WRITER = ""
|
||||
|
||||
--[[
|
||||
nextValues can output a max of two values to throw into the data stream
|
||||
expected to be called until nil is first return value
|
||||
value separator should either be attached to v1 or in innerValue
|
||||
]]
|
||||
local function defaultTableCompositeWriter(nextValues, beginValue, closeValue, innerValue, composite, encode, state)
|
||||
if type(nextValues) == 'string' then
|
||||
local fun = output_utility.prepareEncoder(defaultTableCompositeWriter, nextValues, innerValue, TABLE_VALUE_WRITER, TABLE_INNER_WRITER)
|
||||
local ret = {}
|
||||
fun(composite, ret, encode, state)
|
||||
return beginValue .. table_concat(ret, innerValue) .. closeValue
|
||||
end
|
||||
end
|
||||
|
||||
-- no 'simple' as default action is just to return the value
|
||||
local function getDefault()
|
||||
return { composite = defaultTableCompositeWriter }
|
||||
end
|
||||
|
||||
-- BEGIN IO-WRITER OUTPUT
|
||||
local IO_INNER_WRITER = [[
|
||||
if %WRITE_INNER% then
|
||||
state.__outputFile:write(%INNER_VALUE%)
|
||||
end
|
||||
]]
|
||||
local IO_VALUE_WRITER = [[
|
||||
state.__outputFile:write(%VALUE%)
|
||||
]]
|
||||
|
||||
local function buildIoWriter(output)
|
||||
if not output then -- Default to stdout
|
||||
output = io.output()
|
||||
end
|
||||
local function ioWriter(nextValues, beginValue, closeValue, innerValue, composite, encode, state)
|
||||
-- HOOK OUTPUT STATE
|
||||
state.__outputFile = output
|
||||
if type(nextValues) == 'string' then
|
||||
local fun = output_utility.prepareEncoder(ioWriter, nextValues, innerValue, IO_VALUE_WRITER, IO_INNER_WRITER)
|
||||
local ret = {}
|
||||
output:write(beginValue)
|
||||
fun(composite, ret, encode, state)
|
||||
output:write(closeValue)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function ioSimpleWriter(encoded)
|
||||
if encoded then
|
||||
output:write(encoded)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
return { composite = ioWriter, simple = ioSimpleWriter }
|
||||
end
|
||||
local function getIoWriter(output)
|
||||
return function()
|
||||
return buildIoWriter(output)
|
||||
end
|
||||
end
|
||||
|
||||
local output = {
|
||||
getDefault = getDefault,
|
||||
getIoWriter = getIoWriter
|
||||
}
|
||||
|
||||
return output
|
54
tools/luajson/json/encode/output_utility.lua
Normal file
54
tools/luajson/json/encode/output_utility.lua
Normal file
@ -0,0 +1,54 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local setmetatable = setmetatable
|
||||
local assert, loadstring = assert, loadstring or load
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
-- Key == weak, if main key goes away, then cache cleared
|
||||
local outputCache = setmetatable({}, {__mode = 'k'})
|
||||
-- TODO: inner tables weak?
|
||||
|
||||
local function buildFunction(nextValues, innerValue, valueWriter, innerWriter)
|
||||
local putInner = ""
|
||||
if innerValue and innerWriter then
|
||||
-- Prepare the lua-string representation of the separator to put in between values
|
||||
local formattedInnerValue = ("%q"):format(innerValue)
|
||||
-- Fill in the condition %WRITE_INNER% and the %INNER_VALUE% to actually write
|
||||
putInner = innerWriter:gsub("%%WRITE_INNER%%", "%%1"):gsub("%%INNER_VALUE%%", formattedInnerValue)
|
||||
end
|
||||
-- Template-in the value writer (if present) and its conditional argument
|
||||
local functionCode = nextValues:gsub("PUTINNER(%b())", putInner)
|
||||
-- %VALUE% is to be filled in by the value-to-write
|
||||
valueWriter = valueWriter:gsub("%%VALUE%%", "%%1")
|
||||
-- Template-in the value writer with its argument
|
||||
functionCode = functionCode:gsub("PUTVALUE(%b())", valueWriter)
|
||||
functionCode = [[
|
||||
return function(composite, ret, encode, state)
|
||||
]] .. functionCode .. [[
|
||||
end
|
||||
]]
|
||||
return assert(loadstring(functionCode))()
|
||||
end
|
||||
|
||||
local function prepareEncoder(cacheKey, nextValues, innerValue, valueWriter, innerWriter)
|
||||
local cache = outputCache[cacheKey]
|
||||
if not cache then
|
||||
cache = {}
|
||||
outputCache[cacheKey] = cache
|
||||
end
|
||||
local fun = cache[nextValues]
|
||||
if not fun then
|
||||
fun = buildFunction(nextValues, innerValue, valueWriter, innerWriter)
|
||||
cache[nextValues] = fun
|
||||
end
|
||||
return fun
|
||||
end
|
||||
|
||||
local output_utility = {
|
||||
prepareEncoder = prepareEncoder
|
||||
}
|
||||
|
||||
return output_utility
|
88
tools/luajson/json/encode/strings.lua
Normal file
88
tools/luajson/json/encode/strings.lua
Normal file
@ -0,0 +1,88 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local string_char = require("string").char
|
||||
local pairs = pairs
|
||||
|
||||
local jsonutil = require("json.util")
|
||||
local util_merge = jsonutil.merge
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local normalEncodingMap = {
|
||||
['"'] = '\\"',
|
||||
['\\'] = '\\\\',
|
||||
['/'] = '\\/',
|
||||
['\b'] = '\\b',
|
||||
['\f'] = '\\f',
|
||||
['\n'] = '\\n',
|
||||
['\r'] = '\\r',
|
||||
['\t'] = '\\t',
|
||||
['\v'] = '\\v' -- not in official spec, on report, removing
|
||||
}
|
||||
|
||||
local xEncodingMap = {}
|
||||
for char, encoded in pairs(normalEncodingMap) do
|
||||
xEncodingMap[char] = encoded
|
||||
end
|
||||
|
||||
-- Pre-encode the control characters to speed up encoding...
|
||||
-- NOTE: UTF-8 may not work out right w/ JavaScript
|
||||
-- JavaScript uses 2 bytes after a \u... yet UTF-8 is a
|
||||
-- byte-stream encoding, not pairs of bytes (it does encode
|
||||
-- some letters > 1 byte, but base case is 1)
|
||||
for i = 0, 255 do
|
||||
local c = string_char(i)
|
||||
if c:match('[%z\1-\031\128-\255]') and not normalEncodingMap[c] then
|
||||
-- WARN: UTF8 specializes values >= 0x80 as parts of sequences...
|
||||
-- without \x encoding, do not allow encoding > 7F
|
||||
normalEncodingMap[c] = ('\\u%.4X'):format(i)
|
||||
xEncodingMap[c] = ('\\x%.2X'):format(i)
|
||||
end
|
||||
end
|
||||
|
||||
local defaultOptions = {
|
||||
xEncode = false, -- Encode single-bytes as \xXX
|
||||
processor = nil, -- Simple processor for the string prior to quoting
|
||||
-- / is not required to be quoted but it helps with certain decoding
|
||||
-- Required encoded characters, " \, and 00-1F (0 - 31)
|
||||
encodeSet = '\\"/%z\1-\031',
|
||||
encodeSetAppend = nil -- Chars to append to the default set
|
||||
}
|
||||
|
||||
local modeOptions = {}
|
||||
|
||||
local function mergeOptions(options, mode)
|
||||
jsonutil.doOptionMerge(options, false, 'strings', defaultOptions, mode and modeOptions[mode])
|
||||
end
|
||||
|
||||
local function getEncoder(options)
|
||||
options = options and util_merge({}, defaultOptions, options) or defaultOptions
|
||||
local encodeSet = options.encodeSet
|
||||
if options.encodeSetAppend then
|
||||
encodeSet = encodeSet .. options.encodeSetAppend
|
||||
end
|
||||
local encodingMap = options.xEncode and xEncodingMap or normalEncodingMap
|
||||
local encodeString
|
||||
if options.processor then
|
||||
local processor = options.processor
|
||||
encodeString = function(s, state)
|
||||
return '"' .. processor(s:gsub('[' .. encodeSet .. ']', encodingMap)) .. '"'
|
||||
end
|
||||
else
|
||||
encodeString = function(s, state)
|
||||
return '"' .. s:gsub('[' .. encodeSet .. ']', encodingMap) .. '"'
|
||||
end
|
||||
end
|
||||
return {
|
||||
string = encodeString
|
||||
}
|
||||
end
|
||||
|
||||
local strings = {
|
||||
mergeOptions = mergeOptions,
|
||||
getEncoder = getEncoder
|
||||
}
|
||||
|
||||
return strings
|
152
tools/luajson/json/util.lua
Normal file
152
tools/luajson/json/util.lua
Normal file
@ -0,0 +1,152 @@
|
||||
--[[
|
||||
Licensed according to the included 'LICENSE' document
|
||||
Author: Thomas Harning Jr <harningt@gmail.com>
|
||||
]]
|
||||
local type = type
|
||||
local print = print
|
||||
local tostring = tostring
|
||||
local pairs = pairs
|
||||
local getmetatable, setmetatable = getmetatable, setmetatable
|
||||
local select = select
|
||||
|
||||
local _ENV = nil
|
||||
|
||||
local function foreach(tab, func)
|
||||
for k, v in pairs(tab) do
|
||||
func(k,v)
|
||||
end
|
||||
end
|
||||
local function printValue(tab, name)
|
||||
local parsed = {}
|
||||
local function doPrint(key, value, space)
|
||||
space = space or ''
|
||||
if type(value) == 'table' then
|
||||
if parsed[value] then
|
||||
print(space .. key .. '= <' .. parsed[value] .. '>')
|
||||
else
|
||||
parsed[value] = key
|
||||
print(space .. key .. '= {')
|
||||
space = space .. ' '
|
||||
foreach(value, function(key, value) doPrint(key, value, space) end)
|
||||
end
|
||||
else
|
||||
if type(value) == 'string' then
|
||||
value = '[[' .. tostring(value) .. ']]'
|
||||
end
|
||||
print(space .. key .. '=' .. tostring(value))
|
||||
end
|
||||
end
|
||||
doPrint(name, tab)
|
||||
end
|
||||
|
||||
local function clone(t)
|
||||
local ret = {}
|
||||
for k,v in pairs(t) do
|
||||
ret[k] = v
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local function inner_merge(t, remaining, from, ...)
|
||||
if remaining == 0 then
|
||||
return t
|
||||
end
|
||||
if from then
|
||||
for k,v in pairs(from) do
|
||||
t[k] = v
|
||||
end
|
||||
end
|
||||
return inner_merge(t, remaining - 1, ...)
|
||||
end
|
||||
|
||||
--[[*
|
||||
Shallow-merges tables in order onto the first table.
|
||||
|
||||
@param t table to merge entries onto
|
||||
@param ... sequence of 0 or more tables to merge onto 't'
|
||||
|
||||
@returns table 't' from input
|
||||
]]
|
||||
local function merge(t, ...)
|
||||
return inner_merge(t, select('#', ...), ...)
|
||||
end
|
||||
|
||||
-- Function to insert nulls into the JSON stream
|
||||
local function null()
|
||||
return null
|
||||
end
|
||||
|
||||
-- Marker for 'undefined' values
|
||||
local function undefined()
|
||||
return undefined
|
||||
end
|
||||
|
||||
local ArrayMT = {}
|
||||
|
||||
--[[
|
||||
Return's true if the metatable marks it as an array..
|
||||
Or false if it has no array component at all
|
||||
Otherwise nil to get the normal detection component working
|
||||
]]
|
||||
local function IsArray(value)
|
||||
if type(value) ~= 'table' then return false end
|
||||
local meta = getmetatable(value)
|
||||
local ret = meta == ArrayMT or (meta ~= nil and meta.__is_luajson_array)
|
||||
if not ret then
|
||||
if #value == 0 then return false end
|
||||
else
|
||||
return ret
|
||||
end
|
||||
end
|
||||
local function InitArray(array)
|
||||
setmetatable(array, ArrayMT)
|
||||
return array
|
||||
end
|
||||
|
||||
local CallMT = {}
|
||||
|
||||
local function isCall(value)
|
||||
return CallMT == getmetatable(value)
|
||||
end
|
||||
|
||||
local function buildCall(name, ...)
|
||||
local callData = {
|
||||
name = name,
|
||||
parameters = {n = select('#', ...), ...}
|
||||
}
|
||||
return setmetatable(callData, CallMT)
|
||||
end
|
||||
|
||||
local function decodeCall(callData)
|
||||
if not isCall(callData) then return nil end
|
||||
return callData.name, callData.parameters
|
||||
end
|
||||
|
||||
local function doOptionMerge(options, nested, name, defaultOptions, modeOptions)
|
||||
if nested then
|
||||
modeOptions = modeOptions and modeOptions[name]
|
||||
defaultOptions = defaultOptions and defaultOptions[name]
|
||||
end
|
||||
options[name] = merge(
|
||||
{},
|
||||
defaultOptions,
|
||||
modeOptions,
|
||||
options[name]
|
||||
)
|
||||
end
|
||||
|
||||
local json_util = {
|
||||
printValue = printValue,
|
||||
clone = clone,
|
||||
merge = merge,
|
||||
null = null,
|
||||
undefined = undefined,
|
||||
IsArray = IsArray,
|
||||
InitArray = InitArray,
|
||||
isCall = isCall,
|
||||
buildCall = buildCall,
|
||||
decodeCall = decodeCall,
|
||||
doOptionMerge = doOptionMerge
|
||||
}
|
||||
|
||||
return json_util
|
95
tools/luajson/test_json.lua
Normal file
95
tools/luajson/test_json.lua
Normal file
@ -0,0 +1,95 @@
|
||||
package.path = package.path .. ";lua_scripts/libraries/luajson/?.lua"
|
||||
|
||||
local JSON = require"json"
|
||||
|
||||
local jsontest = [[{ 1:{"scn_ptz_id":"",
|
||||
"scn_ptz_prepos":"Preset 176",
|
||||
"scn_ptz_order":1,
|
||||
"scn_ptz_duration":"30",
|
||||
"scn_ptz_rally_delay":"2"}
|
||||
,
|
||||
2:{"scn_ptz_id":"","scn_ptz_prepos":"route","scn_ptz_order":2,"scn_ptz_duration":"30","scn_ptz_rally_delay":"2"} }
|
||||
]]
|
||||
local jsontest2 = [[{
|
||||
"extension":"mpg",
|
||||
"id":1545148451781,
|
||||
"name":"Foule_1280x720p.mpg",
|
||||
"size":67240746,
|
||||
"date":1545148451,
|
||||
"mime":"video\/mpeg",
|
||||
"filename":"1545148451781.mpg",
|
||||
"dir":"\/home\/pixalarm_data\/fileprocessor_data",
|
||||
"function_metadatas":
|
||||
{
|
||||
"function_faceblur":
|
||||
{
|
||||
"date":1545228627,
|
||||
"current_frame":"845",
|
||||
"polygons":[
|
||||
{
|
||||
"polygon_id":"new_1",
|
||||
"polygon_vertex":"[
|
||||
[0.14254859611231102,0.12476007677543186],[0.13174946004319654,0.4740882917466411],
|
||||
[0.3898488120950324,0.6621880998080614],[0.4038876889848812,0.11516314779270634]
|
||||
]",
|
||||
"polygon_frame_start":"1",
|
||||
"polygon_frame_stop":"300",
|
||||
"polygon_type":"full_blur"
|
||||
},
|
||||
{
|
||||
"polygon_id":"new_2",
|
||||
"polygon_vertex":"[
|
||||
[0.6198704103671706,0.1727447216890595],[0.5496760259179265,0.6007677543186181],
|
||||
[0.7775377969762419,0.7946257197696737],[0.9028077753779697,0.761996161228407],
|
||||
[0.9481641468682506,0.2821497120921305],[0.7829373650107991,0.04798464491362764]
|
||||
]",
|
||||
"polygon_frame_start":"200",
|
||||
"polygon_frame_stop":"845",
|
||||
"polygon_type":"no_blur"
|
||||
}
|
||||
],
|
||||
"framecuts":[
|
||||
["17","110"],
|
||||
["248","298"],
|
||||
["488","620"],
|
||||
["378","428"]
|
||||
],
|
||||
"face_selection":[
|
||||
{
|
||||
"frame":"21",
|
||||
"x":"0.5",
|
||||
"y":"0.356"
|
||||
},
|
||||
{
|
||||
"frame":"108",
|
||||
"x":"0.4289",
|
||||
"y":"0.275"
|
||||
},
|
||||
{
|
||||
"frame":"294",
|
||||
"x":"0.726",
|
||||
"y":"0.2364"
|
||||
}
|
||||
],
|
||||
"blur_type":"blur",
|
||||
"blur_area":"face"
|
||||
}
|
||||
},
|
||||
"total_frame":"845",
|
||||
"status":"DECODE_FINISHED",
|
||||
"fps":"25.00"
|
||||
}]]
|
||||
|
||||
local res = JSON.decode(jsontest2)
|
||||
for k, v in pairs(res) do
|
||||
print( k, v)
|
||||
end
|
||||
|
||||
res = JSON.decode( '{"content" : {},"date" : "2014-12-30T08:29:48Z","error" : {"code" : 0,"httpcode" : 200,"message" : ""},"status" : 1}' )
|
||||
for k, v in pairs(res) do
|
||||
print( k, v)
|
||||
end
|
||||
|
||||
local jsondata = JSON.encode( res )
|
||||
print(jsondata)
|
||||
|
Loading…
Reference in New Issue
Block a user