mirror of
				https://github.com/robertkrimen/otto
				synced 2025-10-19 19:55:30 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			1088 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1088 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env perl
 | |
| 
 | |
| my $_fmt;
 | |
| $_fmt = "gofmt";
 | |
| $_fmt = "cat -n" if "cat" eq ($ARGV[0] || "");
 | |
| 
 | |
| use strict;
 | |
| use warnings;
 | |
| use IO::File;
 | |
| 
 | |
| my $self = __PACKAGE__;
 | |
| 
 | |
| sub functionLabel ($) {
 | |
|     return "$_[0]_function";
 | |
| }
 | |
| 
 | |
| sub trim ($) {
 | |
|     local $_ = shift;
 | |
|     s/^\s*//, s/\s*$// for $_;
 | |
|     return $_;
 | |
| }
 | |
| 
 | |
| open my $fmt, "|-", "$_fmt" or die $!;
 | |
| 
 | |
| $fmt->print(<<_END_);
 | |
| package otto
 | |
| 
 | |
| import (
 | |
|     "math"
 | |
| )
 | |
| 
 | |
| func _newContext(runtime *_runtime) {
 | |
| @{[ join "\n", $self->newContext() ]}
 | |
| }
 | |
| 
 | |
| func newConsoleObject(runtime *_runtime) *_object {
 | |
| @{[ join "\n", $self->newConsoleObject() ]}
 | |
| }
 | |
| _END_
 | |
| 
 | |
