with try-catch working

This commit is contained in:
Jeremy Ashkenas 2009-12-14 10:00:31 -05:00
parent 4e64416397
commit fdc75144fa
7 changed files with 690 additions and 482 deletions

View File

@ -1,5 +1,9 @@
# TODO: switch/case statements
# Better block delimiters
# Flow: For loops, while loops, etc.
# Exceptions: throw catch.
# When an if statement is the last thing in a function, need to be able to
# send the return down to the last expression in the if and else bodies.
# Functions:
square: x => x * x.
@ -11,7 +15,7 @@ odd: x => x % 2 is 0.
even: x => x % 2 aint 0.
run_loop: =>
fire_events(e => e.stopPropagation().)
fire_events( e => e.stopPropagation(). )
listen()
wait().
@ -21,7 +25,7 @@ object_literal: {one: 1, two: 2, three: 3}
multiline_object: {
pi: 3.14159
list: [1, 2, 3, 4]
three: 3
three: new Idea()
inner_obj: {
freedom: => _.freedom().
}
@ -56,6 +60,11 @@ wine &&= cheese
# Nested property access and calls.
((moon.turn(360))).shapes[3].move({x: 45, y: 30}).position
# Try/Catch/Finally.
try
all_hell_breaks_loose()
dogs_and_cats_living_together()
catch error
print( error ).
try all_hell_breaks_loose() catch error then print(error).

View File

@ -8,15 +8,15 @@ dc.model.Document: dc.Model.extend({
# The import process will take care of this in the future, but the inline
# version of the summary has all runs of whitespace squeezed out.
displaySummary: =>
text: this.get('highlight') or this.get('summary')
if text then text.replace(/\s+/g, ' ') else ''..
text: this.get('highlight') or this.get('summary') or ''
text and text.replace(/\s+/g, ' ').
# Return a list of the document's metadata. Think about caching this on the
# document by binding to Metadata, instead of on-the-fly.
metadata: =>
docId: this.id
_.select(Metadata.models()
datum => _.any(datum.get('instances')
meta => _.any(meta.get('instances')
instance => instance.document_id == docId.).).
bookmark: pageNumber =>

View File

@ -10,6 +10,7 @@ token TRUE FALSE NULL
token IDENTIFIER PROPERTY_ACCESS
token CODE PARAM
token NEW RETURN
token TRY CATCH FINALLY THROW
prechigh
nonassoc UMINUS NOT '!'
@ -51,16 +52,14 @@ rule
# All types of expressions in our language
Expression:
Literal
| Variable
| Value
| Call
| Assign
| Object
| Code
| Operation
| Array
| If
| Try
| Return
| Parenthetical
;
# All tokens that can terminate an expression
@ -81,7 +80,7 @@ rule
# Assign to a variable
Assign:
Variable ":" Expression { result = AssignNode.new(val[0], val[2]) }
Value ":" Expression { result = AssignNode.new(val[0], val[2]) }
;
# Assignment within an object literal.
@ -145,11 +144,13 @@ rule
| ParamList "," PARAM { result = val[0] << val[2] }
;
Variable:
IDENTIFIER { result = VariableNode.new(val) }
| Variable Accessor { result = val[0] << val[1] }
| Invocation Accessor { result = VariableNode.new(val[0], [val[1]]) }
| Parenthetical Accessor { result = VariableNode.new(val[0], [val[1]]) }
Value:
IDENTIFIER { result = ValueNode.new(val) }
| Array { result = ValueNode.new(val) }
| Object { result = ValueNode.new(val) }
| Parenthetical { result = ValueNode.new(val) }
| Value Accessor { result = val[0] << val[1] }
| Invocation Accessor { result = ValueNode.new(val[0], [val[1]]) }
;
Accessor:
@ -179,11 +180,11 @@ rule
# A method call.
Call:
Invocation { result = val[0] }
| NEW Invocation { result = val[0].new_instance }
| NEW Invocation { result = val[1].new_instance }
;
Invocation:
Variable "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
Value "(" ArgList ")" { result = CallNode.new(val[0], val[2]) }
;
# An Array.
@ -212,6 +213,13 @@ rule
ELSE Expressions "." { result = IfNode.new(val[1], val[3], val[6]) }
;
Try:
TRY Expressions CATCH IDENTIFIER
Terminator Expressions "." { result = TryNode.new(val[1], val[3], val[5]) }
| TRY Expression CATCH IDENTIFIER
THEN Expression "." { result = TryNode.new(val[1], val[3], val[5]) }
;
Parenthetical:
"(" Expressions ")" { result = ParentheticalNode.new(val[1]) }
;

View File

@ -3,7 +3,8 @@ class Lexer
KEYWORDS = ["if", "else", "then",
"true", "false", "null",
"and", "or", "is", "aint", "not",
"new", "return"]
"new", "return",
"try", "catch", "finally", "throw"]
IDENTIFIER = /\A([a-zA-Z$_]\w*)/
NUMBER = /\A([0-9]+(\.[0-9]+)?)/

View File

@ -70,6 +70,7 @@ class CallNode < Node
def new_instance
@new = true
self
end
def compile(indent, last=false)
@ -79,7 +80,7 @@ class CallNode < Node
end
end
class VariableNode < Node
class ValueNode < Node
def initialize(name, properties=[])
@name, @properties = name, properties
end
@ -243,6 +244,28 @@ class IfNode < Node
end
end
class TryNode < Node
def initialize(try, error, recovery)
@try, @error, @recovery = try, error, recovery
end
def line_ending
''
end
def compile(indent, last=false)
@try.is_a?(Nodes) ? compile_expressions(indent) : compile_raw(indent)
end
def compile_expressions(indent)
"try {\n#{@try.compile(indent + TAB)}\n#{indent}} catch (#{@error}) {\n#{@recovery.compile(indent + TAB)}\n#{indent}}"
end
def compile_raw(indent)
"try {\n#{indent}#{TAB}#{@try.compile(indent)}#{@try.line_ending}\n#{indent}} catch (#{@error}) {\n#{indent}#{TAB}#{@recovery.compile(indent)}#{@recovery.line_ending}\n#{indent}}"
end
end
class ParentheticalNode < Node
def initialize(expressions)
@expressions = expressions

983
parser.rb

File diff suppressed because it is too large Load Diff

106
poignant.jaa Normal file
View File

@ -0,0 +1,106 @@
# Examples from the Poignant Guide.
# ['toast', 'cheese', 'wine'].each { |food| print food.capitalize }
['toast', 'wine', 'cheese'].each( food => print(food.capitalize()). )
# class LotteryTicket
# def picks; @picks; end
# def picks=(var); @picks = var; end
# def purchased; @purchased; end
# def purchased=(var); @purchased = var; end
# end
LotteryTicket: {
get_picks: => this.picks.
set_picks: nums => this.picks: nums.
get_purchase: => this.purchase.
set_purchase: amount => this.purchase: amount.
}
# module WishScanner
# def scan_for_a_wish
# wish = self.read.detect do |thought|
# thought.index( 'wish: ' ) == 0
# end
# wish.gsub( 'wish: ', '' )
# end
# end
WishScanner : {
scan_for_a_wish: =>
wish: this.read().detect( thought => thought.index('wish: ') is 0. )
wish.replace('wish: ', '').
}
# class Creature
#
# # This method applies a hit taken during a fight.
# def hit( damage )
# p_up = rand( charisma )
# if p_up % 9 == 7
# @life += p_up / 4
# puts "[#{ self.class } magick powers up #{ p_up }!]"
# end
# @life -= damage
# puts "[#{ self.class } has died.]" if @life <= 0
# end
#
# # This method takes one turn in a fight.
# def fight( enemy, weapon )
# if life <= 0
# puts "[#{ self.class } is too dead to fight!]"
# return
# end
#
# # Attack the opponent
# your_hit = rand( strength + weapon )
# puts "[You hit with #{ your_hit } points of damage!]"
# enemy.hit( your_hit )
#
# # Retaliation
# p enemy
# if enemy.life > 0
# enemy_hit = rand( enemy.strength + enemy.weapon )
# puts "[Your enemy hit with #{ enemy_hit } points of damage!]"
# self.hit( enemy_hit )
# end
# end
#
# end
Creature : {
# This method applies a hit taken during a fight.
hit: damage =>
p_up: Math.rand( this.charisma )
if p_up % 9 is 7
this.life += p_up / 4
puts( "[" + this.name + " magick powers up " + p_up + "!]" ).
this.life -= damage
if this.life <= 0 then puts( "[" + this.name + " has died.]" )..
# This method takes one turn in a fight.
fight: enemy, weapon =>
if this.life <= 0 then return puts( "[" + this.name + "is too dead to fight!]" ).
# Attack the opponent.
your_hit: Math.rand( this.strength + weapon )
puts( "[You hit with " + your_hit + "points of damage!]" )
enemy.hit( your_hit )
# Retaliation.
puts( enemy )
if enemy.life > 0
enemy_hit: Math.rand( enemy.strength + enemy.weapon )
puts( "[Your enemy hit with " + enemy_hit + "points of damage!]" )
this.hit( enemy_hit ).
this.
}