require 'polyglot' require 'treetop' require 'awesome_print' require 'rest_client' require File.join(File.dirname(__FILE__), 'scope') require File.join(File.dirname(__FILE__), 'extended_value') require File.join(File.dirname(__FILE__), 'webhook') require File.join(File.dirname(__FILE__), 'errors/parse_error') require File.join(File.dirname(__FILE__), 'errors/run_error') Treetop.load File.join(File.dirname(__FILE__), 'expressive_grammar.treetop') module Expressive def self.run(source_code, scope = TopLevel.new ) program = parse(source_code) begin program.eval(scope) rescue Exception => e raise RunError, "Error running '#{source_code}': #{$!}", $!.backtrace end end def self.parse(expressions) parser = ::ExpressiveParser.new begin parser.parse(expressions) rescue Exception => e if expressions.nil? message = "Error parsing nil expression: #{$!}" else message = "Error parsing '#{expressions}': #{$!}" end raise ParseError, message, $!.backtrace end end def self.all_symbols %w(+ - * / = ~ set sum $sub post >= > < <= and or if date datetime get put lookup $lookup $head $tail $reverse round $round $days_ago $hours_ago $minutes_ago $append $id $hash $concat $split $sms $hval $index $random $join $not $include $string $float $integer) end module Boolean def eval(scope = nil) 'true' == text_value end end module IntegerValue def eval(scope = nil) text_value.to_i end end module FloatValue def eval(scope = nil) text_value.to_f end end module StringValue def eval(scope = nil) text_value.gsub('"', '') end end class Program < Treetop::Runtime::SyntaxNode def eval(scope) elements.map {|e| e.eval(scope) }.last end end class List < Treetop::Runtime::SyntaxNode def statements elements[1].elements.map {|e| e.data } end def eval(scope) first_elem = statements.first.eval(scope) if first_elem.is_a? Function first_elem.call(scope, statements[1..-1]) else statements.map {|stat| stat.eval(scope) } end end end class Statement < Treetop::Runtime::SyntaxNode def data elements[1] end def eval(scope) data.eval(scope) end end class Identifier < Treetop::Runtime::SyntaxNode def eval(scope) scope[text_value] end end class Function def initialize(&block) @block = block end def call(scope, statements) parameters = statements.map {|c| c.eval(scope)} @block.call(*parameters) end end class Syntax < Function def call(scope, statements) @block.call(scope, statements) end end end