Skip to content

Commit

Permalink
Initial commit on reworking parser into generator nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
adambeynon committed Oct 21, 2013
1 parent 443adc1 commit e439800
Show file tree
Hide file tree
Showing 8 changed files with 606 additions and 345 deletions.
6 changes: 6 additions & 0 deletions lib/opal/nodes.rb
@@ -0,0 +1,6 @@
require 'opal/nodes/base'
require 'opal/nodes/literal'
require 'opal/nodes/variables'
require 'opal/nodes/constants'
require 'opal/nodes/logic'
require 'opal/nodes/definitions'
110 changes: 110 additions & 0 deletions lib/opal/nodes/base.rb
@@ -0,0 +1,110 @@
module Opal
class Parser
class Node

This comment has been minimized.

Copy link
@elia

elia Oct 21, 2013

Member

I though we were against the Nodez…

This comment has been minimized.

Copy link
@adambeynon

adambeynon Oct 21, 2013

Author Contributor

At the moment a sexp is just a generic class. Here I am starting to separate out each sexp into its own class for code generation. Eventually s(:type, ...) will be removed and the parser will generate these target nodes instead of a generic sexp.

Even further down the line we will compile all ruby nodes into javascript AST nodes, which will then compile themselves into javascript.

But, for now Nodes will just be a replacement for Sexps - even though they will act the same.

This comment has been minimized.

Copy link
@peter-leonov

peter-leonov Oct 21, 2013

Contributor

👍 👍 👍 👍 👍 !!!

def initialize(sexp, level, parser)
@sexp = sexp
@level = level
@parser = parser
end

def type
@sexp.type
end

def children
@sexp[1..-1]
end

def compile_to_fragments
return @fragments if @fragments

@fragments = []
self.compile
@fragments
end

def compile
raise "Not Implemented"
end

def push(str)
str = fragment(str) if str.is_a?(String)
@fragments << str
end

def unshift(str)
str = fragment(str) if str.is_a?(String)
@fragments.unshift str
end

def wrap(pre, post)
unshift pre
push post
end

def fragment(str)
Opal::Parser::Fragment.new str, @sexp
end

def scope
@parser.scope
end

def s(*args)
@parser.s(*args)
end

def expr(sexp)
@parser.process sexp, :expr
end

def recv(sexp)
@parser.process sexp, :recv
end

def stmt(sexp)
@parser.process sexp, :stmt
end

def expr_or_nil(sexp)
sexp ? expr(sexp) : "nil"
end

def reserved?(name)
Opal::Parser::RESERVED.include? name
end

def property(name)
reserved?(name) ? "['#{name}']" : ".#{name}"
end

def variable(name)
reserved?(name) ? "#{name}$" : name
end

def add_local(name)
scope.add_local name.to_sym
end

def add_ivar(name)
scope.add_ivar name
end

def add_temp(temp)
scope.add_temp temp
end

def helper(name)
@parser.instance_variable_get(:@helpers)[name] = true
end

def with_temp(&block)
@parser.with_temp(&block)
end

def in_while?
@parser.in_while?
end
end
end
end
83 changes: 83 additions & 0 deletions lib/opal/nodes/constants.rb
@@ -0,0 +1,83 @@
require 'opal/nodes/base'

module Opal
class Parser
class ConstNode < Node
def initialize(*)
super
@const_missing = true
end

def compile
name = @sexp[1]

if @const_missing
with_temp do |tmp|
push "((#{tmp} = $scope.#{name}) == null ? $opal.cm('#{name}') : #{tmp})"
end
else
push "$scope.#{name}"
end
end
end

class CdeclNode < Node
def compile
push expr(@sexp[2])
wrap "$opal.cdecl($scope, '#{@sexp[1]}', ", ")"
end
end

class CasgnNode < Node
def compile
push "$opal.casgn("
push expr(@sexp[1])
push ", '#{@sexp[2]}', "
push expr(@sexp[3])
push ")"
end
end

