Compare commits
2 Commits
d7f3e03187
...
1816741ce6
Author | SHA1 | Date |
---|---|---|
|
1816741ce6 | 3 months ago |
|
35d7a07a9c | 3 months ago |
88 changed files with 139 additions and 14612 deletions
@ -1 +1 @@ |
||||
Subproject commit b16362d5ac9d252d09e4fb49ee8cc497c59d6f1d |
||||
Subproject commit 51181bb6fd45fc5fc7ad0046349d4597e8edbfbd |
@ -0,0 +1,137 @@ |
||||
# Determinisme |
||||
|
||||
Volgens mij is er geen verschil tussen natuurwetten en governing laws, of tussen mind en matter in zekere zin. |
||||
Alles wat gebeurt kan je in principe verklaren aan de hand van natuurwetten. Zo is bijvoorbeeld het feit |
||||
dat ik nu deze tekst schrijf een gevolg van een hele lange sequentie van evenementen die hiertoe geleid hebben. |
||||
Van het bestaan van het universum tot evolutie van de mens, mijn geboorte en hoe ik opgegroeid ben. Dit heeft geleid |
||||
tot de creatie van verbindingen in mijn brein, waardoor elektronen op zodanige manier vloeien dat de spieren in mijn |
||||
hand toetsen op mijn toetsenbord indrukken. Dit allemaal door het toepassen van natuurwetten. |
||||
|
||||
Maar dan vraag ik mij af waar dit begon. Er moet ergens een "seed" zijn, zodat wanneer we er natuurwetten |
||||
op toepassen we in de huidige toestand komen. Dit geeft aanleiding tot het idee dat er een zekere |
||||
willekeur in het universum moet zitten, de initiële "seed", en dan zou je kunnen concluderen dat het universum niet deterministisch |
||||
kan zijn en de huidige toestand niet noodzakelijk een louter deterministisch gevolg van het toepassen van natuurwetten is. |
||||
|
||||
Of misschien maken wij als mens de fout om te grijpen naar het concept van begin en einde. Misschien is het universum nooit |
||||
begonnen en was het er altijd al. Een idee dat moeilijk te vatten is voor de mens, maar ons eventueel wel toelaat |
||||
om het concept van niet-deteminisme te weigeren. Sinds er geen beginpunt is, kan de "seed" ook een gevolg zijn |
||||
van het toepassen van natuurwetten om de voorgaande toestand. Als tijd geen beginpunt heeft, kan men dan concluderen |
||||
dat voor elke toestand van het universum er een tijdstip is waarop het universum in die toestand is? |
||||
|
||||
# Tijd |
||||
|
||||
Ik heb een bizar idee van wat tijd is. Ik denk dat het universum op elk punt in de tijd splitst in (bijna) oneindig veel parallelle |
||||
universums. Dus wanneer ik bijvoorbeeld een glas water drink, is er ook een universum waarin ik dat niet doe. Wanneer ik sterf is er |
||||
ook een universum waarin ik niet sterf. Maar dit gebeurt op een oneindig klein niveau. Deze splitsing gebeurt niet op discrete tijdstippen, maar gebeurt continu. |
||||
Ook volgt volgens mij iedereen een andere tijdlijn door deze splitsende universums. Maar wat dicteert dan welke tijdlijn ik volg? Zijn er |
||||
oneindig veel verschillende versies van mijn die allemaal een eigen tijdlijn volgen. Als ik jou zie sterven, ben jij dan gestorven in jouw tijdlijn, |
||||
of enkel in mijn tijdlijn? Misschien leef jij gewoon verder en zie ik de versie van jij in mijn universum gewoon sterven? |
||||
Volgens deze redenering is het mogelijk dat ik zelf nooit zal sterven, of toch niet in de tijdlijn die ik volg, aangezien ik |
||||
mijzelf nog niet heb zien sterven in mijn tijdlijn. Ik heb andere mensen zien sterven in mijn tijdlijn, maar dat wil niet zeggen |
||||
dat zij zelf sterven in hun tijdlijn. |
||||
|
||||
Deze redenering geeft aanleiding tot een idealistische zienswijze waarin je een geest hebt die bepaalt welke tijdlijn je volgt. |
||||
|
||||
# Wetten |
||||
|
||||
Ik begrijp eigenlijk niet waarom er een expliciet onderscheid gemaakt wordt tussen natuurwetten en governing laws. |
||||
Natuurwetten zijn wetten, dingen die altijd waar zijn. Governing laws zijn geen wetten, maar gewoon uitspraken van |
||||
mensen, net zoals de uitspraak "ik vind de kleur blauw mooi". Ik begrijp niet wat de relevantie is van |
||||
deze vergelijking, deze twee concepten hebben gewoon niets met elkaar te maken. Het is alsof je de lengte van mijn |
||||
voet zou vergelijken met de kleur van de tafel. Er valt gewoon niets te vergelijken. |
||||
|
||||
# Church |
||||
|
||||
Ik vind materialistische zienswijze van Church voor de oorlog interessant: |
||||
|
||||
> For Church, mathematical entities were “fictions” and “part of an abstract structure constructed by us to enable us to understand reality” |
||||
|
||||
Ik vind het wel vreemd dat Church dan Turing interpreteert als mechanisch proces en niet als wiskundig proces. Ook |
||||
al beweert hij dat wiskunde fictief is. Ik zie geen reden waarom hij de theorie van Turing niet mathematisch zou kunnen |
||||
opvatten, als een abstracte structuur die ons toelaat de realiteit te begrijpen. |
||||
|
||||
# Mijn visie |
||||
|
||||
Ik heb het gevoel dat ik de hele tijd spring tussen verschillende zienswijzen, maar zal trachten een consistente zienswijze te formuleren. |
||||
|
||||
Ik denk dat er een correcte manier is om over het hele universum te redeneren. Een soort van "logica" die volledig het universum kan beschrijven |
||||
aan de hand van "natuurwetten". Ik ben er echter ook van overtuigd dat de huidige manier van redeneren ("formele systemen") en de huidige kennis |
||||
van "natuurwetten" niet voldoende is om het universum correct te beschrijven. In die zin is de wiskunde steeds slechts een benadering. |
||||
Op zoek gaan naar een volledige en correcte manier is zeer moeilijk omdat we dit mogelijks niet zullen kunnen vatten als mensen. |
||||
De huidige wiskunde is een benadering of abstractie van de realiteit die vatbaar is voor de mens en zal waarschijnlijk nooit perfect zijn. |
||||
Dit wil niet zeggen dat de wiskunde niet nuttig is om bepaalde dingen te bewijzen. Onder zekere veronderstelling zijn deze resultaten |
||||
correct, maar er zullen altijd veronderstellingen zijn. Dit zorgt voor een soort van "gap" tussen de abstract wiskunde/logica en de |
||||
fysische realiteit. In de praktische zin ben ik dus eerder een dualist, maar ik geloof wel dat het in theorie mogelijk is |
||||
om die "gap" te sluiten. Ik weet echter niet of dit mogelijk is met de huidige kennis en breincapaciteit van de mens. |
||||
|
||||
Ik begrijp waarom Wittgenstein het misleidend vind om wiskundige proposities te vergelijken met proposities uit de fysica, maar ik |
||||
vind persoonlijk wel dat het nuttig kan zijn om wiskundige resultaten proberen toe te passen in de fysische werkelijkheid, |
||||
ook al is dit niet correct. Het geeft ons een idee van de grootte van de "gap" tussen wiskunde en de fysische realiteit. |
||||
Dit kunnen we dan gebruiken om bepaalde wiskundige abstracties aan te passen of een nieuwe logica te vinden die beter |
||||
aansluit bij de realiteit. |
||||
|
||||
Ons puur baseren op fysische realiteit om resultaten te vinden kan leiden tot problemen zoals die theorie van Newton. Daar bleek |
||||
later dat deze theorie enkel geldig is onder bepaalde veronderstellingen. Einstein moest abstract kunnen denken in de wiskunde |
||||
om tot een algemener resultaat te komen. Relativiteitstheorie zou de mens nooit zo snel gevonden kunnen hebben als we ons enkel |
||||
zouden baseren op fysische waarnemingen. |
||||
|
||||
Ik ben het ergens ook wel eens met Wittgenstein dat wiskunde niets ontdekt en enkel maar dingen uit vindt. Maar ik vind die uitvindingen |
||||
persoonlijk wel nuttig om te kunnen redeneren over een abstractie van de realiteit. |
||||
|
||||
Anderzijds ben ik het deels eens met Turing en Russel: |
||||
|
||||
> But Turing was not to be persuaded. For him, as for Russell and for most professional mathematicians, the beauty of mathematics, its very ‘charm’, lay precisely in its power to provide, in an otherwise uncertain world, unassailable truths.” |
||||
|
||||
Wiskunde geeft ons zekere waarheden, maar ik denk dat als we deze waarheden willen toepassen op de realiteit we er ons van bewust moeten zijn |
||||
dat de wiskunde in de huidige toestand slechts een abstractie en benadering van de realiteit is. Ik geloof wel dat er een wiskunde of logica |
||||
bestaat die ons wel toelaat om deze waarheden door te trekken naar de fysische werkelijkheid, maar daarvoor moet de "gap" gesloten worden. |
||||
|
||||
Toch vind ik dat Wittgenstein dan wel weer een punt heeft. Je kan een brug ontwerpen op basis van "perfecte" calculus die toch instort doordat er een |
||||
vliegtuig op instort. In onze wiskunde hebben we bepaalde abstracties en veronderstellingen gemaakt, iets wat we moeten doen om het realistisch |
||||
te maken om de wiskunde te gebruiken. Een echt correcte en volledige wiskunde die echt waterdicht zou dit allemaal in rekening gebracht hebben, |
||||
maar zou zo moeilijk zijn om mee te werken voor ons mensen. |
||||
|
||||
Ik ben het dus eens met Russel in de zin dat er een wiskunde is waarvoor er een 1-op-1 mapping is met de fysische werkelijkheid. Onze "vereenvoudigde" |
||||
wiskunde is volgens mij gewoon niet die wiskunde. Maar de "echte" perfecte wiskunde is ondenkbaar complex en valt niet in te redeneren. |
||||
|
||||
Aangezien de huidige wiskunde slechts een benadering is van de realiteit moet je dus oppassen wanneer je het in de praktijk wil toepassen en |
||||
is het soms beter om "de ingenieur uit te hangen en wat te spelen met de fysische realiteit" in plaats van trachten te bewijzen wat er in de realiteit gebeurt. |
||||
Dat gezegd denk ik dat als je de juiste veronderstellingen maakt over de realiteit je onze beperkte wiskunde wel goed in de praktijk kan toepassen. |
||||
Bijvoorbeeld als je een perfecte brug ontwerpt aan de hand van de wiskunde, dan maak je direct enkele veronderstellingen, zoals de veronderstelling |
||||
dat er geen meteoor op de brug gaat vallen. |
||||
|
||||
Als dat dan toch gebeurt is dat niet omdat er iets mis is met de wiskunde, maar omdat je noodzakelijkerwijs |
||||
veronderstellingen hebt gemaakt om de wiskunde toe te kunnen passen. |
||||
|
||||
Mijn punt is dat onder de juiste veronderstellingen je de wiskunde wel kan gebruiken om te bepalen hoe je engineering gaat doen. |
||||
|
||||
Dit wordt dan wel gevaarlijk als je een computer gaat ontwerpen op basis van het model van een Turingmachine omdat je dan verondersteld |
||||
dat je oneindig veel geheugen hebt wat niet het geval is. Ook heb je abstractie gemaakt van allerlei concepten zoals snelheid en |
||||
enegieverbruik en productiekost. Die dingen kon je ook allemaal wiskundig formaliseren, maar als je dat allemaal tegelijk probeert te formaliseren wordt |
||||
het veel te moeilijk om de logisch perfecte computer te bepalen, vooral als je alles probeert terug te brengen naar natuurwetten en logische gevolgen daarvan. |
||||
Dus is het praktischer om de ingenieur uit te hangen en maar wat te proberen en wat af te wegen op basis van wat lijkt te werken in de realiteit. |
||||
|
||||
"that one and only logical system" waar Russel het over heeft zou volgens mij dan wel rekening houden met alle factoren en daar zou de perfecte |
||||
computer uit komen. Ik geloof dat zo een systeem bestaat, maar ik denk niet dat de mens dit ooit zal vinden en kunnen vatten. |
||||
Ik denk dus niet dat de calculus van Frege of Principia Mathematica van Russel dit perfecte logische systeem zijn. |
||||
|
||||
Wanneer Dijkstra bewijst dat een programma correct is maakt hij een hele hoop veronderstellingen over de omgeving waarin het programma wordt |
||||
uitgevoerd en de invoer die het programma krijgt. Maar in de realiteit is de invoer het hele universum. De correctheid van een programma |
||||
bewijzen met als invoer het hele universum is praktisch onmogelijk met de huidige formele systemen, maar dat zou misschien wel kunnen |
||||
met de perfecte wiskunde. Helaas kom je dan tot de conclusie dat geen enkel programma correct is aangezien er altijd een atoombom kan ontploffen |
||||
die de computer kapot maakt tijdens de uitvoering. Dat zou geen interessant resultaat zijn. De veronderstellingen die je maakt zijn |
||||
dus wel nodig om enige vorm van correctheid te bewijzen en dat lijkt mij op zich niet volledig nutteloos. |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,21 +0,0 @@ |
||||
/************* |
||||
Schur |
||||
*************/ |
||||
|
||||
vocabulary V { |
||||
type number isa int |
||||
type partition isa int |
||||
maxNumber : number |
||||
|
||||
inPartition(number, partition) |
||||
} |
||||
|
||||
theory T:V { |
||||
!n [number]: n < maxNumber. |
||||
} |
||||
|
||||
structure S:V { |
||||
maxNumber = 70 |
||||
} |
||||
|
||||
|
@ -1,18 +0,0 @@ |
||||
vocabulary V{ |
||||
type Color |
||||
type Area |
||||
Border(Area,Area) |
||||
Coloring(Area):Color |
||||
} |
||||
|
||||
theory T : V { |
||||
// Two adjacent countries can not have the same color |
||||
//TODO add this constraint |
||||
|
||||
!a,b: Border(a,b) => Coloring(a) ~= Coloring(b). |
||||
} |
||||
|
||||
include "coloring_draw.idp" |
||||
include "coloring_instance_1.idp" |
||||
include "coloring_oplossing.aidp" |
||||
include "idpd3/run.idp" |
@ -1,50 +0,0 @@ |
||||
include "idpd3/idpd3_voc.idp" |
||||
|
||||
vocabulary V_out { |
||||
extern vocabulary idpd3::V_out |
||||
extern vocabulary V |
||||
|
||||
type isLabel constructed from {t, f} |
||||
toKey(Area, isLabel) : key |
||||
toLink(Area, Area) : key |
||||
error(Area) |
||||
x(Area) : width |
||||
y(Area) : width |
||||
toLabel(Area) : label |
||||
color(Color) : color |
||||
} |
||||
|
||||
|
||||
theory T_D3 : V_out { |
||||
{ |
||||
error(g1) <- Coloring(g1) = k & Coloring(g2) = k & Border(g1, g2). |
||||
error(g1) <- Coloring(g1) = k & Coloring(g2) = k & Border(g2, g1). |
||||
} |
||||
{ |
||||
d3_type(1, k) = circ <- toKey(g, f) = k. |
||||
d3_circ_r(1, k) = 2 <- toKey(g, f) = k. |
||||
d3_x(1, k) = x <- false. |
||||
d3_y(1, k) = y <- false. |
||||
d3_color(1, k) = c <- toKey(g, f) = k & c = color(Coloring(g)). |
||||
d3_node(1, k) <- toKey(g, f) = k. |
||||
|
||||
d3_type(1, k) = link <- toLink(g1, g2) = k & Border(g1, g2). |
||||
d3_link_from(1, k) = k1 <- toLink(g1, g2) = k & Border(g1, g2) & toKey(g1, f) = k1. |
||||
d3_link_to(1, k) = k1 <- toLink(g1, g2) = k & Border(g1, g2) & toKey(g2, f) = k1. |
||||
d3_link_width(1, k) = 5 <- toLink(g1, g2) = k & Border(g1, g2). |
||||
d3_color(1, k) = "red" <- toLink(g1, g2) = k & Border(g1, g2) & Coloring(g1) = Coloring(g2). |
||||
d3_color(1, k) = "gray" <- toLink(g1, g2) = k & Border(g1, g2) & Coloring(g1) ~= Coloring(g2). |
||||
|
||||
/*d3_type(1, k) = text <- toKey(g, t) = k. |
||||
d3_label(1, k) = toLabel(g) <- toKey(g, t) = k. |
||||
d3_x(1, k) = 2 * y(g) + 1 <- toKey(g, t) = k. |
||||
d3_y(1, k) = 2 * x(g) <- toKey(g, t) = k. |
||||
d3_color(1, k) = "black" <- toKey(g, t) = k./**/ |
||||
d3_rect_height(1, x) = y <- false. |
||||
d3_rect_width(1, x) = y <- false. |
||||
d3_text_size(1, x) = y <- false. |
||||
} |
||||
|
||||
!t : ! x: d3_width(t) = x => ~?y : x<y. |
||||
!t : ! x: d3_height(t) = x => ~?y : x<y. |
||||
} |
@ -1,47 +0,0 @@ |
||||
procedure toKey(l, f) { |
||||
return l..tostring(f); |
||||
} |
||||
procedure toLink(g1, g2) { |
||||
return "g"..tostring(g1).."-"..tostring(g2); |
||||
} |
||||
procedure label(g) { |
||||
return g; |
||||
} |
||||
|
||||
structure S : V_out { |
||||
Color = { blue; orange; yellow; green } |
||||
|
||||
Area = { 1..12 } |
||||
|
||||
Border = { 1,2; 1,4; 1,5; |
||||
2,3; 2,4; |
||||
3,4; 3,7; 3,12; |
||||
4,5; 4,6; 4,7; |
||||
5,6; 5,8; |
||||
6,7; 6,8; 6,9; 6,10; |
||||
7,10; 7,11; 7,12; |
||||
8,9; |
||||
9,10; |
||||
10,11; |
||||
11,12;} |
||||
//idpd3 |
||||
time = {1} |
||||
width = {0..80} |
||||
height = {0..80} |
||||
color = {"white"; "red"; "orange"; "green"; "blue"; "yellow"; "black"; "gray"} |
||||
x = {1,1; 2,3; 3,5; 4,3; 5,1; 6,3; 7,5; 8,1; 9,3; 10,5; 11,7; 12,7} |
||||
y = {1,1; 2,0; 3,1; 4,2; 5,3; 6,4; 7,3; 8,5; 9,6; 10,5; 11,4; 12,2} |
||||
toKey = procedure toKey |
||||
toLink = procedure toLink |
||||
color = { |
||||
blue, "blue"; |
||||
orange, "orange"; |
||||
yellow, "yellow"; |
||||
green, "green" |
||||
} |
||||
toLabel = procedure label |
||||
} |
||||
|
||||
procedure getExpectedModels() { |
||||
return 3; |
||||
} |
@ -1,8 +0,0 @@ |
||||
theory T_exp : V { |
||||
// Two adjacent countries can not have the same color |
||||
!a1, a2:Border(a1,a2) | Border(a2,a1) => Coloring(a1) ~= Coloring(a2). |
||||
} |
||||
|
||||
procedure name(){ |
||||
return "Coloring" |
||||
} |
@ -1,921 +0,0 @@ |
||||
-- -*- coding: utf-8 -*- |
||||
-- |
||||
-- Simple JSON encoding and decoding in pure Lua. |
||||
-- |
||||
-- Copyright 2010-2014 Jeffrey Friedl |
||||
-- http://regex.info/blog/ |
||||
-- |
||||
-- Latest version: http://regex.info/blog/lua/json |
||||
-- |
||||
-- This code is released under a Creative Commons CC-BY "Attribution" License: |
||||
-- http://creativecommons.org/licenses/by/3.0/deed.en_US |
||||
-- |
||||
-- It can be used for any purpose so long as the copyright notice above, |
||||
-- the web-page links above, and the 'AUTHOR_NOTE' string below are |
||||
-- maintained. Enjoy. |
||||
-- |
||||
local VERSION = 20140920.13 -- version history at end of file |
||||
local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 20140920.13 ]-" |
||||
|
||||
-- |
||||
-- The 'AUTHOR_NOTE' variable exists so that information about the source |
||||
-- of the package is maintained even in compiled versions. It's included in |
||||
-- OBJDEF mostly to quiet warnings about unused variables. |
||||
-- |
||||
local OBJDEF = { |
||||
VERSION = VERSION, |
||||
AUTHOR_NOTE = AUTHOR_NOTE, |
||||
} |
||||
|
||||
|
||||
-- |
||||
-- Simple JSON encoding and decoding in pure Lua. |
||||
-- http://www.json.org/ |
||||
-- |
||||
-- |
||||
-- JSON = (loadfile "JSON.lua")() -- one-time load of the routines |
||||
-- |
||||
-- local lua_value = JSON:decode(raw_json_text) |
||||
-- |
||||
-- local raw_json_text = JSON:encode(lua_table_or_value) |
||||
-- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability |
||||
-- |
||||
-- |
||||
-- DECODING |
||||
-- |
||||
-- JSON = (loadfile "JSON.lua")() -- one-time load of the routines |
||||
-- |
||||
-- local lua_value = JSON:decode(raw_json_text) |
||||
-- |
||||
-- If the JSON text is for an object or an array, e.g. |
||||
-- { "what": "books", "count": 3 } |
||||
-- or |
||||
-- [ "Larry", "Curly", "Moe" ] |
||||
-- |
||||
-- the result is a Lua table, e.g. |
||||
-- { what = "books", count = 3 } |
||||
-- or |
||||
-- { "Larry", "Curly", "Moe" } |
||||
-- |
||||
-- |
||||
-- The encode and decode routines accept an optional second argument, "etc", which is not used |
||||
-- during encoding or decoding, but upon error is passed along to error handlers. It can be of any |
||||
-- type (including nil). |
||||
-- |
||||
-- With most errors during decoding, this code calls |
||||
-- |
||||
-- JSON:onDecodeError(message, text, location, etc) |
||||
-- |
||||
-- with a message about the error, and if known, the JSON text being parsed and the byte count |
||||
-- where the problem was discovered. You can replace the default JSON:onDecodeError() with your |
||||
-- own function. |
||||
-- |
||||
-- The default onDecodeError() merely augments the message with data about the text and the |
||||
-- location if known (and if a second 'etc' argument had been provided to decode(), its value is |
||||
-- tacked onto the message as well), and then calls JSON.assert(), which itself defaults to Lua's |
||||
-- built-in assert(), and can also be overridden. |
||||
-- |
||||
-- For example, in an Adobe Lightroom plugin, you might use something like |
||||
-- |
||||
-- function JSON:onDecodeError(message, text, location, etc) |
||||
-- LrErrors.throwUserError("Internal Error: invalid JSON data") |
||||
-- end |
||||
-- |
||||
-- or even just |
||||
-- |
||||
-- function JSON.assert(message) |
||||
-- LrErrors.throwUserError("Internal Error: " .. message) |
||||
-- end |
||||
-- |
||||
-- If JSON:decode() is passed a nil, this is called instead: |
||||
-- |
||||
-- JSON:onDecodeOfNilError(message, nil, nil, etc) |
||||
-- |
||||
-- and if JSON:decode() is passed HTML instead of JSON, this is called: |
||||
-- |
||||
-- JSON:onDecodeOfHTMLError(message, text, nil, etc) |
||||
-- |
||||
-- The use of the fourth 'etc' argument allows stronger coordination between decoding and error |
||||
-- reporting, especially when you provide your own error-handling routines. Continuing with the |
||||
-- the Adobe Lightroom plugin example: |
||||
-- |
||||
-- function JSON:onDecodeError(message, text, location, etc) |
||||
-- local note = "Internal Error: invalid JSON data" |
||||
-- if type(etc) = 'table' and etc.photo then |
||||
-- note = note .. " while processing for " .. etc.photo:getFormattedMetadata('fileName') |
||||
-- end |
||||
-- LrErrors.throwUserError(note) |
||||
-- end |
||||
-- |
||||
-- : |
||||
-- : |
||||
-- |
||||
-- for i, photo in ipairs(photosToProcess) do |
||||
-- : |
||||
-- : |
||||
-- local data = JSON:decode(someJsonText, { photo = photo }) |
||||
-- : |
||||
-- : |
||||
-- end |
||||
-- |
||||
-- |
||||
-- |
||||
-- |
||||
|
||||
-- DECODING AND STRICT TYPES |
||||
-- |
||||
-- Because both JSON objects and JSON arrays are converted to Lua tables, it's not normally |
||||
-- possible to tell which a JSON type a particular Lua table was derived from, or guarantee |
||||
-- decode-encode round-trip equivalency. |
||||
-- |
||||
-- However, if you enable strictTypes, e.g. |
||||
-- |
||||
-- JSON = (loadfile "JSON.lua")() --load the routines |
||||
-- JSON.strictTypes = true |
||||
-- |
||||
-- then the Lua table resulting from the decoding of a JSON object or JSON array is marked via Lua |
||||
-- metatable, so that when re-encoded with JSON:encode() it ends up as the appropriate JSON type. |
||||
-- |
||||
-- (This is not the default because other routines may not work well with tables that have a |
||||
-- metatable set, for example, Lightroom API calls.) |
||||
-- |
||||
-- |
||||
-- ENCODING |
||||
-- |
||||
-- JSON = (loadfile "JSON.lua")() -- one-time load of the routines |
||||
-- |
||||
-- local raw_json_text = JSON:encode(lua_table_or_value) |
||||
-- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability |
||||
|
||||
-- On error during encoding, this code calls: |
||||
-- |
||||
-- JSON:onEncodeError(message, etc) |
||||
-- |
||||
-- which you can override in your local JSON object. |
||||
-- |
||||
-- If the Lua table contains both string and numeric keys, it fits neither JSON's |
||||
-- idea of an object, nor its idea of an array. To get around this, when any string |
||||
-- key exists (or when non-positive numeric keys exist), numeric keys are converted |
||||
-- to strings. |
||||
-- |
||||
-- For example, |
||||
-- JSON:encode({ "one", "two", "three", SOMESTRING = "some string" })) |
||||
-- produces the JSON object |
||||
-- {"1":"one","2":"two","3":"three","SOMESTRING":"some string"} |
||||
-- |
||||
-- To prohibit this conversion and instead make it an error condition, set |
||||
-- JSON.noKeyConversion = true |
||||
|
||||
|
||||
-- |
||||
-- SUMMARY OF METHODS YOU CAN OVERRIDE IN YOUR LOCAL LUA JSON OBJECT |
||||
-- |
||||
-- assert |
||||
-- onDecodeError |
||||
-- onDecodeOfNilError |
||||
-- onDecodeOfHTMLError |
||||
-- onEncodeError |
||||
-- |
||||
-- If you want to create a separate Lua JSON object with its own error handlers, |
||||
-- you can reload JSON.lua or use the :new() method. |
||||
-- |
||||
--------------------------------------------------------------------------- |
||||
|
||||
local isArray = { __tostring = function() return "JSON array" end } isArray.__index = isArray |
||||
local isObject = { __tostring = function() return "JSON object" end } isObject.__index = isObject |
||||
|
||||
|
||||
function OBJDEF:newArray(tbl) |
||||
return setmetatable(tbl or {}, isArray) |
||||
end |
||||
|
||||
function OBJDEF:newObject(tbl) |
||||
return setmetatable(tbl or {}, isObject) |
||||
end |
||||
|
||||
local function unicode_codepoint_as_utf8(codepoint) |
||||
-- |
||||
-- codepoint is a number |
||||
-- |
||||
if codepoint <= 127 then |
||||
return string.char(codepoint) |
||||
|
||||
elseif codepoint <= 2047 then |
||||
-- |
||||
-- 110yyyxx 10xxxxxx <-- useful notation from http://en.wikipedia.org/wiki/Utf8 |
||||
-- |
||||
local highpart = math.floor(codepoint / 0x40) |
||||
local lowpart = codepoint - (0x40 * highpart) |
||||
return string.char(0xC0 + highpart, |
||||
0x80 + lowpart) |
||||
|
||||
elseif codepoint <= 65535 then |
||||
-- |
||||
-- 1110yyyy 10yyyyxx 10xxxxxx |
||||
-- |
||||
local highpart = math.floor(codepoint / 0x1000) |
||||
local remainder = codepoint - 0x1000 * highpart |
||||
local midpart = math.floor(remainder / 0x40) |
||||
local lowpart = remainder - 0x40 * midpart |
||||
|
||||
highpart = 0xE0 + highpart |
||||
midpart = 0x80 + midpart |
||||
lowpart = 0x80 + lowpart |
||||
|
||||
-- |
||||
-- Check for an invalid character (thanks Andy R. at Adobe). |
||||
-- See table 3.7, page 93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070 |
||||
-- |
||||
if ( highpart == 0xE0 and midpart < 0xA0 ) or |
||||
( highpart == 0xED and midpart > 0x9F ) or |
||||
( highpart == 0xF0 and midpart < 0x90 ) or |
||||
( highpart == 0xF4 and midpart > 0x8F ) |
||||
then |
||||
return "?" |
||||
else |
||||
return string.char(highpart, |
||||
midpart, |
||||
lowpart) |
||||
end |
||||
|
||||
else |
||||
-- |
||||
-- 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx |
||||
-- |
||||
local highpart = math.floor(codepoint / 0x40000) |
||||
local remainder = codepoint - 0x40000 * highpart |
||||
local midA = math.floor(remainder / 0x1000) |
||||
remainder = remainder - 0x1000 * midA |
||||
local midB = math.floor(remainder / 0x40) |
||||
local lowpart = remainder - 0x40 * midB |
||||
|
||||
return string.char(0xF0 + highpart, |
||||
0x80 + midA, |
||||
0x80 + midB, |
||||
0x80 + lowpart) |
||||
end |
||||
end |
||||
|
||||
function OBJDEF:onDecodeError(message, text, location, etc) |
||||
if text then |
||||
if location then |
||||
message = string.format("%s at char %d of: %s", message, location, text) |
||||
else |
||||
message = string.format("%s: %s", message, text) |
||||
end |
||||
end |
||||
|
||||
if etc ~= nil then |
||||
message = message .. " (" .. OBJDEF:encode(etc) .. ")" |
||||
end |
||||
|
||||
if self.assert then |
||||
self.assert(false, message) |
||||
else |
||||
assert(false, message) |
||||
end |
||||
end |
||||
|
||||
OBJDEF.onDecodeOfNilError = OBJDEF.onDecodeError |
||||
OBJDEF.onDecodeOfHTMLError = OBJDEF.onDecodeError |
||||
|
||||
function OBJDEF:onEncodeError(message, etc) |
||||
if etc ~= nil then |
||||
message = message .. " (" .. OBJDEF:encode(etc) .. ")" |
||||
end |
||||
|
||||
if self.assert then |
||||
self.assert(false, message) |
||||
else |
||||
assert(false, message) |
||||
end |
||||
end |
||||
|
||||
local function grok_number(self, text, start, etc) |
||||
-- |
||||
-- Grab the integer part |
||||
-- |
||||
local integer_part = text:match('^-?[1-9]%d*', start) |
||||
or text:match("^-?0", start) |
||||
|
||||
if not integer_part then |
||||
self:onDecodeError("expected number", text, start, etc) |
||||
end |
||||
|
||||
local i = start + integer_part:len() |
||||
|
||||
-- |
||||
-- Grab an optional decimal part |
||||
-- |
||||
local decimal_part = text:match('^%.%d+', i) or "" |
||||
|
||||
i = i + decimal_part:len() |
||||
|
||||
-- |
||||
-- Grab an optional exponential part |
||||
-- |
||||
local exponent_part = text:match('^[eE][-+]?%d+', i) or "" |
||||
|
||||
i = i + exponent_part:len() |
||||
|
||||
local full_number_text = integer_part .. decimal_part .. exponent_part |
||||
local as_number = tonumber(full_number_text) |
||||
|
||||
if not as_number then |
||||
self:onDecodeError("bad number", text, start, etc) |
||||
end |
||||
|
||||
return as_number, i |
||||
end |
||||
|
||||
|
||||
local function grok_string(self, text, start, etc) |
||||
|
||||
if text:sub(start,start) ~= '"' then |
||||
self:onDecodeError("expected string's opening quote", text, start, etc) |
||||
end |
||||
|
||||
local i = start + 1 -- +1 to bypass the initial quote |
||||
local text_len = text:len() |
||||
local VALUE = "" |
||||
while i <= text_len do |
||||
local c = text:sub(i,i) |
||||
if c == '"' then |
||||
return VALUE, i + 1 |
||||
end |
||||
if c ~= '\\' then |
||||
VALUE = VALUE .. c |
||||
i = i + 1 |
||||
elseif text:match('^\\b', i) then |
||||
VALUE = VALUE .. "\b" |
||||
i = i + 2 |
||||
elseif text:match('^\\f', i) then |
||||
VALUE = VALUE .. "\f" |
||||
i = i + 2 |
||||
elseif text:match('^\\n', i) then |
||||
VALUE = VALUE .. "\n" |
||||
i = i + 2 |
||||
elseif text:match('^\\r', i) then |
||||
VALUE = VALUE .. "\r" |
||||
i = i + 2 |
||||
elseif text:match('^\\t', i) then |
||||
VALUE = VALUE .. "\t" |
||||
i = i + 2 |
||||
else |
||||
local hex = text:match('^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) |
||||
if hex then |
||||
i = i + 6 -- bypass what we just read |
||||
|
||||
-- We have a Unicode codepoint. It could be standalone, or if in the proper range and |
||||
-- followed by another in a specific range, it'll be a two-code surrogate pair. |
||||
local codepoint = tonumber(hex, 16) |
||||
if codepoint >= 0xD800 and codepoint <= 0xDBFF then |
||||
-- it's a hi surrogate... see whether we have a following low |
||||
local lo_surrogate = text:match('^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) |
||||
if lo_surrogate then |
||||
i = i + 6 -- bypass the low surrogate we just read |
||||
codepoint = 0x2400 + (codepoint - 0xD800) * 0x400 + tonumber(lo_surrogate, 16) |
||||
else |
||||
-- not a proper low, so we'll just leave the first codepoint as is and spit it out. |
||||
end |
||||
end |
||||
VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint) |
||||
|
||||
else |
||||
|
||||
-- just pass through what's escaped |
||||
VALUE = VALUE .. text:match('^\\(.)', i) |
||||
i = i + 2 |
||||
end |
||||
end |
||||
end |
||||
|
||||
self:onDecodeError("unclosed string", text, start, etc) |
||||
end |
||||
|
||||
local function skip_whitespace(text, start) |
||||
|
||||
local _, match_end = text:find("^[ \n\r\t]+", start) -- [http://www.ietf.org/rfc/rfc4627.txt] Section 2 |
||||
if match_end then |
||||
return match_end + 1 |
||||
else |
||||
return start |
||||
end |
||||
end |
||||
|
||||
local grok_one -- assigned later |
||||
|
||||
local function grok_object(self, text, start, etc) |
||||
if text:sub(start,start) ~= '{' then |
||||
self:onDecodeError("expected '{'", text, start, etc) |
||||
end |
||||
|
||||
local i = skip_whitespace(text, start + 1) -- +1 to skip the '{' |
||||
|
||||
local VALUE = self.strictTypes and self:newObject { } or { } |
||||
|
||||
if text:sub(i,i) == '}' then |
||||
return VALUE, i + 1 |
||||
end |
||||
local text_len = text:len() |
||||
while i <= text_len do |
||||
local key, new_i = grok_string(self, text, i, etc) |
||||
|
||||
i = skip_whitespace(text, new_i) |
||||
|
||||
if text:sub(i, i) ~= ':' then |
||||
self:onDecodeError("expected colon", text, i, etc) |
||||
end |
||||
|
||||
i = skip_whitespace(text, i + 1) |
||||
|
||||
local new_val, new_i = grok_one(self, text, i) |
||||
|
||||
VALUE[key] = new_val |
||||
|
||||
-- |
||||
-- Expect now either '}' to end things, or a ',' to allow us to continue. |
||||
-- |
||||
i = skip_whitespace(text, new_i) |
||||
|
||||
local c = text:sub(i,i) |
||||
|
||||
if c == '}' then |
||||
return VALUE, i + 1 |
||||
end |
||||
|
||||
if text:sub(i, i) ~= ',' then |
||||
self:onDecodeError("expected comma or '}'", text, i, etc) |
||||
end |
||||
|
||||
i = skip_whitespace(text, i + 1) |
||||
end |
||||
|
||||
self:onDecodeError("unclosed '{'", text, start, etc) |
||||
end |
||||
|
||||
local function grok_array(self, text, start, etc) |
||||
if text:sub(start,start) ~= '[' then |
||||
self:onDecodeError("expected '['", text, start, etc) |
||||
end |
||||
|
||||
local i = skip_whitespace(text, start + 1) -- +1 to skip the '[' |
||||
local VALUE = self.strictTypes and self:newArray { } or { } |
||||
if text:sub(i,i) == ']' then |
||||
return VALUE, i + 1 |
||||
end |
||||
|
||||
local VALUE_INDEX = 1 |
||||
|
||||
local text_len = text:len() |
||||
while i <= text_len do |
||||
local val, new_i = grok_one(self, text, i) |
||||
|
||||
-- can't table.insert(VALUE, val) here because it's a no-op if val is nil |
||||
VALUE[VALUE_INDEX] = val |
||||
VALUE_INDEX = VALUE_INDEX + 1 |
||||
|
||||
i = skip_whitespace(text, new_i) |
||||
|
||||
-- |
||||
-- Expect now either ']' to end things, or a ',' to allow us to continue. |
||||
-- |
||||
local c = text:sub(i,i) |
||||
if c == ']' then |
||||
return VALUE, i + 1 |
||||
end |
||||
if text:sub(i, i) ~= ',' then |
||||
self:onDecodeError("expected comma or '['", text, i, etc) |
||||
end |
||||
i = skip_whitespace(text, i + 1) |
||||
end |
||||
self:onDecodeError("unclosed '['", text, start, etc) |
||||
end |
||||
|
||||
|
||||
grok_one = function(self, text, start, etc) |
||||
-- Skip any whitespace |
||||
start = skip_whitespace(text, start) |
||||
|
||||
if start > text:len() then |
||||
self:onDecodeError("unexpected end of string", text, nil, etc) |
||||
end |
||||
|
||||
if text:find('^"', start) then |
||||
return grok_string(self, text, start, etc) |
||||
|
||||
elseif text:find('^[-0123456789 ]', start) then |
||||
return grok_number(self, text, start, etc) |
||||
|
||||
elseif text:find('^%{', start) then |
||||
return grok_object(self, text, start, etc) |
||||
|
||||
elseif text:find('^%[', start) then |
||||
return grok_array(self, text, start, etc) |
||||
|
||||
elseif text:find('^true', start) then |
||||
return true, start + 4 |
||||
|
||||
elseif text:find('^false', start) then |
||||
return false, start + 5 |
||||
|
||||
elseif text:find('^null', start) then |
||||
return nil, start + 4 |
||||
|
||||
else |
||||
self:onDecodeError("can't parse JSON", text, start, etc) |
||||
end |
||||
end |
||||
|
||||
function OBJDEF:decode(text, etc) |
||||
if type(self) ~= 'table' or self.__index ~= OBJDEF then |
||||
OBJDEF:onDecodeError("JSON:decode must be called in method format", nil, nil, etc) |
||||
end |
||||
|
||||
if text == nil then |
||||
self:onDecodeOfNilError(string.format("nil passed to JSON:decode()"), nil, nil, etc) |
||||
elseif type(text) ~= 'string' then |
||||
self:onDecodeError(string.format("expected string argument to JSON:decode(), got %s", type(text)), nil, nil, etc) |
||||
end |
||||
|
||||
if text:match('^%s*$') then |
||||
return nil |
||||
end |
||||
|
||||
if text:match('^%s*<') then |
||||
-- Can't be JSON... we'll assume it's HTML |
||||
self:onDecodeOfHTMLError(string.format("html passed to JSON:decode()"), text, nil, etc) |
||||
end |
||||
|
||||
-- |
||||
-- Ensure that it's not UTF-32 or UTF-16. |
||||
-- Those are perfectly valid encodings for JSON (as per RFC 4627 section 3), |
||||
-- but this package can't handle them. |
||||
-- |
||||
if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then |
||||
self:onDecodeError("JSON package groks only UTF-8, sorry", text, nil, etc) |
||||
end |
||||
|
||||
local success, value = pcall(grok_one, self, text, 1, etc) |
||||
|
||||
if success then |
||||
return value |
||||
else |
||||
-- if JSON:onDecodeError() didn't abort out of the pcall, we'll have received the error message here as "value", so pass it along as an assert. |
||||
if self.assert then |
||||
self.assert(false, value) |
||||
else |
||||
assert(false, value) |
||||
end |
||||
-- and if we're still here, return a nil and throw the error message on as a second arg |
||||
return nil, value |
||||
end |
||||
end |
||||
|
||||
local function backslash_replacement_function(c) |
||||
if c == "\n" then |
||||
return "\\n" |
||||
elseif c == "\r" then |
||||
return "\\r" |
||||
elseif c == "\t" then |
||||
return "\\t" |
||||
elseif c == "\b" then |
||||
return "\\b" |
||||
elseif c == "\f" then |
||||
return "\\f" |
||||
elseif c == '"' then |
||||
return '\\"' |
||||
elseif c == '\\' then |
||||
return '\\\\' |
||||
else |
||||
return string.format("\\u%04x", c:byte()) |
||||
end |
||||
end |
||||
|
||||
local chars_to_be_escaped_in_JSON_string |
||||
= '[' |
||||
.. '"' -- class sub-pattern to match a double quote |
||||
.. '%\\' -- class sub-pattern to match a backslash |
||||
.. '%z' -- class sub-pattern to match a null |
||||
.. '\001' .. '-' .. '\031' -- class sub-pattern to match control characters |
||||
.. ']' |
||||
|
||||
local function json_string_literal(value) |
||||
local newval = value:gsub(chars_to_be_escaped_in_JSON_string, backslash_replacement_function) |
||||
return '"' .. newval .. '"' |
||||
end |
||||
|
||||
local function object_or_array(self, T, etc) |
||||
-- |
||||
-- We need to inspect all the keys... if there are any strings, we'll convert to a JSON |
||||
-- object. If there are only numbers, it's a JSON array. |
||||
-- |
||||
-- If we'll be converting to a JSON object, we'll want to sort the keys so that the |
||||
-- end result is deterministic. |
||||
-- |
||||
local string_keys = { } |
||||
local number_keys = { } |
||||
local number_keys_must_be_strings = false |
||||
local maximum_number_key |
||||
|
||||
for key in pairs(T) do |
||||
if type(key) == 'string' then |
||||
table.insert(string_keys, key) |
||||
elseif type(key) == 'number' then |
||||
table.insert(number_keys, key) |
||||
if key <= 0 or key >= math.huge then |
||||
number_keys_must_be_strings = true |
||||
elseif not maximum_number_key or key > maximum_number_key then |
||||
maximum_number_key = key |
||||
end |
||||
else |
||||
self:onEncodeError("can't encode table with a key of type " .. type(key), etc) |
||||
end |
||||
end |
||||
|
||||
if #string_keys == 0 and not number_keys_must_be_strings then |
||||
-- |
||||
-- An empty table, or a numeric-only array |
||||
-- |
||||
if #number_keys > 0 then |
||||
return nil, maximum_number_key -- an array |
||||
elseif tostring(T) == "JSON array" then |
||||
return nil |
||||
elseif tostring(T) == "JSON object" then |
||||
return { } |
||||
else |
||||
-- have to guess, so we'll pick array, since empty arrays are likely more common than empty objects |
||||
return nil |
||||
end |
||||
end |
||||
|
||||
table.sort(string_keys) |
||||
|
||||
local map |
||||
if #number_keys > 0 then |
||||
-- |
||||
-- If we're here then we have either mixed string/number keys, or numbers inappropriate for a JSON array |
||||
-- It's not ideal, but we'll turn the numbers into strings so that we can at least create a JSON object. |
||||
-- |
||||
|
||||
if self.noKeyConversion then |
||||
self:onEncodeError("a table with both numeric and string keys could be an object or array; aborting", etc) |
||||
end |
||||
|
||||
-- |
||||
-- Have to make a shallow copy of the source table so we can remap the numeric keys to be strings |
||||
-- |
||||
map = { } |
||||
for key, val in pairs(T) do |
||||
map[key] = val |
||||
end |
||||
|
||||
table.sort(number_keys) |
||||
|
||||
-- |
||||
-- Throw numeric keys in there as strings |
||||
-- |
||||
for _, number_key in ipairs(number_keys) do |
||||
local string_key = tostring(number_key) |
||||
if map[string_key] == nil then |
||||
table.insert(string_keys , string_key) |
||||
map[string_key] = T[number_key] |
||||
else |
||||
self:onEncodeError("conflict converting table with mixed-type keys into a JSON object: key " .. number_key .. " exists both as a string and a number.", etc) |
||||
end |
||||
end |
||||
end |
||||
|
||||
return string_keys, nil, map |
||||
end |
||||
|
||||
-- |
||||
-- Encode |
||||
-- |
||||
local encode_value -- must predeclare because it calls itself |
||||
function encode_value(self, value, parents, etc, indent) -- non-nil indent means pretty-printing |
||||
|
||||
if value == nil then |
||||
return 'null' |
||||
|
||||
elseif type(value) == 'string' then |
||||
return json_string_literal(value) |
||||
|
||||
elseif type(value) == 'number' then |
||||
if value ~= value then |
||||
-- |
||||
-- NaN (Not a Number). |
||||
-- JSON has no NaN, so we have to fudge the best we can. This should really be a package option. |
||||
-- |
||||
return "null" |
||||
elseif value >= math.huge then |
||||
-- |
||||
-- Positive infinity. JSON has no INF, so we have to fudge the best we can. This should |
||||
-- really be a package option. Note: at least with some implementations, positive infinity |
||||
-- is both ">= math.huge" and "<= -math.huge", which makes no sense but that's how it is. |
||||
-- Negative infinity is properly "<= -math.huge". So, we must be sure to check the ">=" |
||||
-- case first. |
||||
-- |
||||
return "1e+9999" |
||||
elseif value <= -math.huge then |
||||
-- |
||||
-- Negative infinity. |
||||
-- JSON has no INF, so we have to fudge the best we can. This should really be a package option. |
||||
-- |
||||
return "-1e+9999" |
||||
else |
||||
return tostring(value) |
||||
end |
||||
|
||||
elseif type(value) == 'boolean' then |
||||
return tostring(value) |
||||
|
||||
elseif type(value) ~= 'table' then |
||||
self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc) |
||||
|
||||
else |
||||
-- |
||||
-- A table to be converted to either a JSON object or array. |
||||
-- |
||||
local T = value |
||||
|
||||
if parents[T] then |
||||
self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc) |
||||
else |
||||
parents[T] = true |
||||
end |
||||
|
||||
local result_value |
||||
|
||||
local object_keys, maximum_number_key, map = object_or_array(self, T, etc) |
||||
if maximum_number_key then |
||||
-- |
||||
-- An array... |
||||
-- |
||||
local ITEMS = { } |
||||
for i = 1, maximum_number_key do |
||||
table.insert(ITEMS, encode_value(self, T[i], parents, etc, indent)) |
||||
end |
||||
|
||||
if indent then |
||||
result_value = "[ " .. table.concat(ITEMS, ", ") .. " ]" |
||||
else |
||||
result_value = "[" .. table.concat(ITEMS, ",") .. "]" |
||||
end |
||||
|
||||
elseif object_keys then |
||||
-- |
||||
-- An object |
||||
-- |
||||
local TT = map or T |
||||
|
||||
if indent then |
||||
|
||||
local KEYS = { } |
||||
local max_key_length = 0 |
||||
for _, key in ipairs(object_keys) do |
||||
local encoded = encode_value(self, tostring(key), parents, etc, "") |
||||
max_key_length = math.max(max_key_length, #encoded) |
||||
table.insert(KEYS, encoded) |
||||
end |
||||
local key_indent = indent .. " " |
||||
local subtable_indent = indent .. string.rep(" ", max_key_length + 2 + 4) |
||||
local FORMAT = "%s%" .. string.format("%d", max_key_length) .. "s: %s" |
||||
|
||||
local COMBINED_PARTS = { } |
||||
for i, key in ipairs(object_keys) do |
||||
local encoded_val = encode_value(self, TT[key], parents, etc, subtable_indent) |
||||
table.insert(COMBINED_PARTS, string.format(FORMAT, key_indent, KEYS[i], encoded_val)) |
||||
end |
||||
result_value = "{\n" .. table.concat(COMBINED_PARTS, ",\n") .. "\n" .. indent .. "}" |
||||
|
||||
else |
||||
|
||||
local PARTS = { } |
||||
for _, key in ipairs(object_keys) do |
||||
local encoded_val = encode_value(self, TT[key], parents, etc, indent) |
||||
local encoded_key = encode_value(self, tostring(key), parents, etc, indent) |
||||
table.insert(PARTS, string.format("%s:%s", encoded_key, encoded_val)) |
||||
end |
||||
result_value = "{" .. table.concat(PARTS, ",") .. "}" |
||||
|
||||
end |
||||
else |
||||
-- |
||||
-- An empty array/object... we'll treat it as an array, though it should really be an option |
||||
-- |
||||
result_value = "[]" |
||||
end |
||||
|
||||
parents[T] = false |
||||
return result_value |
||||
end |
||||
end |
||||
|
||||
|
||||
function OBJDEF:encode(value, etc) |
||||
if type(self) ~= 'table' or self.__index ~= OBJDEF then |
||||
OBJDEF:onEncodeError("JSON:encode must be called in method format", etc) |
||||
end |
||||
return encode_value(self, value, {}, etc, nil) |
||||
end |
||||
|
||||
function OBJDEF:encode_pretty(value, etc) |
||||
if type(self) ~= 'table' or self.__index ~= OBJDEF then |
||||
OBJDEF:onEncodeError("JSON:encode_pretty must be called in method format", etc) |
||||
end |
||||
return encode_value(self, value, {}, etc, "") |
||||
end |
||||
|
||||
function OBJDEF.__tostring() |
||||
return "JSON encode/decode package" |
||||
end |
||||
|
||||
OBJDEF.__index = OBJDEF |
||||
|
||||
function OBJDEF:new(args) |
||||
local new = { } |
||||
|
||||
if args then |
||||
for key, val in pairs(args) do |
||||
new[key] = val |
||||
end |
||||
end |
||||
|
||||
return setmetatable(new, OBJDEF) |
||||
end |
||||
|
||||
return OBJDEF:new() |
||||
|
||||
-- |
||||
-- Version history: |
||||
-- |
||||
-- 20140920.13 Put back (in a way that doesn't cause warnings about unused variables) the author string, |
||||
-- so that the source of the package, and its version number, are visible in compiled copies. |
||||
-- |
||||
-- 20140911.12 Minor lua cleanup. |
||||
-- Fixed internal reference to 'JSON.noKeyConversion' to reference 'self' instead of 'JSON'. |
||||
-- (Thanks to SmugMug's David Parry for these.) |
||||
-- |
||||
-- 20140418.11 JSON nulls embedded within an array were being ignored, such that |
||||
-- ["1",null,null,null,null,null,"seven"], |
||||
-- would return |
||||
-- {1,"seven"} |
||||
-- It's now fixed to properly return |
||||
-- {1, nil, nil, nil, nil, nil, "seven"} |
||||
-- Thanks to "haddock" for catching the error. |
||||
-- |
||||
-- 20140116.10 The user's JSON.assert() wasn't always being used. Thanks to "blue" for the heads up. |
||||
-- |
||||
-- 20131118.9 Update for Lua 5.3... it seems that tostring(2/1) produces "2.0" instead of "2", |
||||
-- and this caused some problems. |
||||
-- |
||||
-- 20131031.8 Unified the code for encode() and encode_pretty(); they had been stupidly separate, |
||||
-- and had of course diverged (encode_pretty didn't get the fixes that encode got, so |
||||
-- sometimes produced incorrect results; thanks to Mattie for the heads up). |
||||
-- |
||||
-- Handle encoding tables with non-positive numeric keys (unlikely, but possible). |
||||
-- |
||||
-- If a table has both numeric and string keys, or its numeric keys are inappropriate |
||||
-- (such as being non-positive or infinite), the numeric keys are turned into |
||||
-- string keys appropriate for a JSON object. So, as before, |
||||
-- JSON:encode({ "one", "two", "three" }) |
||||
-- produces the array |
||||
-- ["one","two","three"] |
||||
-- but now something with mixed key types like |
||||
-- JSON:encode({ "one", "two", "three", SOMESTRING = "some string" })) |
||||
-- instead of throwing an error produces an object: |
||||
-- {"1":"one","2":"two","3":"three","SOMESTRING":"some string"} |
||||
-- |
||||
-- To maintain the prior throw-an-error semantics, set |
||||
-- JSON.noKeyConversion = true |
||||
-- |
||||
-- 20131004.7 Release under a Creative Commons CC-BY license, which I should have done from day one, sorry. |
||||
-- |
||||
-- 20130120.6 Comment update: added a link to the specific page on my blog where this code can |
||||
-- be found, so that folks who come across the code outside of my blog can find updates |
||||
-- more easily. |
||||
-- |
||||
-- 20111207.5 Added support for the 'etc' arguments, for better error reporting. |
||||
-- |
||||
-- 20110731.4 More feedback from David Kolf on how to make the tests for Nan/Infinity system independent. |
||||
-- |
||||
-- 20110730.3 Incorporated feedback from David Kolf at http://lua-users.org/wiki/JsonModules: |
||||
-- |
||||
-- * When encoding lua for JSON, Sparse numeric arrays are now handled by |
||||
-- spitting out full arrays, such that |
||||
-- JSON:encode({"one", "two", [10] = "ten"}) |
||||
-- returns |
||||
-- ["one","two",null,null,null,null,null,null,null,"ten"] |
||||
-- |
||||
-- In 20100810.2 and earlier, only up to the first non-null value would have been retained. |
||||
-- |
||||
-- * When encoding lua for JSON, numeric value NaN gets spit out as null, and infinity as "1+e9999". |
||||
-- Version 20100810.2 and earlier created invalid JSON in both cases. |
||||
-- |
||||
-- * Unicode surrogate pairs are now detected when decoding JSON. |
||||
-- |
||||
-- 20100810.2 added some checking to ensure that an invalid Unicode character couldn't leak in to the UTF-8 encoding |
||||
-- |
||||
-- 20100731.1 initial public release |
||||
-- |
@ -1,715 +0,0 @@ |
||||
-- Module options: |
||||
local always_try_using_lpeg = false |
||||
local register_global_module_table = false |
||||
local global_module_name = 'json' |
||||
|
||||
--[==[ |
||||
|
||||
David Kolf's JSON module for Lua 5.1/5.2 |
||||
|
||||
Version 2.5 |
||||
|
||||
|
||||
For the documentation see the corresponding readme.txt or visit |
||||
<http://dkolf.de/src/dkjson-lua.fsl/>. |
||||
|
||||
You can contact the author by sending an e-mail to 'david' at the |
||||
domain 'dkolf.de'. |
||||
|
||||
|
||||
Copyright (C) 2010-2013 David Heiko Kolf |
||||
|
||||
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. |
||||
|
||||
--]==] |
||||
|
||||
-- global dependencies: |
||||
local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = |
||||
pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset |
||||
local error, require, pcall, select = error, require, pcall, select |
||||
local floor, huge = math.floor, math.huge |
||||
local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = |
||||
string.rep, string.gsub, string.sub, string.byte, string.char, |
||||
string.find, string.len, string.format |
||||
local strmatch = string.match |
||||
local concat = table.concat |
||||
|
||||
local json = { version = "dkjson 2.5" } |
||||
|
||||
if register_global_module_table then |
||||
_G[global_module_name] = json |
||||
end |
||||
|
||||
local _ENV = nil -- blocking globals in Lua 5.2 |
||||
|
||||
pcall (function() |
||||
-- Enable access to blocked metatables. |
||||
-- Don't worry, this module doesn't change anything in them. |
||||
local debmeta = require "debug".getmetatable |
||||
if debmeta then getmetatable = debmeta end |
||||
end) |
||||
|
||||
json.null = setmetatable ({}, { |
||||
__tojson = function () return "null" end |
||||
}) |
||||
|
||||
local function isarray (tbl) |
||||
local max, n, arraylen = 0, 0, 0 |
||||
for k,v in pairs (tbl) do |
||||
if k == 'n' and type(v) == 'number' then |
||||
arraylen = v |
||||
if v > max then |
||||
max = v |
||||
end |
||||
else |
||||
if type(k) ~= 'number' or k < 1 or floor(k) ~= k then |
||||
return false |
||||
end |
||||
if k > max then |
||||
max = k |
||||
end |
||||
n = n + 1 |
||||
end |
||||
end |
||||
if max > 10 and max > arraylen and max > n * 2 then |
||||
print(max.." "..arraylen.." "..n); |
||||
return false -- don't create an array with too many holes |
||||
end |
||||
return true, max |
||||
end |
||||
|
||||
local escapecodes = { |
||||
["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", |
||||
["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" |
||||
} |
||||
|
||||
local function escapeutf8 (uchar) |
||||
local value = escapecodes[uchar] |
||||
if value then |
||||
return value |
||||
end |
||||
local a, b, c, d = strbyte (uchar, 1, 4) |
||||
a, b, c, d = a or 0, b or 0, c or 0, d or 0 |
||||
if a <= 0x7f then |
||||
value = a |
||||
elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then |
||||
value = (a - 0xc0) * 0x40 + b - 0x80 |
||||
elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then |
||||
value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 |
||||
elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then |
||||
value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 |
||||
else |
||||
return "" |
||||
end |
||||
if value <= 0xffff then |
||||
return strformat ("\\u%.4x", value) |
||||
elseif value <= 0x10ffff then |
||||
-- encode as UTF-16 surrogate pair |
||||
value = value - 0x10000 |
||||
local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) |
||||
return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) |
||||
else |
||||
return "" |
||||
end |
||||
end |
||||
|
||||
local function fsub (str, pattern, repl) |
||||
-- gsub always builds a new string in a buffer, even when no match |
||||
-- exists. First using find should be more efficient when most strings |
||||
-- don't contain the pattern. |
||||
if strfind (str, pattern) then |
||||
return gsub (str, pattern, repl) |
||||
else |
||||
return str |
||||
end |
||||
end |
||||
|
||||
local function quotestring (value) |
||||
-- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js |
||||
value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) |
||||
if strfind (value, "[\194\216\220\225\226\239]") then |
||||
value = fsub (value, "\194[\128-\159\173]", escapeutf8) |
||||
value = fsub (value, "\216[\128-\132]", escapeutf8) |
||||
value = fsub (value, "\220\143", escapeutf8) |
||||
value = fsub (value, "\225\158[\180\181]", escapeutf8) |
||||
value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8) |
||||
value = fsub (value, "\226\129[\160-\175]", escapeutf8) |
||||
value = fsub (value, "\239\187\191", escapeutf8) |
||||
value = fsub (value, "\239\191[\176-\191]", escapeutf8) |
||||
end |
||||
return "\"" .. value .. "\"" |
||||
end |
||||
json.quotestring = quotestring |
||||
|
||||
local function replace(str, o, n) |
||||
local i, j = strfind (str, o, 1, true) |
||||
if i then |
||||
return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1) |
||||
else |
||||
return str |
||||
end |
||||
end |
||||
|
||||
-- locale independent num2str and str2num functions |
||||
local decpoint, numfilter |
||||
|
||||
local function updatedecpoint () |
||||
decpoint = strmatch(tostring(0.5), "([^05+]) |