Permalink
Browse files

compilation to speed up execution (parsing is slooooow), String.proto…

…type.slice, RegExp.prototype.exec, vm bugfixes, console._gets bugfix, label, disasm.rb, to_int32 bugfix
  • Loading branch information...
1 parent bfc1df0 commit 3ebd43ef672d3778912003135e88d4e94a96db0f @charliesome committed Jan 4, 2012
View
@@ -7,13 +7,16 @@
compiler = Twostroke::Compiler::TSASM.new parser.statements
compiler.compile
-#=begin
-compiler.bytecode.each do |section,instructions|
- puts "#{section}:"
- instructions.each_with_index do |ins,offset|
- puts "#{sprintf "%4d", offset} #{ins[0]}#{" " * (12 - ins[0].size)}#{ins.drop(1).map { |x| x.is_a?(String) ? x.inspect : x }.join ", "}"
+if ARGV.include? "-S"
+ compiler.bytecode.each do |section,instructions|
+ puts "#{section}:"
+ instructions.each_with_index do |ins,offset|
+ puts "#{sprintf "%4d", offset} #{ins[0]}#{" " * (12 - ins[0].size)}#{ins.drop(1).map { |x| x.is_a?(String) ? x.inspect : x }.join ", "}"
+ end
end
-end
-#=end
-
-#print compiler.tscode
+else
+ File.open ARGV.first.gsub(/\.js$/, ".ts"), "w" do |f|
+ compiler.bytecode.default = nil
+ f.write Marshal.dump compiler.bytecode
+ end
+end
View
@@ -0,0 +1,9 @@
+$LOAD_PATH << File.expand_path("../lib", __FILE__)
+require "twostroke"
+
+Marshal.load(File.read ARGV.first).each do |section,instructions|
+ puts "#{section}:"
+ instructions.each_with_index do |ins,offset|
+ puts "#{sprintf "%4d", offset} #{ins[0]}#{" " * (12 - ins[0].size)}#{ins.drop(1).map { |x| x.is_a?(String) ? x.inspect : x }.join ", "}"
+ end
+end
@@ -1,6 +1,6 @@
module Twostroke::AST
class Function < Base
- attr_accessor :name, :arguments, :statements
+ attr_accessor :name, :arguments, :statements, :fnid
def initialize(*args)
@arguments = []
@@ -9,7 +9,7 @@ def initialize(*args)
end
def collapse
- self.class.new name: name, arguments: arguments, statements: statements.reject(&:nil?).map(&:collapse)
+ self.class.new name: name, arguments: arguments, statements: statements.reject(&:nil?).map(&:collapse), fnid: fnid
end
def walk(&bk)
@@ -0,0 +1,20 @@
+module Twostroke::AST
+ class Label < Base
+ attr_accessor :name, :statement
+
+ def initialize(*args)
+ @statements = []
+ super *args
+ end
+
+ def collapse
+ self.class.new name: name, statement: statement
+ end
+
+ def walk(&bk)
+ if yield self
+ statement.walk &bk
+ end
+ end
+ end
+end
@@ -1,5 +1,5 @@
module Twostroke::Compiler
- class CompileError < Twostroke::Error
+ class CompileError < SyntaxError
end
Dir.glob File.expand_path("../compiler/*", __FILE__) do |f|
@@ -66,8 +66,11 @@ def hoist(node)
node.walk do |node|
if node.is_a? Twostroke::AST::Declaration
output :".local", node.name.intern
+ false
elsif node.is_a? Twostroke::AST::Function
output :".local", node.name.intern if node.name
+ # because javascript is odd, entire function bodies need to be hoisted, not just their declarations
+ Function(node, true)
false
else
true
@@ -275,27 +278,30 @@ def Regexp(node)
output :regexp, node.regexp
end
- def Function(node)
- fnid = :"#{@prefix}fn_#{uniqid}"
+ def Function(node, in_hoist_stage = false)
+ fnid = node.fnid ||= :"#{@prefix}fn_#{uniqid}"
- section fnid
- output :".name", node.name if node.name
- node.arguments.each do |arg|
- output :".arg", arg.intern
- end
- output :".local", node.name.intern if node.name
- node.statements.each { |s| hoist s }
- if node.name
- output :callee
- output :set, node.name.intern
+ if !node.name or in_hoist_stage
+ section fnid
+ output :".name", node.name if node.name
+ node.arguments.each do |arg|
+ output :".arg", arg.intern
+ end
+ output :".local", node.name.intern if node.name
+ node.statements.each { |s| hoist s }
+ if node.name
+ output :callee
+ output :set, node.name.intern
+ end
+ node.statements.each { |s| compile s }
+ output :undefined
+ output :ret
+ pop_section
+ output :close, fnid
+ output :set, node.name.intern if node.name
+ else
+ output :close, fnid
end
- node.statements.each { |s| compile s }
- output :undefined
- output :ret
- pop_section
-
- output :close, fnid
- output :set, node.name.intern if node.name
end
def Declaration(node)
@@ -562,10 +568,12 @@ def Not(node)
end
def Break(node)
+ raise Twostroke::Compiler::CompileError, "Break not allowed outside of loop" unless @break_stack.any?
output :jmp, @break_stack.last
end
def Continue(node)
+ raise Twostroke::Compiler::CompileError, "Continue not allowed outside of loop" unless @continue_stack.any?
output :jmp, @continue_stack.last
end
@@ -6,7 +6,7 @@ module Twostroke::Runtime
["log", "info", "warn", "error"].each do |m|
console.proto_put m, log
end
- console.proto_put "_gets", Types::Function.new(->(scope, this, args) { Types::String.new gets }, nil, "_gets", [])
+ console.proto_put "_gets", Types::Function.new(->(scope, this, args) { Types::String.new STDIN.gets }, nil, "_gets", [])
console.proto_put "_print", Types::Function.new(->(scope, this, args) { print args.map { |o| Types.to_string(o).string }.join(" ") }, nil, "_print", ["string"])
scope.set_var "console", console
end
@@ -23,6 +23,15 @@ module Twostroke::Runtime
Lib.throw_type_error "RegExp.prototype.test is not generic" unless this.is_a?(Types::RegExp)
Types::Boolean.new((Types.to_string(args[0] || Undefined.new).string =~ this.regexp) != nil)
}, nil, "test", [])
+ proto.proto_put "exec", Types::Function.new(->(scope, this, args) {
+ re = this.is_a?(Types::RegExp) ? this : Types::RegExp.constructor_function.(nil, nil, this)
+ str = Types.to_string(args[0] || Types::Undefined.new).string
+ md = re.regexp.match str
+ result = Types::Array.new md.to_a.map { |s| Types::String.new s }
+ result.put "index", Types::Number.new(md.offset(0).first)
+ result.put "input", Types::String.new(str)
+ result
+ }, nil, "exec", [])
regexp.proto_put "prototype", proto
end
end
@@ -52,31 +52,33 @@ module Twostroke::Runtime
Types::String.new retn
}, nil, "replace", [])
-=begin
- find = args[0] || Types::Undefined.new
- find = Types.to_string(find).string unless find.is_a?(Types::RegExp)
- replace = args[1] || Types::Undefined.new
- replace = Types.to_string(replace).string unless replace.respond_to? :call
- Types::String.new(if find.is_a?(String)
- if replace.is_a?(String)
- s.sub find, replace
- else
- s.sub(find) { |m| Types.to_string(replace.call(scope, nil, [Types::String.new(m), Types::Number.new(s.index m), sobj])).string }
- end
+ # String.prototype.slice
+ proto.proto_put "slice", Types::Function.new(->(scope, this, args) {
+ sobj = Types.to_string(this)
+ s = sobj.string
+ if args[0].nil?
+ sobj
else
- m = s.method(find.global ? :gsub : :sub)
- if replace.is_a?(String)
- m.(find.regexp, replace)
+ start = Types.to_int32 args[0]
+ if args[1]
+ fin = Types.to_int32 args[1]
+ Types::String.new s[start...fin]
else
- offset = 0
- m.(find.regexp) do |m|
- idx = s.index m, offset
- offset = idx + m.size
- Types.to_string(replace.call(scope, nil, [Types::String.new(m), Types::Number.new(idx), sobj])).string
- end
+ Types::String.new s[start..-1]
end
- end)
-=end
+ end
+ }, nil, "slice", [])
+ # String.prototype.match
+ proto.proto_put "match", Types::Function.new(->(scope, this, args) {
+ re = args[0] || Types::Undefined.new
+ re = Types::RegExp.constructor_function.(nil, nil, re) unless re.is_a?(Types::RegExp)
+ unless re.global
+ # same as re.exec(str) in this case
+
+ else
+ #
+ end
+ }, nil, "match", [])
obj.proto_put "prototype", proto
obj.proto_put "fromCharCode", Types::Function.new(->(scope, this, args) {
@@ -44,7 +44,7 @@ def self.to_int32(object)
0
else
int32 = num.number.to_i & 0xffff_ffff
- int32 -= 2 ** 31 if int32 >= 2 ** 31
+ int32 -= 2 ** 32 if int32 >= 2 ** 31
int32
end
end
@@ -3,7 +3,7 @@ class RegExp < Object
def self.constructor_function
@@constructor_function ||=
Function.new(->(scope, this, args) {
- RegExp.new Twostroke::Runtime::Types.to_string(args[0] || Undefined.new).string, args[1] && Twostroke::Runtime::Types.to_string(args[1]).string
+ RegExp.new((args[0] && !args[0].is_a?(Undefined)) ? Twostroke::Runtime::Types.to_string(args[0]).string : "", args[1] && Twostroke::Runtime::Types.to_string(args[1]).string)
}, nil, "RegExp", [])
end
@@ -35,11 +35,12 @@ def execute(scope, this = nil, args = [])
end
until @return
- ins, arg = *insns[ip]
+ ins, arg = insns[ip]
st = @stack.size
@ip += 1
if respond_to? ins
if @exception = catch(:exception) { public_send ins, arg; nil }
+ puts "--> #{Types.to_string(@exception).string} - #{@section}+#{@ip}"
throw :exception, @exception if catch_stack.empty? && finally_stack.empty?
if catch_stack.any?
@ip = catch_stack.last
@@ -174,7 +175,7 @@ def set(arg)
def setprop(arg)
val = stack.pop
- obj = stack.pop
+ obj = Types.to_object(stack.pop)
obj.put arg.to_s, val
stack.push val
end
View
@@ -1,13 +1,19 @@
$LOAD_PATH << File.expand_path("../lib", __FILE__)
require "twostroke"
-parser = Twostroke::Parser.new(Twostroke::Lexer.new(File.read ARGV.first))
-parser.parse
+src = File.read ARGV.first
+begin
+ bytecode = Marshal.load src
+rescue
+ parser = Twostroke::Parser.new Twostroke::Lexer.new src
+ parser.parse
-compiler = Twostroke::Compiler::TSASM.new parser.statements
-compiler.compile
+ compiler = Twostroke::Compiler::TSASM.new parser.statements
+ compiler.compile
+ bytecode = compiler.bytecode
+end
-vm = Twostroke::Runtime::VM.new compiler.bytecode
+vm = Twostroke::Runtime::VM.new bytecode
Twostroke::Runtime::Lib.setup_environment vm
if ARGV.include? "--pry"
Oops, something went wrong.

0 comments on commit 3ebd43e

Please sign in to comment.