class Colon2Node < Node
def initialize(*)
super
@const_missing = true
end

def lhs
expr @sexp[1]
end

def compile
if @const_missing
with_temp do |tmp|
push "((#{tmp} = ("
push lhs
push ")._scope).#{@sexp[2]} == null ? #{tmp}.cm('#{@sexp[2]}') : "
push "#{tmp}.#{@sexp[2]})"
end
else
push lhs
wrap '(', ")._scope.#{@sexp[1]}"
end
end
end

class Colon3Node < Node
def compile
with_temp do |tmp|
push "((#{tmp} = $opal.Object._scope.#{@sexp[1]}) == null ? "
push "$opal.cm('#{@sexp[1]}') : #{tmp})"
end
end
end

class Casgn3Node < Node
def compile
push "$opal.casgn($opal.Object, '#{@sexp[1]}', "
push expr(@sexp[2])
push ")"
end
end
end
end
35 changes: 35 additions & 0 deletions lib/opal/nodes/definitions.rb
@@ -0,0 +1,35 @@
require 'opal/nodes/base'

module Opal
class Parser

class BaseScopeNode < Node
def in_scope(type, &block)
@parser.in_scope(type, &block)
end
end

class SClassNode < BaseScopeNode
def compile
in_scope(:sclass) do
add_temp '$scope = self._scope'
add_temp 'def = self._proto'

push scope.to_vars
push stmt(@sexp[2])
end

push "})("
push recv(@sexp[1])
wrap "(function(self) {", ".$singleton_class())"
end
end

class UndefNode < Node
# FIXME: we should be setting method to a stub method here
def compile
push "delete #{scope.proto}#{@parser.mid_to_jsid @sexp[1][1].to_s}"
end
end
end
end
127 changes: 127 additions & 0 deletions lib/opal/nodes/literal.rb
@@ -0,0 +1,127 @@
require 'opal/nodes/base'

module Opal
class Parser
class ValueNode < Node
def compile
push type.to_s
end
end

class LiteralNode < Node
def literal
@sexp[1]
end
end

class NumericNode < LiteralNode
def compile
push literal.to_s
wrap '(', ')' if @level == :recv
end
end

class StringNode < LiteralNode
def compile
push literal.inspect
end
end

class SymbolNode < LiteralNode
def compile
push literal.to_s.inspect
end
end

class RegexpNode < LiteralNode
def compile
push((literal == // ? /^/ : literal).inspect)
end
end

class DynamicStringNode < Node
def compile
children.each_with_index do |part, idx|
push " + " unless idx == 0

if String === part
push part.inspect
elsif part.type == :evstr
push "("
push expr(part[1])
push ")"
elsif part.type == :str
push part[1].inspect
else
raise "Bad dstr part"
end

wrap '(', ')' if @level == :recv
end
end
end

class DynamicSymbolNode < Node
def compile
children.each_with_index do |part, idx|
push " + " unless idx == 0

if String === part
push part.inspect
elsif part.type == :evstr
push expr(s(:call, part.last, :to_s, s(:arglist)))
elsif part.type == :str
push part.last.inspect
else
raise "Bad dsym part"
end
end

wrap '(', ')'
end
end

class DynamicRegexpNode < Node
def compile
children.each_with_index do |part, idx|
push " + " unless idx == 0

if String === part
push part.inspect
elsif part.type == :str
push part[1].inspect
else
push expr(part[1])
end
end

wrap '(new RegExp(', '))'
end
end

class ExclusiveRangeNode < Node
def compile
helper :range

push "$range("
push expr(@sexp[1])
push ", "
push expr(@sexp[2])
push ", false)"
end
end

class InclusiveRangeNode < Node
def compile
helper :range

push "$range("
push expr(@sexp[1])
push ", "
push expr(@sexp[2])
push ", true)"
end
end

end
end

0 comments on commit e439800

Please sign in to comment.