#!/usr/bin/env perl my $_fmt; $_fmt = "gofmt"; $_fmt = "cat -n" if "cat" eq ($ARGV[0] || ""); use strict; use warnings; 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{ _valueType: valueNumber, value: value, } } _END_ } $fmt->print(<<_END_); func toValue_string(value string) Value { return Value{ _valueType: valueString, value: value, } } func toValue_string16(value []uint16) Value { return Value{ _valueType: valueString, value: value, } } func toValue_bool(value bool) Value { return Value{ _valueType: valueBoolean, value: value, } } func toValue_object(value *_object) Value { return Value{ _valueType: 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, ); return ".${class}Prototype.property =", $self->propertyMap( @got, ), }), # FunctionPrototype $self->block(sub { my $class = "Function"; my @got = $self->functionDeclare( $class, "toString", 0, "apply", 2, "call", 2, "bind", 1, ); return ".${class}Prototype.property =", $self->propertyMap( @got, $self->property("constructor", undef, "0101"), $self->property("length", $self->numberValue(0), "0"), ), }), # 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", ".${class}Prototype.property[\"constructor\"] =", $self->property(undef, $self->objectValue("Function"), "0"), }), # 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", 0, "indexOf", 1, "lastIndexOf", 1, "every", 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", 0, ), ), }), # 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, ); 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, ); return ".${class}Prototype =", $self->globalPrototype( $class, "_classObject", ".ObjectPrototype", "prototypeValueNumber", @got, ), ".$class =", $self->globalFunction( $class, 1, $self->functionDeclare( $class, ), $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", 1, "setUTCSeconds", 1, "setMinutes", 1, "setUTCMinutes", 1, "setHours", 1, "setUTCHours", 1, "setDate", 1, "setUTCDate", 1, "setMonth", 1, "setUTCMonth", 1, "setYear", 1, "setFullYear", 1, "setUTCFullYear", 1, ); return ".${class}Prototype =", $self->globalPrototype( $class, "_classObject", ".ObjectPrototype", "prototypeValueDate", @got, ), ".$class =", $self->globalFunction( $class, 7, $self->functionDeclare( $class, "parse", 0, "UTC", 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/), # 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, ); return "runtime.GlobalObject.property =", $self->propertyMap( @got, $self->globalDeclare( "Object", "Function", "Array", "String", "Boolean", "Number", "Math", "RegExp", "Date", "Error", "EvalError", "TypeError", "RangeError", "ReferenceError", "SyntaxError", "URIError", ), $self->property("undefined", $self->undefinedValue(), "0"), $self->property("NaN", $self->numberValue("math.NaN()"), "0"), $self->property("Infinity", $self->numberValue("math.Inf(+1)"), "0"), ), }), ; } 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 globalObject { my $self = shift; my $name = shift; my $propertyMap = ""; if (@_) { $propertyMap = join "\n", $self->propertyMap(@_); $propertyMap = "property: $propertyMap,"; } 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(@_); $propertyMap = "property: $propertyMap,"; } 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->functionOf($self->nativeCallFunction($builtin), $builtinNew) ]}, $propertyMap } _END_ } sub nativeCallFunction { my $self = shift; my $func = shift; return trim <<_END_; _nativeCallFunction($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(@_); $propertyMap = "property: $propertyMap,"; } 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]; } my $prototype = "${name}_prototype"; if ($name =~ s/^-//) { undef $prototype; } if ($func =~ m/^builtin\w+_$/) { undef $prototype; $func = "$func$name[1]"; } my @propertyMap = ( $self->property("length", $self->numberValue($length), "0"), ); if ($prototype) { push @propertyMap, $self->property("prototype", $self->objectValue($prototype), "0100"); } if ($prototype) { push @preblock, $self->statement( "$prototype :=", $self->newObject( $self->property("constructor", undef, "0101"), ), ); } 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) ]}, value: @{[ $self->functionOf($self->nativeCallFunction($func)) ]}, } _END_ ); if ($prototype) { push @preblock, $self->statement( "$prototype.property[\"constructor\"] =", $self->property(undef, $self->objectValue("$label"), "0101"), ); } return $name; } sub newObject { my $self = shift; my @propertyMap = @_; return trim <<_END_; &_object{ runtime: runtime, class: "Object", objectClass: _classObject, prototype: runtime.Global.ObjectPrototype, extensible: true, property: @{[ join "\n", $self->propertyMap(@propertyMap) ]}, } _END_ } sub newPrototypeObject { my $self = shift; my $class = shift; my $objectClass = shift; my $value = shift; if (defined $value) { $value = "value: $value,"; } my @propertyMap = @_; return trim <<_END_; &_object{ runtime: runtime, class: "$class", objectClass: $objectClass, prototype: runtime.Global.ObjectPrototype, extensible: true, property: @{[ join "\n", $self->propertyMap(@propertyMap) ]}, $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 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{ _valueType: 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{ _valueType: valueObject, value: $value, } _END_ } sub stringValue { my $self = shift; my $value = shift; return trim <<_END_ Value{ _valueType: valueString, value: "$value", } _END_ } sub booleanValue { my $self = shift; my $value = shift; return trim <<_END_ Value{ _valueType: valueBoolean, value: $value, } _END_ } sub undefinedValue { my $self = shift; return trim <<_END_ Value{ _valueType: valueUndefined, } _END_ }