1#!/usr/bin/env perl
2
3my $_fmt;
4$_fmt = "gofmt";
5$_fmt = "cat -n" if "cat" eq ($ARGV[0] || "");
6
7use strict;
8use warnings;
9use IO::File;
10
11my $self = __PACKAGE__;
12
13sub functionLabel ($) {
14    return "$_[0]_function";
15}
16
17sub trim ($) {
18    local $_ = shift;
19    s/^\s*//, s/\s*$// for $_;
20    return $_;
21}
22
23open my $fmt, "|-", "$_fmt" or die $!;
24
25$fmt->print(<<_END_);
26package otto
27
28import (
29    "math"
30)
31
32func _newContext(runtime *_runtime) {
33@{[ join "\n", $self->newContext() ]}
34}
35
36func newConsoleObject(runtime *_runtime) *_object {
37@{[ join "\n", $self->newConsoleObject() ]}
38}
39_END_
40
41for (qw/int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float32 float64/) {
42    $fmt->print(<<_END_);
43
44func toValue_$_(value $_) Value {
45    return Value{
46        kind: valueNumber,
47        value: value,
48    }
49}
50_END_
51}
52
53$fmt->print(<<_END_);
54
55func toValue_string(value string) Value {
56    return Value{
57        kind: valueString,
58        value: value,
59    }
60}
61
62func toValue_string16(value []uint16) Value {
63    return Value{
64        kind: valueString,
65        value: value,
66    }
67}
68
69func toValue_bool(value bool) Value {
70    return Value{
71        kind: valueBoolean,
72        value: value,
73    }
74}
75
76func toValue_object(value *_object) Value {
77    return Value{
78        kind: valueObject,
79        value: value,
80    }
81}
82_END_
83
84close $fmt;
85
86sub newConsoleObject {
87    my $self = shift;
88
89    return
90        $self->block(sub {
91            my $class = "Console";
92            my @got = $self->functionDeclare(
93                $class,
94                "log", 0,
95                "debug:log", 0,
96                "info:log", 0,
97                "error", 0,
98                "warn:error", 0,
99                "dir", 0,
100                "time", 0,
101                "timeEnd", 0,
102                "trace", 0,
103                "assert", 0,
104            );
105            return
106            "return @{[ $self->newObject(@got) ]}"
107        }),
108    ;
109}
110
111sub newContext {
112    my $self = shift;
113    return
114        # ObjectPrototype
115        $self->block(sub {
116            my $class = "Object";
117            return
118            ".${class}Prototype =",
119            $self->globalPrototype(
120                $class,
121                "_classObject",
122                undef,
123                "prototypeValueObject",
124            ),
125        }),
126
127        # FunctionPrototype
128        $self->block(sub {
129            my $class = "Function";
130            return
131            ".${class}Prototype =",
132            $self->globalPrototype(
133                $class,
134                "_classObject",
135                ".ObjectPrototype",
136                "prototypeValueFunction",
137            ),
138        }),
139
140        # ObjectPrototype
141        $self->block(sub {
142            my $class = "Object";
143            my @got = $self->functionDeclare(
144                $class,
145                "valueOf", 0,
146                "toString", 0,
147                "toLocaleString", 0,
148                "hasOwnProperty", 1,
149                "isPrototypeOf", 1,
150                "propertyIsEnumerable", 1,
151            );
152            my @propertyMap = $self->propertyMap(
153                @got,
154                $self->property("constructor", undef),
155            );
156            my $propertyOrder = $self->propertyOrder(@propertyMap);
157            $propertyOrder =~ s/^propertyOrder: //;
158            return
159            ".${class}Prototype.property =", @propertyMap,
160            ".${class}Prototype.propertyOrder =", $propertyOrder,
161        }),
162
163        # FunctionPrototype
164        $self->block(sub {
165            my $class = "Function";
166            my @got = $self->functionDeclare(
167                $class,
168                "toString", 0,
169                "apply", 2,
170                "call", 1,
171                "bind", 1,
172            );
173            my @propertyMap = $self->propertyMap(
174                @got,
175                $self->property("constructor", undef),
176                $self->property("length", $self->numberValue(0), "0"),
177            );
178            my $propertyOrder = $self->propertyOrder(@propertyMap);
179            $propertyOrder =~ s/^propertyOrder: //;
180            return
181            ".${class}Prototype.property =", @propertyMap,
182            ".${class}Prototype.propertyOrder =", $propertyOrder,
183        }),
184
185        # Object
186        $self->block(sub {
187            my $class = "Object";
188            return
189            ".$class =",
190            $self->globalFunction(
191                $class,
192                1,
193                $self->functionDeclare(
194                    $class,
195                    "getPrototypeOf", 1,
196                    "getOwnPropertyDescriptor", 2,
197                    "defineProperty", 3,
198                    "defineProperties", 2,
199                    "create", 2,
200                    "isExtensible", 1,
201                    "preventExtensions", 1,
202                    "isSealed", 1,
203                    "seal", 1,
204                    "isFrozen", 1,
205                    "freeze", 1,
206                    "keys", 1,
207                    "getOwnPropertyNames", 1,
208                ),
209            ),
210        }),
211
212        # Function
213        $self->block(sub {
214            my $class = "Function";
215            return
216            "Function :=",
217            $self->globalFunction(
218                $class,
219                1,
220            ),
221            ".$class = Function",
222        }),
223
224        # Array
225        $self->block(sub {
226            my $class = "Array";
227            my @got = $self->functionDeclare(
228                $class,
229                "toString", 0,
230                "toLocaleString", 0,
231                "concat", 1,
232                "join", 1,
233                "splice", 2,
234                "shift", 0,
235                "pop", 0,
236                "push", 1,
237                "slice", 2,
238                "unshift", 1,
239                "reverse", 0,
240                "sort", 1,
241                "indexOf", 1,
242                "lastIndexOf", 1,
243                "every", 1,
244                "some", 1,
245                "forEach", 1,
246                "map", 1,
247                "filter", 1,
248                "reduce", 1,
249                "reduceRight", 1,
250            );
251            return
252            ".${class}Prototype =",
253            $self->globalPrototype(
254                $class,
255                "_classArray",
256                ".ObjectPrototype",
257                undef,
258                $self->property("length", $self->numberValue("uint32(0)"), "0100"),
259                @got,
260            ),
261            ".$class =",
262            $self->globalFunction(
263                $class,
264                1,
265                $self->functionDeclare(
266                    $class,
267                    "isArray", 1,
268                ),
269            ),
270        }),
271
272        # String
273        $self->block(sub {
274            my $class = "String";
275            my @got = $self->functionDeclare(
276                $class,
277                "toString", 0,
278                "valueOf", 0,
279                "charAt", 1,
280                "charCodeAt", 1,
281                "concat", 1,
282                "indexOf", 1,
283                "lastIndexOf", 1,
284                "match", 1,
285                "replace", 2,
286                "search", 1,
287                "split", 2,
288                "slice", 2,
289                "substring", 2,
290                "toLowerCase", 0,
291                "toUpperCase", 0,
292                "substr", 2,
293                "trim", 0,
294                "trimLeft", 0,
295                "trimRight", 0,
296                "localeCompare", 1,
297                "toLocaleLowerCase", 0,
298                "toLocaleUpperCase", 0,
299            );
300            return
301            ".${class}Prototype =",
302            $self->globalPrototype(
303                $class,
304                "_classString",
305                ".ObjectPrototype",
306                "prototypeValueString",
307                $self->property("length", $self->numberValue("int(0)"), "0"),
308                @got,
309            ),
310            ".$class =",
311            $self->globalFunction(
312                $class,
313                1,
314                $self->functionDeclare(
315                    $class,
316		            "fromCharCode", 1,
317                ),
318            ),
319        }),
320
321        # Boolean
322        $self->block(sub {
323            my $class = "Boolean";
324            my @got = $self->functionDeclare(
325                $class,
326                "toString", 0,
327                "valueOf", 0,
328            );
329            return
330            ".${class}Prototype =",
331            $self->globalPrototype(
332                $class,
333                "_classObject",
334                ".ObjectPrototype",
335                "prototypeValueBoolean",
336                @got,
337            ),
338            ".$class =",
339            $self->globalFunction(
340                $class,
341                1,
342                $self->functionDeclare(
343                    $class,
344                ),
345            ),
346        }),
347
348        # Number
349        $self->block(sub {
350            my $class = "Number";
351            my @got = $self->functionDeclare(
352                $class,
353                "toString", 0,
354                "valueOf", 0,
355                "toFixed", 1,
356                "toExponential", 1,
357                "toPrecision", 1,
358                "toLocaleString", 1,
359            );
360            return
361            ".${class}Prototype =",
362            $self->globalPrototype(
363                $class,
364                "_classObject",
365                ".ObjectPrototype",
366                "prototypeValueNumber",
367                @got,
368            ),
369            ".$class =",
370            $self->globalFunction(
371                $class,
372                1,
373                $self->functionDeclare(
374                    $class,
375                ),
376                $self->numberConstantDeclare(
377                    "MAX_VALUE", "math.MaxFloat64",
378                    "MIN_VALUE", "math.SmallestNonzeroFloat64",
379                    "NaN", "math.NaN()",
380                    "NEGATIVE_INFINITY", "math.Inf(-1)",
381                    "POSITIVE_INFINITY", "math.Inf(+1)",
382                ),
383            ),
384        }),
385
386        # Math
387        $self->block(sub {
388            my $class = "Math";
389            return
390            ".$class =",
391            $self->globalObject(
392                $class,
393                $self->functionDeclare(
394                    $class,
395                    "abs", 1,
396                    "acos", 1,
397                    "asin", 1,
398                    "atan", 1,
399                    "atan2", 1,
400                    "ceil", 1,
401                    "cos", 1,
402                    "exp", 1,
403                    "floor", 1,
404                    "log", 1,
405                    "max", 2,
406                    "min", 2,
407                    "pow", 2,
408                    "random", 0,
409                    "round", 1,
410                    "sin", 1,
411                    "sqrt", 1,
412                    "tan", 1,
413                ),
414                $self->numberConstantDeclare(
415                    "E", "math.E",
416                    "LN10", "math.Ln10",
417                    "LN2", "math.Ln2",
418                    "LOG2E", "math.Log2E",
419                    "LOG10E", "math.Log10E",
420                    "PI", "math.Pi",
421                    "SQRT1_2", "sqrt1_2",
422                    "SQRT2", "math.Sqrt2",
423                )
424            ),
425        }),
426
427        # Date
428        $self->block(sub {
429            my $class = "Date";
430            my @got = $self->functionDeclare(
431                $class,
432                "toString", 0,
433                "toDateString", 0,
434                "toTimeString", 0,
435                "toUTCString", 0,
436                "toISOString", 0,
437                "toJSON", 1,
438                "toGMTString", 0,
439                "toLocaleString", 0,
440                "toLocaleDateString", 0,
441                "toLocaleTimeString", 0,
442                "valueOf", 0,
443                "getTime", 0,
444                "getYear", 0,
445                "getFullYear", 0,
446                "getUTCFullYear", 0,
447                "getMonth", 0,
448                "getUTCMonth", 0,
449                "getDate", 0,
450                "getUTCDate", 0,
451                "getDay", 0,
452                "getUTCDay", 0,
453                "getHours", 0,
454                "getUTCHours", 0,
455                "getMinutes", 0,
456                "getUTCMinutes", 0,
457                "getSeconds", 0,
458                "getUTCSeconds", 0,
459                "getMilliseconds", 0,
460                "getUTCMilliseconds", 0,
461                "getTimezoneOffset", 0,
462                "setTime", 1,
463                "setMilliseconds", 1,
464                "setUTCMilliseconds", 1,
465                "setSeconds", 2,
466                "setUTCSeconds", 2,
467                "setMinutes", 3,
468                "setUTCMinutes", 3,
469                "setHours", 4,
470                "setUTCHours", 4,
471                "setDate", 1,
472                "setUTCDate", 1,
473                "setMonth", 2,
474                "setUTCMonth", 2,
475                "setYear", 1,
476                "setFullYear", 3,
477                "setUTCFullYear", 3,
478            );
479            return
480            ".${class}Prototype =",
481            $self->globalPrototype(
482                $class,
483                "_classObject",
484                ".ObjectPrototype",
485                "prototypeValueDate",
486                @got,
487            ),
488            ".$class =",
489            $self->globalFunction(
490                $class,
491                7,
492                $self->functionDeclare(
493                    $class,
494                    "parse", 1,
495                    "UTC", 7,
496                    "now", 0,
497                ),
498            ),
499        }),
500
501        # RegExp
502        $self->block(sub {
503            my $class = "RegExp";
504            my @got = $self->functionDeclare(
505                $class,
506                "toString", 0,
507                "exec", 1,
508                "test", 1,
509                "compile", 1,
510            );
511            return
512            ".${class}Prototype =",
513            $self->globalPrototype(
514                $class,
515                "_classObject",
516                ".ObjectPrototype",
517                "prototypeValueRegExp",
518                @got,
519            ),
520            ".$class =",
521            $self->globalFunction(
522                $class,
523                2,
524                $self->functionDeclare(
525                    $class,
526                ),
527            ),
528        }),
529
530        # Error
531        $self->block(sub {
532            my $class = "Error";
533            my @got = $self->functionDeclare(
534                $class,
535                "toString", 0,
536            );
537            return
538            ".${class}Prototype =",
539            $self->globalPrototype(
540                $class,
541                "_classObject",
542                ".ObjectPrototype",
543                undef,
544                @got,
545                $self->property("name", $self->stringValue("Error")),
546                $self->property("message", $self->stringValue("")),
547            ),
548            ".$class =",
549            $self->globalFunction(
550                $class,
551                1,
552                $self->functionDeclare(
553                    $class,
554                ),
555            ),
556        }),
557
558        (map {
559            my $class = "${_}Error";
560            $self->block(sub {
561                my @got = $self->functionDeclare(
562                    $class,
563                );
564                return
565                ".${class}Prototype =",
566                $self->globalPrototype(
567                    $class,
568                    "_classObject",
569                    ".ErrorPrototype",
570                    undef,
571                    @got,
572                    $self->property("name", $self->stringValue($class)),
573                ),
574                ".$class =",
575                $self->globalFunction(
576                    $class,
577                    1,
578                    $self->functionDeclare(
579                        $class,
580                    ),
581                ),
582            });
583        } qw/Eval Type Range Reference Syntax URI/),
584
585        # JSON
586        $self->block(sub {
587            my $class = "JSON";
588            return
589            ".$class =",
590            $self->globalObject(
591                $class,
592                $self->functionDeclare(
593                    $class,
594                    "parse", 2,
595                    "stringify", 3,
596                ),
597            ),
598        }),
599
600        # Global
601        $self->block(sub {
602            my $class = "Global";
603            my @got = $self->functionDeclare(
604                $class,
605                "eval", 1,
606                "parseInt", 2,
607                "parseFloat", 1,
608                "isNaN", 1,
609                "isFinite", 1,
610                "decodeURI", 1,
611                "decodeURIComponent", 1,
612                "encodeURI", 1,
613                "encodeURIComponent", 1,
614                "escape", 1,
615                "unescape", 1,
616            );
617            my @propertyMap = $self->propertyMap(
618                @got,
619                $self->globalDeclare(
620                    "Object",
621                    "Function",
622                    "Array",
623                    "String",
624                    "Boolean",
625                    "Number",
626                    "Math",
627                    "Date",
628                    "RegExp",
629                    "Error",
630                    "EvalError",
631                    "TypeError",
632                    "RangeError",
633                    "ReferenceError",
634                    "SyntaxError",
635                    "URIError",
636                    "JSON",
637                ),
638                $self->property("undefined", $self->undefinedValue(), "0"),
639                $self->property("NaN", $self->numberValue("math.NaN()"), "0"),
640                $self->property("Infinity", $self->numberValue("math.Inf(+1)"), "0"),
641            );
642            my $propertyOrder = $self->propertyOrder(@propertyMap);
643            $propertyOrder =~ s/^propertyOrder: //;
644            return
645            "runtime.globalObject.property =",
646            @propertyMap,
647            "runtime.globalObject.propertyOrder =",
648            $propertyOrder,
649            ;
650        }),
651    ;
652}
653
654sub propertyMap {
655    my $self = shift;
656    return "map[string]_property{", (join ",\n", @_, ""), "}",
657}
658
659our (@preblock, @postblock);
660sub block {
661    my $self = shift;
662    local @preblock = ();
663    local @postblock = ();
664    my @input = $_[0]->();
665    my @output;
666    while (@input) {
667        local $_ = shift @input;
668        if (m/^\./) {
669            $_ = "runtime.global$_";
670        }
671        if (m/ :?=$/) {
672            $_ .= shift @input;
673        }
674        push @output, $_;
675    }
676    return
677    "{",
678        @preblock,
679        @output,
680        @postblock,
681    "}",
682    ;
683}
684
685sub numberConstantDeclare {
686    my $self = shift;
687    my @got;
688    while (@_) {
689        my $name = shift;
690        my $value = shift;
691        push @got, $self->property($name, $self->numberValue($value), "0"),
692    }
693    return @got;
694}
695
696sub functionDeclare {
697    my $self = shift;
698    my $class = shift;
699    my $builtin = "builtin${class}";
700    my @got;
701    while (@_) {
702        my $name = shift;
703        my $length = shift;
704        $name = $self->newFunction($name, "${builtin}_", $length);
705        push @got, $self->functionProperty($name),
706    }
707    return @got;
708}
709
710sub globalDeclare {
711    my $self = shift;
712    my @got;
713    while (@_) {
714        my $name = shift;
715        push @got, $self->property($name, $self->objectValue("runtime.global.$name"), "0101"),
716    }
717    return @got;
718}
719
720sub propertyOrder {
721    my $self = shift;
722    my $propertyMap = join "", @_;
723
724    my (@keys) = $propertyMap =~ m/("\w+"):/g;
725    my $propertyOrder =
726        join "\n", "propertyOrder: []string{", (join ",\n", @keys, ""), "}";
727    return $propertyOrder;
728}
729
730sub globalObject {
731    my $self = shift;
732    my $name = shift;
733
734    my $propertyMap = "";
735    if (@_) {
736        $propertyMap = join "\n", $self->propertyMap(@_);
737        my $propertyOrder = $self->propertyOrder($propertyMap);
738        $propertyMap = "property: $propertyMap,\n$propertyOrder,";
739    }
740
741    return trim <<_END_;
742&_object{
743    runtime: runtime,
744    class: "$name",
745    objectClass: _classObject,
746    prototype: runtime.global.ObjectPrototype,
747    extensible: true,
748    $propertyMap
749}
750_END_
751}
752
753sub globalFunction {
754    my $self = shift;
755    my $name = shift;
756    my $length = shift;
757
758    my $builtin = "builtin${name}";
759    my $builtinNew = "builtinNew${name}";
760    my $prototype = "runtime.global.${name}Prototype";
761    my $propertyMap = "";
762    unshift @_,
763        $self->property("length", $self->numberValue($length), "0"),
764        $self->property("prototype", $self->objectValue($prototype), "0"),
765    ;
766
767    if (@_) {
768        $propertyMap = join "\n", $self->propertyMap(@_);
769        my $propertyOrder = $self->propertyOrder($propertyMap);
770        $propertyMap = "property: $propertyMap,\n$propertyOrder,";
771    }
772
773    push @postblock, $self->statement(
774        "$prototype.property[\"constructor\"] =",
775        $self->property(undef, $self->objectValue("runtime.global.${name}"), "0101"),
776    );
777
778    return trim <<_END_;
779&_object{
780    runtime: runtime,
781    class: "Function",
782    objectClass: _classObject,
783    prototype: runtime.global.FunctionPrototype,
784    extensible: true,
785    value: @{[ $self->nativeFunctionOf($name, $builtin, $builtinNew) ]},
786    $propertyMap
787}
788_END_
789}
790
791sub nativeCallFunction {
792    my $self = shift;
793    my $name = shift;
794    my $func = shift;
795    return trim <<_END_;
796_nativeCallFunction{ "$name", $func }
797_END_
798}
799
800sub globalPrototype {
801    my $self = shift;
802    my $class = shift;
803    my $classObject = shift;
804    my $prototype = shift;
805    my $value = shift;
806
807    if (!defined $prototype) {
808        $prototype = "nil";
809    }
810
811    if (!defined $value) {
812        $value = "nil";
813    }
814
815    if ($prototype =~ m/^\./) {
816        $prototype = "runtime.global$prototype";
817    }
818
819    my $propertyMap = "";
820    if (@_) {
821        $propertyMap = join "\n", $self->propertyMap(@_);
822        my $propertyOrder = $self->propertyOrder($propertyMap);
823        $propertyMap = "property: $propertyMap,\n$propertyOrder,";
824    }
825
826    return trim <<_END_;
827&_object{
828    runtime: runtime,
829    class: "$class",
830    objectClass: $classObject,
831    prototype: $prototype,
832    extensible: true,
833    value: $value,
834    $propertyMap
835}
836_END_
837}
838
839sub newFunction {
840    my $self = shift;
841    my $name = shift;
842    my $func = shift;
843    my $length = shift;
844
845    my @name = ($name, $name);
846    if ($name =~ m/^(\w+):(\w+)$/) {
847        @name = ($1, $2);
848        $name = $name[0];
849    }
850
851    if ($func =~ m/^builtin\w+_$/) {
852        $func = "$func$name[1]";
853    }
854
855    my $propertyOrder = "";
856    my @propertyMap = (
857        $self->property("length", $self->numberValue($length), "0"),
858    );
859
860    if (@propertyMap) {
861        $propertyOrder = $self->propertyOrder(@propertyMap);
862        $propertyOrder = "$propertyOrder,";
863    }
864
865    my $label = functionLabel($name);
866    push @preblock, $self->statement(
867        "$label := @{[ trim <<_END_ ]}",
868&_object{
869    runtime: runtime,
870    class: "Function",
871    objectClass: _classObject,
872    prototype: runtime.global.FunctionPrototype,
873    extensible: true,
874    property: @{[ join "\n", $self->propertyMap(@propertyMap) ]},
875    $propertyOrder
876    value: @{[ $self->nativeFunctionOf($name, $func) ]},
877}
878_END_
879    );
880
881    return $name;
882}
883
884sub newObject {
885    my $self = shift;
886
887    my $propertyMap = join "\n", $self->propertyMap(@_);
888    my $propertyOrder = $self->propertyOrder($propertyMap);
889
890    return trim <<_END_;
891&_object{
892    runtime: runtime,
893    class: "Object",
894    objectClass: _classObject,
895    prototype: runtime.global.ObjectPrototype,
896    extensible: true,
897    property: $propertyMap,
898    $propertyOrder,
899}
900_END_
901}
902
903sub newPrototypeObject {
904    my $self = shift;
905    my $class = shift;
906    my $objectClass = shift;
907    my $value = shift;
908    if (defined $value) {
909        $value = "value: $value,";
910    }
911
912    my $propertyMap = join "\n", $self->propertyMap(@_);
913    my $propertyOrder = $self->propertyOrder($propertyMap);
914
915    return trim <<_END_;
916&_object{
917    runtime: runtime,
918    class: "$class",
919    objectClass: $objectClass,
920    prototype: runtime.global.ObjectPrototype,
921    extensible: true,
922    property: $propertyMap,
923    $propertyOrder,
924    $value
925}
926_END_
927}
928
929sub functionProperty {
930    my $self = shift;
931    my $name = shift;
932
933    return $self->property(
934        $name,
935        $self->objectValue(functionLabel($name))
936    );
937}
938
939sub statement {
940    my $self = shift;
941    return join "\n", @_;
942}
943
944sub functionOf {
945    my $self = shift;
946    my $call = shift;
947    my $construct = shift;
948    if ($construct) {
949        $construct = "construct: $construct,";
950    } else {
951        $construct = "";
952    }
953
954    return trim <<_END_
955_functionObject{
956    call: $call,
957    $construct
958}
959_END_
960}
961
962sub nativeFunctionOf {
963    my $self = shift;
964    my $name = shift;
965    my $call = shift;
966    my $construct = shift;
967    if ($construct) {
968        $construct = "construct: $construct,";
969    } else {
970        $construct = "";
971    }
972
973    return trim <<_END_
974_nativeFunctionObject{
975    name: "$name",
976    call: $call,
977    $construct
978}
979_END_
980}
981
982sub nameProperty {
983    my $self = shift;
984    my $name = shift;
985    my $value = shift;
986
987    return trim <<_END_;
988"$name": _property{
989    mode: 0101,
990    value: $value,
991}
992_END_
993}
994
995sub numberValue {
996    my $self = shift;
997    my $value = shift;
998    return trim <<_END_;
999Value{
1000    kind: valueNumber,
1001    value: $value,
1002}
1003_END_
1004}
1005
1006sub property {
1007    my $self = shift;
1008    my $name = shift;
1009    my $value = shift;
1010    my $mode = shift;
1011    $mode = "0101" unless defined $mode;
1012    if (! defined $value) {
1013        $value = "Value{}";
1014    }
1015    if (defined $name) {
1016        return trim <<_END_;
1017"$name": _property{
1018    mode: $mode,
1019    value: $value,
1020}
1021_END_
1022    } else {
1023        return trim <<_END_;
1024_property{
1025    mode: $mode,
1026    value: $value,
1027}
1028_END_
1029    }
1030
1031}
1032
1033sub objectProperty {
1034    my $self = shift;
1035    my $name = shift;
1036    my $value = shift;
1037
1038    return trim <<_END_;
1039"$name": _property{
1040    mode: 0101,
1041    value: @{[ $self->objectValue($value)]},
1042}
1043_END_
1044}
1045
1046sub objectValue {
1047    my $self = shift;
1048    my $value = shift;
1049    return trim <<_END_
1050Value{
1051    kind: valueObject,
1052    value: $value,
1053}
1054_END_
1055}
1056
1057sub stringValue {
1058    my $self = shift;
1059    my $value = shift;
1060    return trim <<_END_
1061Value{
1062    kind: valueString,
1063    value: "$value",
1064}
1065_END_
1066}
1067
1068sub booleanValue {
1069    my $self = shift;
1070    my $value = shift;
1071    return trim <<_END_
1072Value{
1073    kind: valueBoolean,
1074    value: $value,
1075}
1076_END_
1077}
1078
1079sub undefinedValue {
1080    my $self = shift;
1081    return trim <<_END_
1082Value{
1083    kind: valueUndefined,
1084}
1085_END_
1086}
1087