Permalink
Browse files

Some more little bits of documentation and pre-flight checks before 0…

….2.0 release.
  • Loading branch information...
1 parent 6b4dd20 commit 8fe377039f023b2e85d49ecfe001a3d5e834e4cd @jcoglan committed Apr 1, 2009
Showing with 116 additions and 50 deletions.
  1. +17 −0 History.txt
  2. +10 −10 Manifest.txt
  3. +8 −8 README.rdoc
  4. +12 −20 lib/builtin/library.scm
  5. +31 −11 lib/builtin/primitives.rb
  6. +25 −0 lib/parser/nodes.rb
  7. +12 −0 lib/parser/ruby.rb
  8. +1 −1 test/arithmetic.scm
View
@@ -1,3 +1,20 @@
+=== Version 0.2.0 (2009-04-01)
+
+The 'lists' release
+* Entirely revised to correctly support lists as linked pairs
+* Complete set of R5RS list functions
+* Syntax for dotted pairs and improper lists implemented
+* Rest-args for functions using dot notation
+* Almost-complete R5RS numeric library, including complexes and rationals
+* Some parser bugs regarding literals and quoting fixed
+* Many macro parsing and expansion bugs fixed, esp. concerning nested repeating patterns
+* Macro keywords and collisions with local variables now follow the spec
+* R6RS ellipsis escaping feature -- (... ...) -- implemented
+* All library syntax now implemented as macros, should all support call/cc
+* Ruby data can now be executed as Scheme code
+* Lots of inline documentation for the runtime
+
+
=== Version 0.1.0 (2009-02-25)
First public release
View
@@ -1,28 +1,28 @@
History.txt
Manifest.txt
-README.txt
Rakefile
+README.txt
bin/heist
-lib/heist.rb
lib/bin_spec.rb
+lib/heist.rb
lib/repl.rb
+lib/builtin/library.scm
lib/builtin/primitives.rb
lib/builtin/syntax.scm
-lib/builtin/library.scm
+lib/parser/nodes.rb
lib/parser/ruby.rb
-lib/parser/scheme.tt
lib/parser/scheme.rb
-lib/parser/nodes.rb
-lib/runtime/data/expression.rb
-lib/runtime/data/identifier.rb
-lib/runtime/data/cons.rb
+lib/parser/scheme.tt
+lib/runtime/callable/continuation.rb
lib/runtime/callable/function.rb
lib/runtime/callable/syntax.rb
lib/runtime/callable/macro.rb
lib/runtime/callable/macro/matches.rb
lib/runtime/callable/macro/tree.rb
lib/runtime/callable/macro/expansion.rb
-lib/runtime/callable/continuation.rb
+lib/runtime/data/cons.rb
+lib/runtime/data/expression.rb
+lib/runtime/data/identifier.rb
lib/runtime/binding.rb
lib/runtime/frame.rb
lib/runtime/runtime.rb
@@ -31,7 +31,6 @@ lib/runtime/stack.rb
lib/runtime/stackless.rb
lib/stdlib/benchmark.scm
lib/stdlib/birdhouse.scm
-test/test_heist.rb
test/arithmetic.scm
test/benchmarks.scm
test/booleans.scm
@@ -52,6 +51,7 @@ test/macro-helpers.scm
test/macros.scm
test/numbers.scm
test/plt-macros.txt
+test/test_heist.rb
test/unhygienic.scm
test/vars.scm
View
@@ -33,11 +33,10 @@ languages.
== Features
-Heist nominally targets R5RS and is still in the early stages of development
-(at time of writing it is about a month old). The priority at this stage
-is runtime support for features that cannot be directly expressed as
-Scheme functions. The library is therefore rather small but the runtime
-is more advanced than some other toy Schemes I've seen.
+Heist nominally targets R5RS (http://www.schemers.org/Documents/Standards/R5RS/HTML).
+It has good support for many Scheme runtime features such as tail call
+optimisation, macros and first-class continuations, as well as reasonably
+complete numeric and list libraries.
Currently implemented R5RS features include:
@@ -50,7 +49,9 @@ Currently implemented R5RS features include:
<tt>`</tt>, <tt>,</tt>, <tt>,@</tt>
* Macros: <tt>syntax-rules</tt>, <tt>define-syntax</tt>,
<tt>let-syntax</tt>, <tt>letrec-syntax</tt>. Supports keywords,
- lists, pattern variables and ellipses down to arbitrary depth
+ lists, pattern variables and ellipses down to arbitrary depth, as
+ well as the R6RS ellipsis-escaping feature (<tt>(... ...)</tt>) to
+ better enable macro-writing macros
* Continuations: <tt>call-with-current-continuation</tt>, aliased
as <tt>call/cc</tt>
* Proper tail recursion
@@ -85,8 +86,7 @@ Currently implemented R5RS features include:
<tt>set-cdr!</tt>, <tt>c[ad]{1,4}r</tt>, <tt>list</tt>, <tt>length</tt>,
<tt>append</tt>, <tt>reverse</tt>, <tt>list-tail</tt>, <tt>list-ref</tt>,
<tt>memq</tt>, <tt>memv</tt>, <tt>member</tt>, <tt>assq</tt>, <tt>assv</tt>,
- <tt>assoc</tt>
-* Control features: <tt>apply</tt>, <tt>map</tt>, <tt>for-each</tt>
+ <tt>assoc</tt>, <tt>apply</tt>, <tt>map</tt>, <tt>for-each</tt>
* String library: <tt>display</tt>, <tt>newline</tt>, <tt>string->number</tt>
* File loading: <tt>(load "foo.scm")</tt>, paths are relative to the
current file
View
@@ -81,14 +81,16 @@
; (max arg1 arg2 ...)
; Returns the maximum value in the list of arguments
(define (max . values)
- (reduce (lambda (a b) (if (>= a b) a b))
- values))
+ (foldr (lambda (a b) (if (>= a b) a b))
+ (car values)
+ (cdr values)))
; (min arg1 arg2 ...)
; Returns the minimum value in the list of arguments
(define (min . values)
- (reduce (lambda (a b) (if (<= a b) a b))
- values))
+ (foldr (lambda (a b) (if (<= a b) a b))
+ (car values)
+ (cdr values)))
; (abs x)
; Returns the absolute value of a number
@@ -256,10 +258,6 @@
(define assv (assoc-list-search eqv?))
(define assoc (assoc-list-search equal?))
-;----------------------------------------------------------------
-
-; Control features
-
; (map proc list1 list2 ...)
; Returns a new list formed by applying proc to each member
; (or set of members) of the given list(s).
@@ -286,16 +284,10 @@
(apply proc (cons (car pair)
(map car others)))))
-; (reduce proc list)
-; Returns a new object by applying the given function
-; to the memo and each member of the list. The initial
-; value of the memo is the first member of the list.
-; The return value of the function becomes the memo for
-; the next invocation.
-(define (reduce proc list)
- (let ([result (car list)])
- (for-each (lambda (value)
- (set! result (proc result value)))
- (cdr list))
- result))
+; (foldr proc value list)
+(define (foldr proc value list)
+ (if (null? list)
+ value
+ (proc (car list)
+ (foldr proc value (cdr list)))))
View
@@ -1,19 +1,19 @@
# Functions that create new functions
-# (define) binds values to names in the current scope.
-# If the first parameter is a list it creates a function,
-# otherwise it eval's the second parameter and binds it
-# to the name given by the first.
+# (define) binds values to names in the current scope. If the
+# first parameter is a list it creates a function, otherwise
+# it eval's the second parameter and binds it to the name
+# given by the first.
syntax('define') do |scope, cells|
name = cells.car
Cons === name ?
scope.define(name.car, name.cdr, cells.cdr) :
scope[name] = Heist.evaluate(cells.cdr.car, scope)
end
-# (lambda) returns an anonymous function whose arguments
-# are named by the first parameter and whose body is given
-# by the remaining parameters.
+# (lambda) returns an anonymous function whose arguments are
+# named by the first parameter and whose body is given by the
+# remaining parameters.
syntax('lambda') do |scope, cells|
Function.new(scope, cells.car, cells.cdr)
end
@@ -48,6 +48,10 @@
# Continuations
+# Creates a +Continuation+ encapsulating the current +Stack+
+# state, and returns the result of calling the second parameter
+# (which should evaluate to a +Function+) with the continuation
+# as the argument.
syntax('call-with-current-continuation') do |scope, cells|
continuation = Continuation.new(scope.runtime.stack)
callback = Heist.evaluate(cells.car, scope)
@@ -86,24 +90,37 @@
# Runtime utilities
+# (exit) causes the host Ruby process to quit
define('exit') { exit }
+# (runtime) returns the amount of time the host +Runtime+ has
+# been alive, in microseconds. Not a standard function, but
+# used in SICP.
syntax('runtime') do |scope, cells|
scope.runtime.elapsed_time
end
+# (eval) evaluates Scheme code and returns the result. The
+# argument can be a string or a list containing a valid
+# Scheme expression.
syntax('eval') do |scope, cells|
scope.eval(Heist.evaluate(cells.car, scope))
end
+# (display) prints the given value to the console
define('display') do |expression|
print expression
end
+# (load) loads a file containing Scheme code and executes its
+# contents. The path can be relative to the current file, or
+# it can be the name of a file in the Heist library.
syntax('load') do |scope, cells|
scope.load(cells.car)
end
+# (error) raises an error with the given message. Additional
+# arguments are appended to the message.
define('error') do |message, *args|
raise RuntimeError.new("#{ message } :: #{ args * ', ' }")
end
@@ -172,7 +189,7 @@
end
define('real?') do |value|
- call('rational?', value) || Float === value
+ Float === value || call('rational?', value)
end
define('rational?') do |value|
@@ -289,12 +306,13 @@
Complex.new(real, imag)
end
-# Accessors for complex numbers
-
+# Returns the real part of a number
define('real-part') do |value|
Complex === value ? value.real : value
end
+# Returns the imaginary part of a number, which is zero
+# unless the number is not real
define('imag-part') do |value|
Complex === value ? value.imag : 0
end
@@ -304,10 +322,12 @@
rand(max)
end
+# Casts a number to a string
define('number->string') do |number, radix|
number.to_s(radix || 10)
-end
+end
+# Casts a string to a number
define('string->number') do |string, radix|
radix.nil? ? string.to_f : string.to_i(radix)
end
View
@@ -1,26 +1,45 @@
module Heist
+ # The +Scheme+ module hosts various classes used by the +SchemeParser+ class,
+ # which is generated from a parsing expression grammar using +Treetop+. (See
+ # <tt>lib/parser/scheme.tt</tt>.) The classes map syntax structures generated
+ # by +Treetop+ to Heist runtime objects for execution. All the classes except
+ # +Program+ are evaluated without a runtime environment; evaluating them
+ # simply casts to non-Treetop objects in the Heist library, or to raw Ruby
+ # objects. Evaluating a +Program+ requires a +Runtime+ in which to do so.
module Scheme
+ # Any list-generating shorthands present in the grammar should be listed here.
+ # In Scheme, this list includes the various quoting symbols that can be used
+ # as shorthands for calling quoting functions.
SHORTHANDS = {
"'" => :quote,
"`" => :quasiquote,
"," => :unquote,
",@" => :'unquote-splicing'
}
+ # +Program+ is the root of the parse tree; parsing any string of Scheme code
+ # produces one of these.
class Program < Treetop::Runtime::SyntaxNode
+ # Evaluates all the expressions in the +Program+ in order, returning the
+ # result of the last expression.
def eval(scope)
convert!
@data.map { |part| Heist.evaluate(part, scope) }.last
end
+ # Converts all the +Treetop+ objects in the +Program+ to Heist objects
+ # and raw Ruby data ready for interpretation using a +Runtime+.
def convert!
return if @data
@data = Runtime::Cons.construct(elements, true) { |c| c.eval }
end
end
+ # A +List+ has an array of +cells+, and optionally a +tail+ if it's an
+ # improper list or a dotted pair.
module List
+ # Evaluating a +List+ produces a Heist +Cons+ object.
def eval
list = Runtime::Cons.construct(cells, true) { |c| c.eval }
list.tail.cdr = tail.cell.eval if tail.respond_to?(:dot)
@@ -36,20 +55,26 @@ def tail
end
end
+ # <tt>QuotedCell</tt> are generated using the quoting shorthands.
class QuotedCell < Treetop::Runtime::SyntaxNode
+ # Evaluating a +QuotedCell+ produces a +Cons+ that expresses a function
+ # call to the appropriate quoting function, with the cell as the argument.
def eval
quote = elements[1].text_value
cell = elements[2].eval
Runtime::Cons.construct([Runtime::Identifier.new(SHORTHANDS[quote]), cell])
end
end
+ # <tt>Cells</tt> are any piece of Scheme data: numbers, booleans, strings,
+ # lists. Any building block of Scheme code goes in a +Cell+.
class Cell < Treetop::Runtime::SyntaxNode
def eval
elements[1].eval
end
end
+ # A +Datum+ is any piece of atomic literal data.
class Datum < Treetop::Runtime::SyntaxNode
def eval
elements[0].eval
View
@@ -1,6 +1,18 @@
module Heist
+ # +RubyParser+ parses non-string code given as Ruby data such as arrays. It
+ # allows Lisp-style code to be expressed inline with Ruby, for example:
+ #
+ # scheme = Heist::Runtime.new
+ # scheme.exec [:define, [:square, :x], [:*, :x, :x]]
+ # scheme.exec [:square, 9]
+ # #=> 81
+ #
+ # The above API uses +RubyParser+ behind the scenes to turn Ruby data into
+ # Heist runtime objects such as +Cons+ based lists before execution.
+ #
class RubyParser
+ # Parses a single piece of Ruby data in
def parse(source)
case source
when Array then
View
@@ -44,7 +44,7 @@
(assert-equal 3 (numerator (/ 6 4)))
(assert-equal 2 (denominator (/ 6 4)))
-; (assert-equal 2.0 (denominator (exact->inexact (/ 6 4))))
+; (assert-equal 2.0 (denominator (exact->inexact (/ 6 4)))) TODO implement this
(assert-equal -5.0 (floor -4.3))
(assert-equal -4.0 (ceiling -4.3))

0 comments on commit 8fe3770

Please sign in to comment.