Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Start converting to a Citrus parser.

  • Loading branch information...
commit 1fc77caf83752eb035dcce1c98e5f23194fd7064 1 parent 105812f
@matthewd authored
View
5 bin/capuchin
@@ -18,6 +18,11 @@ ARGV.each do |arg|
# SunSpider compatibility
next if arg == '-f'
+ if arg == '-d'
+ cx.debug = true
+ next
+ end
+
if arg == '-e'
next_is_expr = true
next
View
1  eg/obj.js
@@ -3,6 +3,7 @@ var o = { a: 1, b: 2, c: 3, x: function() { p(this); print('xx'); } };
print(o.a);
o.d = 4;
print(o.d);
+print(o['d'+'']);
p(o);
View
2  lib/capuchin.rb
@@ -1,7 +1,7 @@
module Capuchin
end
-require "rkelly"
+require "capuchin/parser"
require "capuchin/kernel"
require "capuchin/generator"
require "capuchin/nodes"
View
33 lib/capuchin/context.rb
@@ -3,26 +3,28 @@ class Capuchin::Context
def initialize(debug=$DEBUG)
@debug = debug
- @parser = RKelly::Parser.new
- class << @parser
- # A mostly-futile attempt to get the parser to tell us why it fails
- def yyerror; yyabort; end
- def yyabort
- raise ParseError, sprintf("\nparse error on value %s (%s)",
- @racc_val.inspect, token_to_str(@racc_t) || '?')
- end
- end
+ #@parser = RKelly::Parser.new
+ #class << @parser
+ # # A mostly-futile attempt to get the parser to tell us why it fails
+ # def yyerror; yyabort; end
+ # def yyabort
+ # raise ParseError, sprintf("\nparse error on value %s (%s)",
+ # @racc_val.inspect, token_to_str(@racc_t) || '?')
+ # end
+ #end
+ @parser = Capuchin::Parser
end
+ attr_accessor :debug
def parse_expression(expression, filename=nil)
- ast = @parser.parse(expression, filename)
+ ast = @parser.parse(expression, :consume => true, :filename => filename)
raise "Parse of #{filename ? filename.inspect : 'expression'} failed :(" if ast.nil?
- Rubinius::AST::AsciiGrapher.new(ast, RKelly::Nodes::Node).print if @debug
+ Rubinius::AST::AsciiGrapher.new(ast, Capuchin::Nodes::Node).print if @debug
ast
end
def parse(filename)
- ast = @parser.parse(File.read(filename), filename)
+ ast = @parser.parse(File.read(filename), :consume => true, :filename => filename)
raise "Parse of #{filename.inspect} failed :(" if ast.nil?
- Rubinius::AST::AsciiGrapher.new(ast, RKelly::Nodes::Node).print if @debug
+ Rubinius::AST::AsciiGrapher.new(ast, Capuchin::Nodes::Node).print if @debug
ast
end
def load(filename)
@@ -30,6 +32,11 @@ def load(filename)
code = compile(ast, filename)
code.call
end
+ def eval(expression, filename='(eval)')
+ ast = parse_expression(expression, filename)
+ code = compile(ast, filename)
+ code.call
+ end
def compile(ast, filename)
code = Object.new
View
8 lib/capuchin/kernel.rb
@@ -471,3 +471,11 @@ def -(other)
(done - start) * 1000
end
+# This isn't a real solution; eval needs to be a compiler construct, so
+# it gets the containing scope's variables, etc.
+Capuchin::Globals[:eval] = Capuchin::Function.new do |src|
+ Capuchin::Context.new.eval(src)
+end
+
+Capuchin::Globals[:version] = Capuchin::Function.new {|x| }
+
View
38 lib/capuchin/nodes.rb
@@ -13,7 +13,8 @@
# block[n] -- given the number of "internal" stack entries have been
# added; these should be undisturbed when the block is finished.
-class RKelly::Nodes::Node
+module Capuchin::Nodes
+class Node
def call_bytecode(v, g)
v.pos(self)
v.accept(self)
@@ -22,7 +23,7 @@ def call_bytecode(v, g)
g.send :js_call, arg_count + 1
end
end
-class RKelly::Nodes::DotAccessorNode
+class DotAccessorNode
def get_bytecode(v, g)
v.pos(self)
v.accept self.value
@@ -54,14 +55,14 @@ def call_bytecode(v, g)
g.send :js_invoke, arg_count + 1
end
end
-class RKelly::Nodes::BracketAccessorNode
+class BracketAccessorNode
def get_bytecode(v, g)
v.pos(self)
v.accept self.value
v.accept self.accessor
- unless key_safe?(o.left.accessor)
- pos(o.left.accessor)
- @g.send :js_key, 0
+ unless v.key_safe?(self.accessor)
+ v.pos(self.accessor)
+ g.send :js_key, 0
end
g.send :js_get, 1
end
@@ -69,9 +70,9 @@ def set_bytecode(v, g)
v.pos(self)
v.accept self.value
v.accept self.accessor
- unless key_safe?(o.left.accessor)
- pos(o.left.accessor)
- @g.send :js_key, 0
+ unless v.key_safe?(self.accessor)
+ v.pos(self.accessor)
+ g.send :js_key, 0
end
yield 2
g.send :js_set, 2
@@ -80,9 +81,9 @@ def get_and_set_bytecode(v, g)
v.pos(self)
v.accept self.value
v.accept self.accessor
- unless key_safe?(o.left.accessor)
- pos(o.left.accessor)
- @g.send :js_key, 0
+ unless v.key_safe?(self.accessor)
+ v.pos(self.accessor)
+ g.send :js_key, 0
end
g.dup_many 2
g.send :js_get, 1
@@ -94,15 +95,15 @@ def call_bytecode(v, g)
v.accept self.value
v.pos(self)
v.accept self.accessor
- unless key_safe?(o.left.accessor)
- pos(o.left.accessor)
- @g.send :js_key, 0
+ unless v.key_safe?(self.accessor)
+ v.pos(self.accessor)
+ g.send :js_key, 0
end
arg_count = yield(2)
g.send :js_invoke, arg_count + 1
end
end
-class RKelly::Nodes::ResolveNode
+class ResolveNode
def get_bytecode(v, g)
v.pos(self)
if ref = g.state.scope.search_local(self.value.to_sym)
@@ -124,7 +125,7 @@ def set_bytecode(v, g)
g.find_const :Globals
g.push_literal self.value.to_sym
yield 2
- g.send :[]=, 1
+ g.send :[]=, 2
end
end
def get_and_set_bytecode(v, g)
@@ -140,8 +141,9 @@ def get_and_set_bytecode(v, g)
g.dup_many 2
g.send :[], 1
yield 2
- g.send :[]=, 1
+ g.send :[]=, 2
end
end
end
+end
View
534 lib/capuchin/parser.citrus
@@ -0,0 +1,534 @@
+
+grammar Capuchin::Parser
+ rule source_file
+ SP? source_elements SP?
+ end
+
+ rule source_elements
+ source_element*
+ end
+
+ rule source_element
+ function_declaration | statement
+ end
+
+ rule statement
+ (block
+ |variable_statement
+ |const_statement
+ |empty_statement
+ |expr_statement
+ |if_statement
+ |iteration_statement
+ |continue_statement
+ |break_statement
+ |return_statement
+ |with_statement
+ |switch_statement
+ |labelled_statement
+ |throw_statement
+ |try_statement
+ )
+ end
+
+ rule literal
+ ('null'
+ |'true'
+ |'false'
+ |number
+ |string
+ |regexp
+ )
+ end
+
+ rule property
+ (ident SP? ':' SP? assignment_expr
+ |string SP? ':' SP? assignment_expr
+ |number SP? ':' SP? assignment_expr
+ |ident SP ident SP? '(' SP? formal_parameter_list? SP? ')' SP? '{' SP? function_body SP? '}'
+ )
+ end
+
+ rule property_list
+ (property SP? ',' SP?)* property
+ end
+
+ rule primary_expr
+ (primary_expr_no_brace
+ |'{' SP? (property_list SP? (',' SP?)?)? '}'
+ )
+ end
+
+ rule primary_expr_no_brace
+ ('this'
+ |literal
+ |array_literal
+ |ident
+ |'(' SP? expr SP? ')'
+ )
+ end
+
+ rule array_literal
+ '[' SP? element_list? SP? elision? SP? ']'
+ end
+
+ rule element_list
+ (elision SP?)? (assignment_expr SP? ',' SP? (elision SP?)?)* assignment_expr
+ end
+
+ rule elision
+ ',' (SP? ',')*
+ end
+
+ rule member_expr
+ (primary_expr
+ |function_expr
+ |'new' SP member_expr SP? arguments
+ )
+ (SP? '[' SP? expr SP? ']'
+ |SP? '.' SP? ident
+ )+
+ end
+
+ rule member_expr_no_bf
+ (primary_expr_no_brace
+ |'new' SP member_expr SP? arguments
+ )
+ (SP? '[' SP? expr SP? ']'
+ |SP? '.' SP? ident
+ )+
+ end
+
+ rule new_expr
+ ('new' SP new_expr
+ |member_expr
+ )
+ end
+
+ rule new_expr_no_bf
+ ('new' SP new_expr
+ |member_expr_no_bf
+ )
+ end
+
+ rule call_expr
+ member_expr SP? arguments
+ (SP? call_expr SP?
+ (arguments
+ |'[' SP? expr SP? ']'
+ |'.' SP? ident
+ )
+ )*
+ end
+
+ rule call_expr_no_bf
+ member_expr_no_bf SP? arguments
+ (SP? call_expr SP?
+ (arguments
+ |'[' SP? expr SP? ']'
+ |'.' SP? ident
+ )
+ )*
+ end
+
+ rule arguments
+ '(' SP? argument_list? SP? ')'
+ end
+
+ rule argument_list
+ assignment_expr (SP? ',' SP? assignment_expr)*
+ end
+
+ rule left_hand_side_expr
+ (new_expr
+ |call_expr
+ )
+ end
+
+ rule left_hand_side_expr_no_bf
+ (new_expr_no_bf
+ |call_expr_no_bf
+ )
+ end
+
+ rule postfix_expr
+ left_hand_side_expr (SP? '++' | SP? '--')?
+ end
+
+ rule postfix_expr_no_bf
+ left_hand_side_expr_no_bf (SP? '++' | SP? '--')?
+ end
+
+ rule unary_expr_common
+ ('delete' SP unary_expr
+ |'void' SP unary_expr
+ |'typeof' SP unary_expr
+ |'++' SP? unary_expr
+ |'--' SP? unary_expr
+ |'+' SP? unary_expr
+ |'-' SP? unary_expr
+ |'~' SP? unary_expr
+ |'!' SP? unary_expr
+ )
+ end
+
+ rule unary_expr
+ (unary_expr_common
+ |postfix_expr
+ )
+ end
+
+ rule unary_expr_no_bf
+ (unary_expr_common
+ |postfix_expr_no_bf
+ )
+ end
+
+ rule multiplicative_expr
+ unary_expr (SP? ('*' SP? unary_expr
+ |'/' SP? unary_expr
+ |'%' SP? unary_expr
+ )
+ )*
+ end
+ rule multiplicative_expr_no_bf
+ unary_expr_no_bf (SP? ('*' SP? unary_expr
+ |'/' SP? unary_expr
+ |'%' SP? unary_expr
+ )
+ )*
+ end
+
+ rule additive_expr
+ multiplicative_expr (SP? ('+' SP? multiplicative_expr
+ |'-' SP? multiplicative_expr
+ )
+ )*
+ end
+ rule additive_expr_no_bf
+ multiplicative_expr_no_bf (SP? ('+' SP? multiplicative_expr
+ |'-' SP? multiplicative_expr
+ )
+ )*
+ end
+
+ rule shift_expr
+ additive_expr (SP? ('<<' SP? additive_expr
+ |'>>' SP? additive_expr
+ |'>>>' SP? additive_expr
+ )
+ )*
+ end
+ rule shift_expr_no_bf
+ additive_expr_no_bf (SP? ('<<' SP? additive_expr
+ |'>>' SP? additive_expr
+ |'>>>' SP? additive_expr
+ )
+ )*
+ end
+
+ rule relational_expr
+ shift_expr (SP? '<' SP? shift_expr
+ |SP? '>' SP? shift_expr
+ |SP? '<=' SP? shift_expr
+ |SP? '>=' SP? shift_expr
+ |SP 'instanceof' SP shift_expr
+ |SP 'in' SP shift_expr
+ )*
+ end
+ rule relational_expr_no_in
+ shift_expr_no_in (SP? '<' SP? shift_expr
+ |SP? '>' SP? shift_expr
+ |SP? '<=' SP? shift_expr
+ |SP? '>=' SP? shift_expr
+ |SP 'instanceof' SP shift_expr
+ )*
+ end
+ rule relational_expr_no_bf
+ shift_expr_no_bf (SP? '<' SP? shift_expr
+ |SP? '>' SP? shift_expr
+ |SP? '<=' SP? shift_expr
+ |SP? '>=' SP? shift_expr
+ |SP 'instanceof' SP shift_expr
+ |SP 'in' SP shift_expr
+ )*
+ end
+
+ rule equality_expr
+ relational_expr (SP? ('==' SP? relational_expr
+ |'!=' SP? relational_expr
+ |'===' SP? relational_expr
+ |'!==' SP? relational_expr
+ )
+ )*
+ end
+ rule equality_expr_no_in
+ relational_expr_no_in (SP? ('==' SP? relational_expr_no_in
+ |'!=' SP? relational_expr_no_in
+ |'===' SP? relational_expr_no_in
+ |'!==' SP? relational_expr_no_in
+ )
+ )*
+ end
+ rule equality_expr_no_bf
+ relational_expr_no_bf (SP? ('==' SP? relational_expr
+ |'!=' SP? relational_expr
+ |'===' SP? relational_expr
+ |'!==' SP? relational_expr
+ )
+ )*
+ end
+
+ rule bitwise_and_expr
+ equality_expr (SP? '&' SP? equality_expr)*
+ end
+ rule bitwise_and_expr_no_in
+ equality_expr_no_in (SP? '&' SP? equality_expr_no_in)*
+ end
+ rule bitwise_and_expr_no_bf
+ equality_expr_no_bf (SP? '&' SP? equality_expr)*
+ end
+
+ rule bitwise_xor_expr
+ bitwise_and_expr (SP? '^' SP? bitwise_and_expr)*
+ end
+ rule bitwise_xor_expr_no_in
+ bitwise_and_expr_no_in (SP? '^' SP? bitwise_and_expr_no_in)*
+ end
+ rule bitwise_xor_expr_no_bf
+ bitwise_and_expr_no_bf (SP? '^' SP? bitwise_and_expr)*
+ end
+
+ rule bitwise_or_expr
+ bitwise_xor_expr (SP? '|' SP? bitwise_xor_expr)*
+ end
+ rule bitwise_or_expr_no_in
+ bitwise_xor_expr_no_in (SP? '|' SP? bitwise_xor_expr_no_in)*
+ end
+ rule bitwise_or_expr_no_bf
+ bitwise_xor_expr_no_bf (SP? '|' SP? bitwise_xor_expr)*
+ end
+
+ rule logical_and_expr
+ bitwise_or_expr (SP? '&&' SP? bitwise_or_expr)*
+ end
+ rule logical_and_expr_no_in
+ bitwise_or_expr_no_in (SP? '&&' SP? bitwise_or_expr_no_in)*
+ end
+ rule logical_and_expr_no_bf
+ bitwise_or_expr_no_bf (SP? '&&' SP? bitwise_or_expr)*
+ end
+
+ rule logical_or_expr
+ logical_and_expr (SP? '||' SP? logical_and_expr)*
+ end
+ rule logical_or_expr_no_in
+ logical_and_expr_no_in (SP? '||' SP? logical_and_expr_no_in)*
+ end
+ rule logical_or_expr_no_bf
+ logical_and_expr_no_bf (SP? '||' SP? logical_and_expr)*
+ end
+
+ rule conditional_expr
+ logical_or_expr (SP? '?' SP? assignment_expr SP? ':' SP? assignment_expr)*
+ end
+ rule conditional_expr_no_in
+ logical_or_expr_no_in (SP? '?' SP? assignment_expr_no_in SP? ':' SP? assignment_expr_no_in)*
+ end
+ rule conditional_expr_no_bf
+ logical_or_expr_no_bf (SP? '?' SP? assignment_expr SP? ':' SP? assignment_expr)*
+ end
+
+ rule assignment_expr
+ (left_hand_side_expr SP? assignment_operator SP?)* conditional_expr
+ end
+ rule assignment_expr_no_in
+ (left_hand_side_expr SP? assignment_operator SP?)* conditional_expr_no_in
+ end
+ rule assignment_expr_no_bf
+ (left_hand_side_expr_no_bf SP? assignment_operator SP?)? assignment_expr
+ end
+
+ rule assignment_operator
+ ('='
+ |'+='
+ |'-='
+ |'*='
+ |'/='
+ |'<<='
+ |'>>='
+ |'>>>='
+ |'&='
+ |'^='
+ |'|='
+ |'%='
+ )
+ end
+
+ rule expr
+ assignment_expr (SP? ',' SP? assignment_expr)*
+ end
+ rule expr_no_in
+ assignment_expr_no_in (SP? ',' SP? assignment_expr_no_in)*
+ end
+ rule expr_no_bf
+ assignment_expr_no_bf (SP? ',' SP? assignment_expr)*
+ end
+
+ rule block
+ '{' SP? source_elements SP? '}'
+ end
+
+ rule variable_statement
+ 'var' SP variable_declaration_list SP? (';' | error)
+ end
+
+ rule variable_declaration_list
+ variable_declaration (SP? ',' SP? variable_declaration)*
+ end
+ rule variable_declaration_list_no_in
+ variable_declaration_no_in (SP? ',' SP? variable_declaration_no_in)*
+ end
+
+ rule variable_declaration
+ ident (SP? '=' SP? assignment_expr)?
+ end
+ rule variable_declaration_no_in
+ ident (SP? '=' SP? assignment_expr_no_in)?
+ end
+
+ rule const_statement
+ 'const' SP const_declaration_list SP? (';' | error)
+ end
+ rule const_declaration_list
+ (const_declaration SP? ',' SP?)* const_declaration
+ end
+ rule const_declaration
+ ident (SP? '=' SP? assignment_expr)?
+ end
+
+ rule empty_statement
+ ';'
+ end
+
+ rule expr_statement
+ expr_no_bf SP? (';' | error)
+ end
+
+ rule if_statement
+ 'if' SP? '(' SP? expr SP? ')' SP? statement (SP? 'else' SP? statement)?
+ end
+
+ rule iteration_statement
+ ('do' SP? statement SP? 'while' SP? '(' SP? expr SP? ')' SP? (';' | error)
+ |'while' SP? '(' SP? expr SP? ')' SP? statement
+ |'for' SP? '(' SP? (expr_no_in SP?)? ';' SP? (expr SP?)? ';' SP? (expr SP?)? ')' SP? statement
+ |'for' SP? '(' SP? 'var' SP variable_declaration_list_no_in SP? ';' SP? (expr SP?)? ';' SP? (expr SP?)? ')' SP? statement
+ |'for' '(' left_hand_side_expr SP 'in' SP (expr SP?)? ')' SP? statement
+ |'for' '(' 'var' SP ident SP 'in' SP (expr SP?)? ')' SP? statement
+ |'for' '(' 'var' SP ident SP? '=' SP? assignment_expr_no_in SP 'in' SP (expr SP?)? ')' SP? statement
+ )
+ end
+
+ rule continue_statement
+ 'continue' (SP ident)? SP? (';' | error)
+ end
+ rule break_statement
+ 'break' (SP ident)? SP? (';' | error)
+ end
+ rule return_statement
+ 'return' SP? (expr SP?) (';' | error)
+ end
+
+ rule with_statement
+ 'with' SP? '(' SP? expr SP? ')' SP? statement
+ end
+
+ rule switch_statement
+ 'switch' SP? '(' SP? expr SP? ')' SP? case_block
+ end
+
+ rule case_block
+ '{' SP? case_clause* (default_clause case_clause*)? '}'
+ end
+ rule case_clause
+ 'case' SP? expr SP? ':' SP? source_elements SP?
+ end
+ rule case_clause
+ 'default' SP? ':' SP? source_elements SP?
+ end
+
+ rule labelled_statement
+ ident SP? ':' SP? statement
+ end
+
+ rule throw_statement
+ 'throw' SP? expr SP? (';' | error)
+ end
+
+ rule try_statement
+ 'try' SP? block SP? ('finally' SP? block
+ |'catch' SP? '(' SP? ident SP? ')' SP? block (SP? 'finally' SP? block)?
+ )
+ end
+
+ rule function_declaration
+ 'function' SP ident SP? '(' SP? (formal_parameter_list SP?)? ')' SP? '{' SP? function_body SP? '}'
+ end
+ rule function_expr
+ 'function' (SP ident)? SP? '(' SP? (formal_parameter_list SP?)? ')' SP? '{' SP? function_body SP? '}'
+ end
+
+ rule formal_parameter_list
+ ident (SP? ',' SP? ident)*
+ end
+
+ rule function_body
+ source_elements
+ end
+
+
+ rule string
+ /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/m
+ end
+
+ rule number
+ float | integer
+ end
+ rule float
+ /\d+\.\d*(?:[eE][-+]?\d+)?|\d+(?:\.\d*)?[eE][-+]?\d+|\.\d+(?:[eE][-+]?\d+)?/
+ end
+ rule integer
+ /0[xX][\da-fA-F]+|0[0-7]*|\d+/
+ end
+
+ rule regexp
+ /\/([^\[\/\\]+|\\.|\[\^?\]?([^\]]+|\\.)*\])*\/[gim]*/
+ end
+
+ rule ident
+ /[A-Za-z_$][A-Za-z0-9_$]*/
+ end
+
+ rule error
+ /\s+\n/
+ end
+
+
+ rule BLANK
+ /[ \t]*(?:(?:'[^\n]+)?\n[ \t]*)+/ { nil }
+ end
+ rule EOL
+ (BLANK | EOF) { nil }
+ end
+ rule EOF
+ !.
+ end
+ rule comment
+ /'[^\n]+/ { nil }
+ end
+ rule SP
+ /\s+|\n+|\/\/.*|\/\*(.|\n|\r)*?\*\// { nil }
+ end
+end
+
View
9 lib/capuchin/parser.rb
@@ -0,0 +1,9 @@
+require 'citrus'
+Citrus.load( File.dirname(__FILE__) + '/parser' )
+
+class << Capuchin::Parser
+ def nodes(string, root=nil)
+ self.parse(string, :consume => true, :root => root).value
+ end
+end
+
View
12 lib/capuchin/visitor.rb
@@ -1,6 +1,9 @@
-class Capuchin::Visitor < RKelly::Visitors::Visitor
- class DeclScanner < RKelly::Visitors::Visitor
+class Capuchin::Visitor
+end
+
+class Capuchin::CompileVisitor < Capuchin::Visitor
+ class DeclScanner < Capuchin::Visitor
def initialize(scope)
@scope = scope
end
@@ -279,7 +282,7 @@ def visit_DotAccessorNode(o)
o.get_bytecode(self, @g)
end
def key_safe?(o)
- RKelly::Nodes::NumberNode === o && Fixnum === o.value
+ Capuchin::Nodes::NumberNode === o && Fixnum === o.value
end
def visit_BracketAccessorNode(o)
o.get_bytecode(self, @g)
@@ -401,6 +404,7 @@ def visit_CommaNode(o)
end
def visit_NewExprNode(o)
# see also: call_bytecode in nodes.rb
+ accept o.value
args = o.arguments.value
o.arguments.value.each do |arg|
accept arg
@@ -501,7 +505,7 @@ def visit_ForNode(o)
if o.init
accept o.init
- unless RKelly::Nodes::VarStatementNode === o.init
+ unless Capuchin::Nodes::VarStatementNode === o.init
pos(o.init)
@g.pop
end
Please sign in to comment.
Something went wrong with that request. Please try again.