| for (qw/int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float32 float64/) {
 | |
|     $fmt->print(<<_END_);
 | |
| 
 | |
| func toValue_$_(value $_) Value {
 | |
|     return Value{
 | |
|         kind: valueNumber,
 | |
|         value: value,
 | |
|     }
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| $fmt->print(<<_END_);
 | |
| 
 | |
| func toValue_string(value string) Value {
 | |
|     return Value{
 | |
|         kind: valueString,
 | |
|         value: value,
 | |
|     }
 | |
| }
 | |
| 
 | |
| func toValue_string16(value []uint16) Value {
 | |
|     return Value{
 | |
|         kind: valueString,
 | |
|         value: value,
 | |
|     }
 | |
| }
 | |
| 
 | |
| func toValue_bool(value bool) Value {
 | |
|     return Value{
 | |
|         kind: valueBoolean,
 | |
|         value: value,
 | |
|     }
 | |
| }
 | |
| 
 | |
| func toValue_object(value *_object) Value {
 | |
|     return Value{
 | |
|         kind: valueObject,
 | |
|         value: value,
 | |
|     }
 | |
| }
 | |
| _END_
 | |
| 
 | |
| close $fmt;
 | |
| 
 | |
| sub newConsoleObject {
 | |
|     my $self = shift;
 | |
| 
 | |
|     return
 | |
|         $self->block(sub {
 | |
|             my $class = "Console";
 | |
|             my @got = $self->functionDeclare(
 | |
|                 $class,
 | |
|                 "log", 0,
 | |
|                 "debug:log", 0,
 | |
|                 "info:log", 0,
 | |
|                 "error", 0,
 | |
|                 "warn:error", 0,
 | |
|                 "dir", 0,
 | |
|                 "time", 0,
 | |
|                 "timeEnd", 0,
 | |
|                 "trace", 0,
 | |
|                 "assert", 0,
 | |
|             );
 | |
|             return
 | |
|             "return @{[ $self->newObject(@got) ]}"
 | |
|         }),
 | |
|     ;
 | |
| }
 | |
| 
 | |
| sub newContext {
 | |
|     my $self = shift;
 | |
|     return
 | |
|         # ObjectPrototype
 | |
|         $self->block(sub {
 | |
|             my $class = "Object";
 | |
|             return
 | |
|             ".${class}Prototype =",
 | |
|             $self->globalPrototype(
 | |
|                 $class,
 | |
|                 "_classObject",
 | |
|                 undef,
 | |
|                 "prototypeValueObject",
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         # FunctionPrototype
 | |
|         $self->block(sub {
 | |
|             my $class = "Function";
 | |
|             return
 | |
|             ".${class}Prototype =",
 | |
|             $self->globalPrototype(
 | |
|                 $class,
 | |
|                 "_classObject",
 | |
|                 ".ObjectPrototype",
 | |
|                 "prototypeValueFunction",
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         # ObjectPrototype
 | |
|         $self->block(sub {
 | |
|             my $class = "Object";
 | |
|             my @got = $self->functionDeclare(
 | |
|                 $class,
 | |
|                 "valueOf", 0,
 | |
|                 "toString", 0,
 | |
|                 "toLocaleString", 0,
 | |
|                 "hasOwnProperty", 1,
 | |
|                 "isPrototypeOf", 1,
 | |
|                 "propertyIsEnumerable", 1,
 | |
|             );
 | |
|             my @propertyMap = $self->propertyMap(
 | |
|                 @got,
 | |
|                 $self->property("constructor", undef),
 | |
|             );
 | |
|             my $propertyOrder = $self->propertyOrder(@propertyMap);
 | |
|             $propertyOrder =~ s/^propertyOrder: //;
 | |
|             return
 | |
|             ".${class}Prototype.property =", @propertyMap,
 | |
|             ".${class}Prototype.propertyOrder =", $propertyOrder,
 | |
|         }),
 | |
| 
 | |
|         # FunctionPrototype
 | |
|         $self->block(sub {
 | |
|             my $class = "Function";
 | |
|             my @got = $self->functionDeclare(
 | |
|                 $class,
 | |
|                 "toString", 0,
 | |
|                 "apply", 2,
 | |
|                 "call", 1,
 | |
|                 "bind", 1,
 | |
|             );
 | |
|             my @propertyMap = $self->propertyMap(
 | |
|                 @got,
 | |
|                 $self->property("constructor", undef),
 | |
|                 $self->property("length", $self->numberValue(0), "0"),
 | |
|             );
 | |
|             my $propertyOrder = $self->propertyOrder(@propertyMap);
 | |
|             $propertyOrder =~ s/^propertyOrder: //;
 | |
|             return
 | |
|             ".${class}Prototype.property =", @propertyMap,
 | |
|             ".${class}Prototype.propertyOrder =", $propertyOrder,
 | |
|         }),
 | |
| 
 | |
|         # Object
 | |
|         $self->block(sub {
 | |
|             my $class = "Object";
 | |
|             return
 | |
|             ".$class =",
 | |
|             $self->globalFunction(
 | |
|                 $class,
 | |
|                 1,
 | |
|                 $self->functionDeclare(
 | |
|                     $class,
 | |
|                     "getPrototypeOf", 1,
 | |
|                     "getOwnPropertyDescriptor", 2,
 | |
|                     "defineProperty", 3,
 | |
|                     "defineProperties", 2,
 | |
|                     "create", 2,
 | |
|                     "isExtensible", 1,
 | |
|                     "preventExtensions", 1,
 | |
|                     "isSealed", 1,
 | |
|                     "seal", 1,
 | |
|                     "isFrozen", 1,
 | |
|                     "freeze", 1,
 | |
|                     "keys", 1,
 | |
|                     "getOwnPropertyNames", 1,
 | |
|                 ),
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         # Function
 | |
|         $self->block(sub {
 | |
|             my $class = "Function";
 | |
|             return
 | |
|             "Function :=",
 | |
|             $self->globalFunction(
 | |
|                 $class,
 | |
|                 1,
 | |
|             ),
 | |
|             ".$class = Function",
 | |
|         }),
 | |
| 
 | |
|         # Array
 | |
|         $self->block(sub {
 | |
|             my $class = "Array";
 | |
|             my @got = $self->functionDeclare(
 | |
|                 $class,
 | |
|                 "toString", 0,
 | |
|                 "toLocaleString", 0,
 | |
|                 "concat", 1,
 | |
|                 "join", 1,
 | |
|                 "splice", 2,
 | |
|                 "shift", 0,
 | |
|                 "pop", 0,
 | |
|                 "push", 1,
 | |
|                 "slice", 2,
 | |
|                 "unshift", 1,
 | |
|                 "reverse", 0,
 | |
|                 "sort", 1,
 | |
|                 "indexOf", 1,
 | |
|                 "lastIndexOf", 1,
 | |
|                 "every", 1,
 | |
|                 "some", 1,
 | |
|                 "forEach", 1,
 | |
|                 "map", 1,
 | |
|                 "filter", 1,
 | |
|                 "reduce", 1,
 | |
|                 "reduceRight", 1,
 | |
|             );
 | |
|             return
 | |
|             ".${class}Prototype =",
 | |
|             $self->globalPrototype(
 | |
|                 $class,
 | |
|                 "_classArray",
 | |
|                 ".ObjectPrototype",
 | |
|                 undef,
 | |
|                 $self->property("length", $self->numberValue("uint32(0)"), "0100"),
 | |
|                 @got,
 | |
|             ),
 | |
|             ".$class =",
 | |
|             $self->globalFunction(
 | |
|                 $class,
 | |
|                 1,
 | |
|                 $self->functionDeclare(
 | |
|                     $class,
 | |
|                     "isArray", 1,
 | |
|                 ),
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         # String
 | |
|         $self->block(sub {
 | |
|             my $class = "String";
 | |
|             my @got = $self->functionDeclare(
 | |
|                 $class,
 | |
|                 "toString", 0,
 | |
|                 "valueOf", 0,
 | |
|                 "charAt", 1,
 | |
|                 "charCodeAt", 1,
 | |
|                 "concat", 1,
 | |
|                 "indexOf", 1,
 | |
|                 "lastIndexOf", 1,
 | |
|                 "match", 1,
 | |
|                 "replace", 2,
 | |
|                 "search", 1,
 | |
|                 "split", 2,
 | |
|                 "slice", 2,
 | |
|                 "substring", 2,
 | |
|                 "toLowerCase", 0,
 | |
|                 "toUpperCase", 0,
 | |
|                 "substr", 2,
 | |
|                 "trim", 0,
 | |
|                 "trimLeft", 0,
 | |
|                 "trimRight", 0,
 | |
|                 "localeCompare", 1,
 | |
|                 "toLocaleLowerCase", 0,
 | |
|                 "toLocaleUpperCase", 0,
 | |
|             );
 | |
|             return
 | |
|             ".${class}Prototype =",
 | |
|             $self->globalPrototype(
 | |
|                 $class,
 | |
|                 "_classString",
 | |
|                 ".ObjectPrototype",
 | |
|                 "prototypeValueString",
 | |
|                 $self->property("length", $self->numberValue("int(0)"), "0"),
 | |
|                 @got,
 | |
|             ),
 | |
|             ".$class =",
 | |
|             $self->globalFunction(
 | |
|                 $class,
 | |
|                 1,
 | |
|                 $self->functionDeclare(
 | |
|                     $class,
 | |
| 		            "fromCharCode", 1,
 | |
|                 ),
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         # Boolean
 | |
|         $self->block(sub {
 | |
|             my $class = "Boolean";
 | |
|             my @got = $self->functionDeclare(
 | |
|                 $class,
 | |
|                 "toString", 0,
 | |
|                 "valueOf", 0,
 | |
|             );
 | |
|             return
 | |
|             ".${class}Prototype =",
 | |
|             $self->globalPrototype(
 | |
|                 $class,
 | |
|                 "_classObject",
 | |
|                 ".ObjectPrototype",
 | |
|                 "prototypeValueBoolean",
 | |
|                 @got,
 | |
|             ),
 | |
|             ".$class =",
 | |
|             $self->globalFunction(
 | |
|                 $class,
 | |
|                 1,
 | |
|                 $self->functionDeclare(
 | |
|                     $class,
 | |
|                 ),
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         # Number
 | |
|         $self->block(sub {
 | |
|             my $class = "Number";
 | |
|             my @got = $self->functionDeclare(
 | |
|                 $class,
 | |
|                 "toString", 0,
 | |
|                 "valueOf", 0,
 | |
|                 "toFixed", 1,
 | |
|                 "toExponential", 1,
 | |
|                 "toPrecision", 1,
 | |
|                 "toLocaleString", 1,
 | |
|             );
 | |
|             return
 | |
|             ".${class}Prototype =",
 | |
|             $self->globalPrototype(
 | |
|                 $class,
 | |
|                 "_classObject",
 | |
|                 ".ObjectPrototype",
 | |
|                 "prototypeValueNumber",
 | |
|                 @got,
 | |
|             ),
 | |
|             ".$class =",
 | |
|             $self->globalFunction(
 | |
|                 $class,
 | |
|                 1,
 | |
|                 $self->functionDeclare(
 | |
|                     $class,
 | |
|                     "isNaN", 1,
 | |
|                 ),
 | |
|                 $self->numberConstantDeclare(
 | |
|                     "MAX_VALUE", "math.MaxFloat64",
 | |
|                     "MIN_VALUE", "math.SmallestNonzeroFloat64",
 | |
|                     "NaN", "math.NaN()",
 | |
|                     "NEGATIVE_INFINITY", "math.Inf(-1)",
 | |
|                     "POSITIVE_INFINITY", "math.Inf(+1)",
 | |
|                 ),
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         # Math
 | |
|         $self->block(sub {
 | |
|             my $class = "Math";
 | |
|             return
 | |
|             ".$class =",
 | |
|             $self->globalObject(
 | |
|                 $class,
 | |
|                 $self->functionDeclare(
 | |
|                     $class,
 | |
|                     "abs", 1,
 | |
|                     "acos", 1,
 | |
|                     "asin", 1,
 | |
|                     "atan", 1,
 | |
|                     "atan2", 1,
 | |
|                     "ceil", 1,
 | |
|                     "cos", 1,
 | |
|                     "exp", 1,
 | |
|                     "floor", 1,
 | |
|                     "log", 1,
 | |
|                     "max", 2,
 | |
|                     "min", 2,
 | |
|                     "pow", 2,
 | |
|                     "random", 0,
 | |
|                     "round", 1,
 | |
|                     "sin", 1,
 | |
|                     "sqrt", 1,
 | |
|                     "tan", 1,
 | |
|                 ),
 | |
|                 $self->numberConstantDeclare(
 | |
|                     "E", "math.E",
 | |
|                     "LN10", "math.Ln10",
 | |
|                     "LN2", "math.Ln2",
 | |
|                     "LOG2E", "math.Log2E",
 | |
|                     "LOG10E", "math.Log10E",
 | |
|                     "PI", "math.Pi",
 | |
|                     "SQRT1_2", "sqrt1_2",
 | |
|                     "SQRT2", "math.Sqrt2",
 | |
|                 )
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         # Date
 | |
|         $self->block(sub {
 | |
|             my $class = "Date";
 | |
|             my @got = $self->functionDeclare(
 | |
|                 $class,
 | |
|                 "toString", 0,
 | |
|                 "toDateString", 0,
 | |
|                 "toTimeString", 0,
 | |
|                 "toUTCString", 0,
 | |
|                 "toISOString", 0,
 | |
|                 "toJSON", 1,
 | |
|                 "toGMTString", 0,
 | |
|                 "toLocaleString", 0,
 | |
|                 "toLocaleDateString", 0,
 | |
|                 "toLocaleTimeString", 0,
 | |
|                 "valueOf", 0,
 | |
|                 "getTime", 0,
 | |
|                 "getYear", 0,
 | |
|                 "getFullYear", 0,
 | |
|                 "getUTCFullYear", 0,
 | |
|                 "getMonth", 0,
 | |
|                 "getUTCMonth", 0,
 | |
|                 "getDate", 0,
 | |
|                 "getUTCDate", 0,
 | |
|                 "getDay", 0,
 | |
|                 "getUTCDay", 0,
 | |
|                 "getHours", 0,
 | |
|                 "getUTCHours", 0,
 | |
|                 "getMinutes", 0,
 | |
|                 "getUTCMinutes", 0,
 | |
|                 "getSeconds", 0,
 | |
|                 "getUTCSeconds", 0,
 | |
|                 "getMilliseconds", 0,
 | |
|                 "getUTCMilliseconds", 0,
 | |
|                 "getTimezoneOffset", 0,
 | |
|                 "setTime", 1,
 | |
|                 "setMilliseconds", 1,
 | |
|                 "setUTCMilliseconds", 1,
 | |
|                 "setSeconds", 2,
 | |
|                 "setUTCSeconds", 2,
 | |
|                 "setMinutes", 3,
 | |
|                 "setUTCMinutes", 3,
 | |
|                 "setHours", 4,
 | |
|                 "setUTCHours", 4,
 | |
|                 "setDate", 1,
 | |
|                 "setUTCDate", 1,
 | |
|                 "setMonth", 2,
 | |
|                 "setUTCMonth", 2,
 | |
|                 "setYear", 1,
 | |
|                 "setFullYear", 3,
 | |
|                 "setUTCFullYear", 3,
 | |
|             );
 | |
|             return
 | |
|             ".${class}Prototype =",
 | |
|             $self->globalPrototype(
 | |
|                 $class,
 | |
|                 "_classObject",
 | |
|                 ".ObjectPrototype",
 | |
|                 "prototypeValueDate",
 | |
|                 @got,
 | |
|             ),
 | |
|             ".$class =",
 | |
|             $self->globalFunction(
 | |
|                 $class,
 | |
|                 7,
 | |
|                 $self->functionDeclare(
 | |
|                     $class,
 | |
|                     "parse", 1,
 | |
|                     "UTC", 7,
 | |
|                     "now", 0,
 | |
|                 ),
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         # RegExp
 | |
|         $self->block(sub {
 | |
|             my $class = "RegExp";
 | |
|             my @got = $self->functionDeclare(
 | |
|                 $class,
 | |
|                 "toString", 0,
 | |
|                 "exec", 1,
 | |
|                 "test", 1,
 | |
|                 "compile", 1,
 | |
|             );
 | |
|             return
 | |
|             ".${class}Prototype =",
 | |
|             $self->globalPrototype(
 | |
|                 $class,
 | |
|                 "_classObject",
 | |
|                 ".ObjectPrototype",
 | |
|                 "prototypeValueRegExp",
 | |
|                 @got,
 | |
|             ),
 | |
|             ".$class =",
 | |
|             $self->globalFunction(
 | |
|                 $class,
 | |
|                 2,
 | |
|                 $self->functionDeclare(
 | |
|                     $class,
 | |
|                 ),
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         # Error
 | |
|         $self->block(sub {
 | |
|             my $class = "Error";
 | |
|             my @got = $self->functionDeclare(
 | |
|                 $class,
 | |
|                 "toString", 0,
 | |
|             );
 | |
|             return
 | |
|             ".${class}Prototype =",
 | |
|             $self->globalPrototype(
 | |
|                 $class,
 | |
|                 "_classObject",
 | |
|                 ".ObjectPrototype",
 | |
|                 undef,
 | |
|                 @got,
 | |
|                 $self->property("name", $self->stringValue("Error")),
 | |
|                 $self->property("message", $self->stringValue("")),
 | |
|             ),
 | |
|             ".$class =",
 | |
|             $self->globalFunction(
 | |
|                 $class,
 | |
|                 1,
 | |
|                 $self->functionDeclare(
 | |
|                     $class,
 | |
|                 ),
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         (map {
 | |
|             my $class = "${_}Error";
 | |
|             $self->block(sub {
 | |
|                 my @got = $self->functionDeclare(
 | |
|                     $class,
 | |
|                 );
 | |
|                 return
 | |
|                 ".${class}Prototype =",
 | |
|                 $self->globalPrototype(
 | |
|                     $class,
 | |
|                     "_classObject",
 | |
|                     ".ErrorPrototype",
 | |
|                     undef,
 | |
|                     @got,
 | |
|                     $self->property("name", $self->stringValue($class)),
 | |
|                 ),
 | |
|                 ".$class =",
 | |
|                 $self->globalFunction(
 | |
|                     $class,
 | |
|                     1,
 | |
|                     $self->functionDeclare(
 | |
|                         $class,
 | |
|                     ),
 | |
|                 ),
 | |
|             });
 | |
|         } qw/Eval Type Range Reference Syntax URI/),
 | |
| 
 | |
|         # JSON
 | |
|         $self->block(sub {
 | |
|             my $class = "JSON";
 | |
|             return
 | |
|             ".$class =",
 | |
|             $self->globalObject(
 | |
|                 $class,
 | |
|                 $self->functionDeclare(
 | |
|                     $class,
 | |
|                     "parse", 2,
 | |
|                     "stringify", 3,
 | |
|                 ),
 | |
|             ),
 | |
|         }),
 | |
| 
 | |
|         # Global
 | |
|         $self->block(sub {
 | |
|             my $class = "Global";
 | |
|             my @got = $self->functionDeclare(
 | |
|                 $class,
 | |
|                 "eval", 1,
 | |
|                 "parseInt", 2,
 | |
|                 "parseFloat", 1,
 | |
|                 "isNaN", 1,
 | |
|                 "isFinite", 1,
 | |
|                 "decodeURI", 1,
 | |
|                 "decodeURIComponent", 1,
 | |
|                 "encodeURI", 1,
 | |
|                 "encodeURIComponent", 1,
 | |
|                 "escape", 1,
 | |
|                 "unescape", 1,
 | |
|             );
 | |
|             my @propertyMap = $self->propertyMap(
 | |
|                 @got,
 | |
|                 $self->globalDeclare(
 | |
|                     "Object",
 | |
|                     "Function",
 | |
|                     "Array",
 | |
|                     "String",
 | |
|                     "Boolean",
 | |
|                     "Number",
 | |
|                     "Math",
 | |
|                     "Date",
 | |
|                     "RegExp",
 | |
|                     "Error",
 | |
|                     "EvalError",
 | |
|                     "TypeError",
 | |
|                     "RangeError",
 | |
|                     "ReferenceError",
 | |
|                     "SyntaxError",
 | |
|                     "URIError",
 | |
|                     "JSON",
 | |
|                 ),
 | |
|                 $self->property("undefined", $self->undefinedValue(), "0"),
 | |
|                 $self->property("NaN", $self->numberValue("math.NaN()"), "0"),
 | |
|                 $self->property("Infinity", $self->numberValue("math.Inf(+1)"), "0"),
 | |
|             );
 | |
|             my $propertyOrder = $self->propertyOrder(@propertyMap);
 | |
|             $propertyOrder =~ s/^propertyOrder: //;
 | |
|             return
 | |
|             "runtime.globalObject.property =",
 | |
|             @propertyMap,
 | |
|             "runtime.globalObject.propertyOrder =",
 | |
|             $propertyOrder,
 | |
|             ;
 | |
|         }),
 | |
|     ;
 | |
| }
 | |
| 
 | |
| sub propertyMap {
 | |
|     my $self = shift;
 | |
|     return "map[string]_property{", (join ",\n", @_, ""), "}",
 | |
| }
 | |
| 
 | |
| our (@preblock, @postblock);
 | |
| sub block {
 | |
|     my $self = shift;
 | |
|     local @preblock = ();
 | |
|     local @postblock = ();
 | |
|     my @input = $_[0]->();
 | |
|     my @output;
 | |
|     while (@input) {
 | |
|         local $_ = shift @input;
 | |
|         if (m/^\./) {
 | |
|             $_ = "runtime.global$_";
 | |
|         }
 | |
|         if (m/ :?=$/) {
 | |
|             $_ .= shift @input;
 | |
|         }
 | |
|         push @output, $_;
 | |
|     }
 | |
|     return
 | |
|     "{",
 | |
|         @preblock,
 | |
|         @output,
 | |
|         @postblock,
 | |
|     "}",
 | |
|     ;
 | |
| }
 | |
| 
 | |
| sub numberConstantDeclare {
 | |
|     my $self = shift;
 | |
|     my @got;
 | |
|     while (@_) {
 | |
|         my $name = shift;
 | |
|         my $value = shift;
 | |
|         push @got, $self->property($name, $self->numberValue($value), "0"),
 | |
|     }
 | |
|     return @got;
 | |
| }
 | |
| 
 | |
| sub functionDeclare {
 | |
|     my $self = shift;
 | |
|     my $class = shift;
 | |
|     my $builtin = "builtin${class}";
 | |
|     my @got;
 | |
|     while (@_) {
 | |
|         my $name = shift;
 | |
|         my $length = shift;
 | |
|         $name = $self->newFunction($name, "${builtin}_", $length);
 | |
|         push @got, $self->functionProperty($name),
 | |
|     }
 | |
|     return @got;
 | |
| }
 | |
| 
 | |
| sub globalDeclare {
 | |
|     my $self = shift;
 | |
|     my @got;
 | |
|     while (@_) {
 | |
|         my $name = shift;
 | |
|         push @got, $self->property($name, $self->objectValue("runtime.global.$name"), "0101"),
 | |
|     }
 | |
|     return @got;
 | |
| }
 | |
| 
 | |
| sub propertyOrder {
 | |
|     my $self = shift;
 | |
|     my $propertyMap = join "", @_;
 | |
| 
 | |
|     my (@keys) = $propertyMap =~ m/("\w+"):/g;
 | |
|     my $propertyOrder =
 | |
|         join "\n", "propertyOrder: []string{", (join ",\n", @keys, ""), "}";
 | |
|     return $propertyOrder;
 | |
| }
 | |
| 
 | |
| sub globalObject {
 | |
|     my $self = shift;
 | |
|     my $name = shift;
 | |
| 
 | |
|     my $propertyMap = "";
 | |
|     if (@_) {
 | |
|         $propertyMap = join "\n", $self->propertyMap(@_);
 | |
|         my $propertyOrder = $self->propertyOrder($propertyMap);
 | |
|         $propertyMap = "property: $propertyMap,\n$propertyOrder,";
 | |
|     }
 | |
| 
 | |
|     return trim <<_END_;
 | |
| &_object{
 | |
|     runtime: runtime,
 | |
|     class: "$name",
 | |
|     objectClass: _classObject,
 | |
|     prototype: runtime.global.ObjectPrototype,
 | |
|     extensible: true,
 | |
|     $propertyMap
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub globalFunction {
 | |
|     my $self = shift;
 | |
|     my $name = shift;
 | |
|     my $length = shift;
 | |
| 
 | |
|     my $builtin = "builtin${name}";
 | |
|     my $builtinNew = "builtinNew${name}";
 | |
|     my $prototype = "runtime.global.${name}Prototype";
 | |
|     my $propertyMap = "";
 | |
|     unshift @_,
 | |
|         $self->property("length", $self->numberValue($length), "0"),
 | |
|         $self->property("prototype", $self->objectValue($prototype), "0"),
 | |
|     ;
 | |
| 
 | |
|     if (@_) {
 | |
|         $propertyMap = join "\n", $self->propertyMap(@_);
 | |
|         my $propertyOrder = $self->propertyOrder($propertyMap);
 | |
|         $propertyMap = "property: $propertyMap,\n$propertyOrder,";
 | |
|     }
 | |
| 
 | |
|     push @postblock, $self->statement(
 | |
|         "$prototype.property[\"constructor\"] =",
 | |
|         $self->property(undef, $self->objectValue("runtime.global.${name}"), "0101"),
 | |
|     );
 | |
| 
 | |
|     return trim <<_END_;
 | |
| &_object{
 | |
|     runtime: runtime,
 | |
|     class: "Function",
 | |
|     objectClass: _classObject,
 | |
|     prototype: runtime.global.FunctionPrototype,
 | |
|     extensible: true,
 | |
|     value: @{[ $self->nativeFunctionOf($name, $builtin, $builtinNew) ]},
 | |
|     $propertyMap
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub nativeCallFunction {
 | |
|     my $self = shift;
 | |
|     my $name = shift;
 | |
|     my $func = shift;
 | |
|     return trim <<_END_;
 | |
| _nativeCallFunction{ "$name", $func }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub globalPrototype {
 | |
|     my $self = shift;
 | |
|     my $class = shift;
 | |
|     my $classObject = shift;
 | |
|     my $prototype = shift;
 | |
|     my $value = shift;
 | |
| 
 | |
|     if (!defined $prototype) {
 | |
|         $prototype = "nil";
 | |
|     }
 | |
| 
 | |
|     if (!defined $value) {
 | |
|         $value = "nil";
 | |
|     }
 | |
| 
 | |
|     if ($prototype =~ m/^\./) {
 | |
|         $prototype = "runtime.global$prototype";
 | |
|     }
 | |
| 
 | |
|     my $propertyMap = "";
 | |
|     if (@_) {
 | |
|         $propertyMap = join "\n", $self->propertyMap(@_);
 | |
|         my $propertyOrder = $self->propertyOrder($propertyMap);
 | |
|         $propertyMap = "property: $propertyMap,\n$propertyOrder,";
 | |
|     }
 | |
| 
 | |
|     return trim <<_END_;
 | |
| &_object{
 | |
|     runtime: runtime,
 | |
|     class: "$class",
 | |
|     objectClass: $classObject,
 | |
|     prototype: $prototype,
 | |
|     extensible: true,
 | |
|     value: $value,
 | |
|     $propertyMap
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub newFunction {
 | |
|     my $self = shift;
 | |
|     my $name = shift;
 | |
|     my $func = shift;
 | |
|     my $length = shift;
 | |
| 
 | |
|     my @name = ($name, $name);
 | |
|     if ($name =~ m/^(\w+):(\w+)$/) {
 | |
|         @name = ($1, $2);
 | |
|         $name = $name[0];
 | |
|     }
 | |
| 
 | |
|     if ($func =~ m/^builtin\w+_$/) {
 | |
|         $func = "$func$name[1]";
 | |
|     }
 | |
| 
 | |
|     my $propertyOrder = "";
 | |
|     my @propertyMap = (
 | |
|         $self->property("length", $self->numberValue($length), "0"),
 | |
|     );
 | |
| 
 | |
|     if (@propertyMap) {
 | |
|         $propertyOrder = $self->propertyOrder(@propertyMap);
 | |
|         $propertyOrder = "$propertyOrder,";
 | |
|     }
 | |
| 
 | |
|     my $label = functionLabel($name);
 | |
|     push @preblock, $self->statement(
 | |
|         "$label := @{[ trim <<_END_ ]}",
 | |
| &_object{
 | |
|     runtime: runtime,
 | |
|     class: "Function",
 | |
|     objectClass: _classObject,
 | |
|     prototype: runtime.global.FunctionPrototype,
 | |
|     extensible: true,
 | |
|     property: @{[ join "\n", $self->propertyMap(@propertyMap) ]},
 | |
|     $propertyOrder
 | |
|     value: @{[ $self->nativeFunctionOf($name, $func) ]},
 | |
| }
 | |
| _END_
 | |
|     );
 | |
| 
 | |
|     return $name;
 | |
| }
 | |
| 
 | |
| sub newObject {
 | |
|     my $self = shift;
 | |
| 
 | |
|     my $propertyMap = join "\n", $self->propertyMap(@_);
 | |
|     my $propertyOrder = $self->propertyOrder($propertyMap);
 | |
| 
 | |
|     return trim <<_END_;
 | |
| &_object{
 | |
|     runtime: runtime,
 | |
|     class: "Object",
 | |
|     objectClass: _classObject,
 | |
|     prototype: runtime.global.ObjectPrototype,
 | |
|     extensible: true,
 | |
|     property: $propertyMap,
 | |
|     $propertyOrder,
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub newPrototypeObject {
 | |
|     my $self = shift;
 | |
|     my $class = shift;
 | |
|     my $objectClass = shift;
 | |
|     my $value = shift;
 | |
|     if (defined $value) {
 | |
|         $value = "value: $value,";
 | |
|     }
 | |
| 
 | |
|     my $propertyMap = join "\n", $self->propertyMap(@_);
 | |
|     my $propertyOrder = $self->propertyOrder($propertyMap);
 | |
| 
 | |
|     return trim <<_END_;
 | |
| &_object{
 | |
|     runtime: runtime,
 | |
|     class: "$class",
 | |
|     objectClass: $objectClass,
 | |
|     prototype: runtime.global.ObjectPrototype,
 | |
|     extensible: true,
 | |
|     property: $propertyMap,
 | |
|     $propertyOrder,
 | |
|     $value
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub functionProperty {
 | |
|     my $self = shift;
 | |
|     my $name = shift;
 | |
| 
 | |
|     return $self->property(
 | |
|         $name,
 | |
|         $self->objectValue(functionLabel($name))
 | |
|     );
 | |
| }
 | |
| 
 | |
| sub statement {
 | |
|     my $self = shift;
 | |
|     return join "\n", @_;
 | |
| }
 | |
| 
 | |
| sub functionOf {
 | |
|     my $self = shift;
 | |
|     my $call = shift;
 | |
|     my $construct = shift;
 | |
|     if ($construct) {
 | |
|         $construct = "construct: $construct,";
 | |
|     } else {
 | |
|         $construct = "";
 | |
|     }
 | |
| 
 | |
|     return trim <<_END_
 | |
| _functionObject{
 | |
|     call: $call,
 | |
|     $construct
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub nativeFunctionOf {
 | |
|     my $self = shift;
 | |
|     my $name = shift;
 | |
|     my $call = shift;
 | |
|     my $construct = shift;
 | |
|     if ($construct) {
 | |
|         $construct = "construct: $construct,";
 | |
|     } else {
 | |
|         $construct = "";
 | |
|     }
 | |
| 
 | |
|     return trim <<_END_
 | |
| _nativeFunctionObject{
 | |
|     name: "$name",
 | |
|     call: $call,
 | |
|     $construct
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub nameProperty {
 | |
|     my $self = shift;
 | |
|     my $name = shift;
 | |
|     my $value = shift;
 | |
| 
 | |
|     return trim <<_END_;
 | |
| "$name": _property{
 | |
|     mode: 0101,
 | |
|     value: $value,
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub numberValue {
 | |
|     my $self = shift;
 | |
|     my $value = shift;
 | |
|     return trim <<_END_;
 | |
| Value{
 | |
|     kind: valueNumber,
 | |
|     value: $value,
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub property {
 | |
|     my $self = shift;
 | |
|     my $name = shift;
 | |
|     my $value = shift;
 | |
|     my $mode = shift;
 | |
|     $mode = "0101" unless defined $mode;
 | |
|     if (! defined $value) {
 | |
|         $value = "Value{}";
 | |
|     }
 | |
|     if (defined $name) {
 | |
|         return trim <<_END_;
 | |
| "$name": _property{
 | |
|     mode: $mode,
 | |
|     value: $value,
 | |
| }
 | |
| _END_
 | |
|     } else {
 | |
|         return trim <<_END_;
 | |
| _property{
 | |
|     mode: $mode,
 | |
|     value: $value,
 | |
| }
 | |
| _END_
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| sub objectProperty {
 | |
|     my $self = shift;
 | |
|     my $name = shift;
 | |
|     my $value = shift;
 | |
| 
 | |
|     return trim <<_END_;
 | |
| "$name": _property{
 | |
|     mode: 0101,
 | |
|     value: @{[ $self->objectValue($value)]},
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub objectValue {
 | |
|     my $self = shift;
 | |
|     my $value = shift;
 | |
|     return trim <<_END_
 | |
| Value{
 | |
|     kind: valueObject,
 | |
|     value: $value,
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub stringValue {
 | |
|     my $self = shift;
 | |
|     my $value = shift;
 | |
|     return trim <<_END_
 | |
| Value{
 | |
|     kind: valueString,
 | |
|     value: "$value",
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub booleanValue {
 | |
|     my $self = shift;
 | |
|     my $value = shift;
 | |
|     return trim <<_END_
 | |
| Value{
 | |
|     kind: valueBoolean,
 | |
|     value: $value,
 | |
| }
 | |
| _END_
 | |
| }
 | |
| 
 | |
| sub undefinedValue {
 | |
|     my $self = shift;
 | |
|     return trim <<_END_
 | |
| Value{
 | |
|     kind: valueUndefined,
 | |
| }
 | |
| _END_
 | |
| }
 | 
