Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 8 commits
  • 9 files changed
  • 0 commit comments
  • 1 contributor
Showing with 308 additions and 69 deletions.
  1. +21 −32 README.markdown
  2. +6 −0 Robin/Small.lhs
  3. +62 −5 doc/module/Bind-Args.markdown
  4. +100 −3 doc/module/Small.markdown
  5. +48 −0 doc/module/Term.markdown
  6. +22 −17 module/bind-args_0_1.robin
  7. +9 −7 module/small_0_1.robin
  8. +30 −5 module/term_0_1.robin
  9. +10 −0 test.sh
View
53 README.markdown
@@ -189,44 +189,33 @@ Plans
final-result and unknown-tag reply messages, particularly during `call`
and `respond`.
-* A macro for asserting that the correct number of arguments have been
- given to a macro. (Right now the `small` macros don't complain if
- given too many arguments.)
-
* Some kind of macro for capturing the recursive function call pattern
(like `letrec`, but not necessary to support mutual recursion.) Possibly
called `bind-recur`. Also `let-recur` could build on that. Turn:
- (bind-recur foo (fun (a b c)
- (if a
- (b c)
- (foo (bar a) b c))) ...)
-
-into
-
- (bind foo
- (bind foo-r (fun (self a b c)
- (if a
- (b c)
- (self self (bar a) b c)))
- (fun (a b c) (foo-r foo-r a b c))) ...)
+ (bind-recur foo (fun (a b c)
+ (if a
+ (b c)
+ (foo (bar a) b c))) ...)
-Lack of a `gensym` will make this tricky. We don't really have to
-bind `foo-r`, we can just repeat the definition of the recursive
-function; but I don't know how we can add the `self` parameter without
-potentially shadowing a user parameter also named `self`.
+ into
-An alternative which might be easier (but less elegant) would be to
-introduce a primitive `(self)` which evaluates to the function currently
-being evaluated. This might make the `self` parameter to macros redundant,
-though.
-
-* Use `subst-env` to implement `literal-with`, as a substitute for
- `quasiquote`, which works more like `let` (cf. `let-symbol`).
-
-* In the `term` module, export `cast`, which works like Scheme's
- `quasiquote`, except the unquote symbol can be specified at the top of
- the form.
+ (bind foo
+ (bind foo-r (fun (self a b c)
+ (if a
+ (b c)
+ (self self (bar a) b c)))
+ (fun (a b c) (foo-r foo-r a b c))) ...)
+
+ Lack of a `gensym` will make this tricky. We don't really have to
+ bind `foo-r`, we can just repeat the definition of the recursive
+ function; but I don't know how we can add the `self` parameter without
+ potentially shadowing a user parameter also named `self`.
+
+ An alternative which might be easier (but less elegant) would be to
+ introduce a primitive `(self)` which evaluates to the function currently
+ being evaluated. This might make the `self` parameter to macros redundant,
+ though.
* Work out the static analysis modules. See the Static Analysis document
for more information.
View
6 Robin/Small.lhs
@@ -42,6 +42,7 @@ This implementation of the `small` module is non-normative.
> cc $ Env.insert formal value rest))
> evalArgs _ _ origActuals _ ienv cc = do
> raise ienv (errMsg "illegal-arguments" (List origActuals))
+> robinFun env ienv other cc = raise ienv (errMsg "illegal-arguments" other)
> choose env ienv (List [(List [(Symbol "else"), branch])]) cc =
> eval env ienv branch cc
@@ -52,10 +53,12 @@ This implementation of the `small` module is non-normative.
> eval env ienv branch cc
> Boolean False ->
> choose env ienv (List rest) cc)
+> choose env ienv other cc = raise ienv (errMsg "illegal-arguments" other)
> bind env ienv (List [name@(Symbol _), expr, body]) cc =
> eval env ienv expr (\value ->
> eval (Env.insert name value env) ienv body cc)
+> bind env ienv other cc = raise ienv (errMsg "illegal-arguments" other)
> robinLet env ienv (List [(List bindings), body]) cc =
> bindAll bindings env ienv (\newEnv ->
@@ -66,6 +69,9 @@ This implementation of the `small` module is non-normative.
> bindAll (List [name@(Symbol _), sexpr]:rest) env ienv cc =
> eval env ienv sexpr (\value ->
> bindAll rest (Env.insert name value env) ienv cc)
+> bindAll (other:rest) env ienv cc =
+> raise ienv (errMsg "illegal-binding" other)
+> robinLet env ienv other cc = raise ienv (errMsg "illegal-arguments" other)
Module Definition
-----------------
View
67 doc/module/Bind-Args.markdown
@@ -1,17 +1,33 @@
+Robin - Bind-Args module (provisional)
+======================================
-> Tests for functionality "Interpret Robin Program"
+`bind-args` is a macro for binding the arguments of another value to
+identifiers, as well as asserting that the correct number of arguments
+have been given to the macro.
+
+This macro should really be defined in `small`, and used by the other
+macros defined in `small` (right now they don't complain if given too
+many arguments, and complain about an `expected-list` if given too few.)
+
### `bind-args` ###
+`bind-args` takes a literal list of identifiers, and expresion which
+evaluates to a literal list of expressions whose values are to be bound
+to those identifiers, an expresion which evaluates to the environment in
+which those expressions will be evaluated, and an expression to evaluate
+in the new environment in which the identifiers are bound.
+
| (robin (0 1) ((small (0 1) *) (bind-args (0 1) *))
- | (bind-args (a b) (1 2)
+ | (bind-args (a b) (literal (1 2)) (env)
| (list a b)))
= (1 2)
Expressions in the list of values are evaluated.
| (robin (0 1) ((small (0 1) *) (bind-args (0 1) *))
- | (bind-args (a b) ((subtract 5 4) (subtract 10 1))
+ | (bind-args (a b) (literal ((subtract 5 4) (subtract 10 1))) (env)
| (list a b)))
= (1 9)
@@ -19,18 +35,59 @@ Too many or too few arguments will raise an `illegal-arguments`
exception.
| (robin (0 1) ((small (0 1) *) (bind-args (0 1) *))
- | (bind-args (a b) (1)
+ | (bind-args (a b) (literal (1)) (env)
| (list a b)))
? uncaught exception: (illegal-arguments (1))
| (robin (0 1) ((small (0 1) *) (bind-args (0 1) *))
- | (bind-args (a b) (1 2 3)
+ | (bind-args (a b) (literal (1 2 3)) (env)
| (list a b)))
? uncaught exception: (illegal-arguments (1 2 3))
The literal arguments are reported in the exception.
| (robin (0 1) ((small (0 1) *) (bind-args (0 1) *))
- | (bind-args (a) ((subtract 5 4) (subtract 1 0))
+ | (bind-args (a) (literal ((subtract 5 4) (subtract 1 0))) (env)
| a))
? uncaught exception: (illegal-arguments ((subtract 5 4) (subtract 1 0)))
+
+This is how it might be used in a macro definition. The reason for the
+seemingly strange requirements of the second and third arguments should
+become clear here: typically you would just pass the macro's `args` and
+`env` to those arguments.
+
+ | (robin (0 1) ((small (0 1) *) (bind-args (0 1) *))
+ | (bind add (macro (self args env)
+ | (bind-args (a b) args env
+ | (subtract a (subtract 0 b))))
+ | (add 4 (add 5 6))))
+ = 15
+
+ | (robin (0 1) ((small (0 1) *) (bind-args (0 1) *))
+ | (bind add (macro (self args env)
+ | (bind-args (a b) args env
+ | (subtract a (subtract 0 b))))
+ | (bind r 7
+ | (add r r))))
+ = 14
+
+ | (robin (0 1) ((small (0 1) *) (bind-args (0 1) *))
+ | (bind add (macro (self args env)
+ | (bind-args (a b) args env
+ | (subtract a (subtract 0 b))))
+ | (add (subtract 0 0))))
+ ? uncaught exception: (illegal-arguments ((subtract 0 0)))
+
+ | (robin (0 1) ((small (0 1) *) (bind-args (0 1) *))
+ | (bind add (macro (self args env)
+ | (bind-args (a b) args env
+ | (subtract a (subtract 0 b))))
+ | (add 9 9 9)))
+ ? uncaught exception: (illegal-arguments (9 9 9))
+
+ | (robin (0 1) ((small (0 1) *) (bind-args (0 1) *))
+ | (bind add (macro (self args env)
+ | (bind-args (a b) args env
+ | (subtract a (subtract 0 b))))
+ | (add 1 n)))
+ ? uncaught exception: (unbound-identifier n)
View
103 doc/module/Small.markdown
@@ -1,9 +1,6 @@
Module `small`
==============
- -> Functionality "Interpret Robin Program" is implemented by
- -> shell command "bin/robin -B small %(test-file)"
-
-> Tests for functionality "Interpret Robin Program"
The `core` module only exports macros which are necessarily, or for reasons
@@ -40,6 +37,12 @@ any S-expression.
| (literal (hello (there) world)))
= (hello (there) world)
+`literal` requires at least one argument.
+
+ | (robin (0 1) ((small (0 1) *))
+ | (literal))
+ ? uncaught exception: (illegal-arguments ())
+
`literal` is basically equivalent to Scheme's `quote`.
### `list` ###
@@ -48,10 +51,14 @@ any S-expression.
| (small:list 1 2 3))
= (1 2 3)
+Unlike `literal`, `list` does evaluate all of its arguments.
+
| (robin (0 1) ((small (0 1) *))
| (list (literal x) (literal y)))
= (x y)
+`list` does not require any arguments.
+
| (robin (0 1) ((small (0 1) *))
| (list))
= ()
@@ -107,6 +114,20 @@ A function may have no arguments at all.
| ((fun () 7)))
= 7
+But, a function must have exactly both a body and a list of formal arguments.
+
+ | (robin (0 1) ((small (0 1) *))
+ | ((fun ())))
+ ? uncaught exception: (illegal-arguments (()))
+
+ | (robin (0 1) ((small (0 1) *))
+ | ((fun)))
+ ? uncaught exception: (illegal-arguments ())
+
+ | (robin (0 1) ((small (0 1) *))
+ | ((fun (a) a a)))
+ ? uncaught exception: (illegal-arguments ((a) a a))
+
An exception will be raised if not enough arguments are supplied to a
function call.
@@ -160,6 +181,20 @@ it evaluates.
| (find find (literal ((c d) (e f) (a b))) (literal a))))
= (a b)
+`bind` expects exactly three arguments.
+
+ | (robin (0 1) ((small (0 1) *))
+ | (bind smoosh (fun (x y) (list y x))))
+ ? uncaught exception: (illegal-arguments (smoosh (fun (x y) (list y x))))
+
+ | (robin (0 1) ((small (0 1) *))
+ | (bind smoosh))
+ ? uncaught exception: (illegal-arguments (smoosh))
+
+ | (robin (0 1) ((small (0 1) *))
+ | (bind))
+ ? uncaught exception: (illegal-arguments ())
+
`bind` is basically equivalent to Scheme's `let`, but only one
binding may be given.
@@ -213,6 +248,42 @@ Shadowing happens.
| (let () (literal hi)))
= hi
+Both the body and the list of bindings are required.
+
+ | (robin (0 1) ((small (0 1) *))
+ | (let ()))
+ ? uncaught exception: (illegal-arguments (()))
+
+ | (robin (0 1) ((small (0 1) *))
+ | (let))
+ ? uncaught exception: (illegal-arguments ())
+
+No arguments may be given besides the body and list of bindings.
+
+ | (robin (0 1) ((small (0 1) *))
+ | (let ((a 1)) a a))
+ ? uncaught exception: (illegal-arguments (((a 1)) a a))
+
+Each binding must have exactly one name and one value.
+
+ | (robin (0 1) ((small (0 1) *))
+ | (let ((a 1 3)) a))
+ ? uncaught exception: (illegal-binding (a 1 3))
+
+ | (robin (0 1) ((small (0 1) *))
+ | (let ((a)) a))
+ ? uncaught exception: (illegal-binding (a))
+
+ | (robin (0 1) ((small (0 1) *))
+ | (let (()) 7))
+ ? uncaught exception: (illegal-binding ())
+
+The identifier in a binding must be a symbol.
+
+ | (robin (0 1) ((small (0 1) *))
+ | (let ((3 1)) 3))
+ ? uncaught exception: (illegal-binding (3 1))
+
`let` is basically equivalent to Scheme's `let*` or Haskell's `let`.
### `choose` ###
@@ -242,6 +313,26 @@ corresponding expression will be evaluated if all other tests failed.
| (robin (0 1) ((small (0 1) *))
| (choose (else (literal woo))))
= woo
+
+`choose` does require an `else` branch.
+
+ | (robin (0 1) ((small (0 1) *))
+ | (choose (#f (literal hi)) (#f (literal med))))
+ ? uncaught exception: (illegal-arguments ())
+
+ | (robin (0 1) ((small (0 1) *))
+ | (choose))
+ ? uncaught exception: (illegal-arguments ())
+
+Each branch of a `choose` needs to be a two-element list.
+
+ | (robin (0 1) ((small (0 1) *))
+ | (choose (#t) (else (literal lo))))
+ ? uncaught exception: (illegal-arguments ((#t) (else (literal lo))))
+
+ | (robin (0 1) ((small (0 1) *))
+ | (choose (#f 66) (else)))
+ ? uncaught exception: (illegal-arguments ((else)))
`choose` is basically equivalent to Scheme's `cond`.
@@ -259,3 +350,9 @@ where this form is encountered, as an alist.
| (prepend
| (find find (env) (literal boolean?)) (find find (env) (literal prepend)))))
= ((boolean? (builtin boolean?)) prepend (builtin prepend))
+
+`env` expects exactly no arguments.
+
+ | (robin (0 1) ((small (0 1) *))
+ | (env hello))
+ ? uncaught exception: (illegal-arguments (hello))
View
48 doc/module/Term.markdown
@@ -48,3 +48,51 @@ second value.
| (subst-many (literal ((p 100) (q 200)))
| (literal (a d (r p q) q (p (z q) p p) p z q)))))
= (a d (r 100 200) 200 (100 (z 200) 100 100) 100 z 200)
+
+### `literal-with` ###
+
+ | (robin (0 1) ((term (0 1)))
+ | (term:literal-with ((p 100) (q 200))
+ | (a d (r p q) q (p (z q) p p) p z q)))
+ = (a d (r 100 200) 200 (100 (z 200) 100 100) 100 z 200)
+
+Somewhat unlike `subst-many`, `literal-with` evaluates the expressions
+given as values in the bindings.
+
+ | (robin (0 1) ((core (0 1) *) (term (0 1) *))
+ | (literal-with ((p (subtract 200 100)) (q (subtract 350 150)))
+ | (a d (r p q) q (p (z q) p p) p z q)))
+ = (a d (r 100 200) 200 (100 (z 200) 100 100) 100 z 200)
+
+`literal-with` may be given an empty list of bindings; in this case it does
+the same thing as `literal` would.
+
+ | (robin (0 1) ((term (0 1)))
+ | (term:literal-with ()
+ | (a d (r p q) q (p (z q) p p) p z q)))
+ = (a d (r p q) q (p (z q) p p) p z q)
+
+### `cast` ###
+
+`cast` is a macro which works a lot like Scheme's `quasiquote`, except
+that the unquote symbol is specified as the first argument of the macro.
+
+ | (robin (0 1) ((small (0 1) *) (term (0 1) *))
+ | (bind b 44
+ | (cast $ (a b c))))
+ = (a b c)
+
+ | (robin (0 1) ((small (0 1) *) (term (0 1) *))
+ | (bind b 44
+ | (cast $ (a ($ b) c))))
+ = (a 44 c)
+
+ | (robin (0 1) ((small (0 1) *) (term (0 1) *))
+ | (bind b 44
+ | (cast $ (a b ($ c)))))
+ ? uncaught exception: (unbound-identifier c)
+
+ | (robin (0 1) ((small (0 1) *) (term (0 1) *))
+ | (bind b 44
+ | (cast $ (a $ b c))))
+ = (a $ b c)
View
39 module/bind-args_0_1.robin
@@ -1,4 +1,4 @@
-(robin (0 1) ((small (0 1) *) (list (0 1) *))
+(robin (0 1) ((small (0 1) *))
;''Should really be a part of small, but prototyping
it here now.''
(let (
@@ -6,22 +6,27 @@
(macro (self args env)
(let (
(id-list (head args))
- (orig-val-list (head (tail args)))
- (expr (head (tail (tail args))))
- (bind-args-r (fun (self id-list val-list env-acc)
- ;''This will need to be converted into a macro at some point,
- but for now, this is easier.''
- (if (empty? id-list)
- (if (empty? val-list)
- env-acc
- (raise (list (literal illegal-arguments) orig-val-list)))
- (if (empty? val-list)
- (raise (list (literal illegal-arguments) orig-val-list))
- (self self (tail id-list) (tail val-list)
- (prepend
- (list (head id-list) (eval env (head val-list)))
- env-acc))))))
- (new-env (bind-args-r bind-args-r id-list orig-val-list env))
+ (orig-val-list (eval env (head (tail args))))
+ (given-env (eval env (head (tail (tail args)))))
+ (expr (head (tail (tail (tail args)))))
+ (bind-args-r (macro (self args env)
+ (let (
+ (id-list (eval env (head args)))
+ (val-list (eval env (head (tail args))))
+ (env-acc (eval env (head (tail (tail args)))))
+ )
+ (if (equal? id-list ())
+ (if (equal? val-list ())
+ env-acc
+ (raise (list (literal illegal-arguments) orig-val-list)))
+ (if (equal? val-list ())
+ (raise (list (literal illegal-arguments) orig-val-list))
+ (self
+ (tail id-list) (tail val-list)
+ (prepend
+ (list (head id-list) (eval given-env (head val-list)))
+ env-acc)))))))
+ (new-env (bind-args-r id-list orig-val-list env))
)
(eval new-env expr))))
)
View
16 module/small_0_1.robin
@@ -1,13 +1,15 @@
(robin (0 1) ((core (0 1) *))
(eval
(prepend
- (prepend ((macro (self args env) (head args)) literal) (macro (self args env) (head args)))
+ (prepend ((macro (self args env) (head args)) literal) (prepend (macro (self args env) (head args)) ()))
(prepend
- (prepend ((macro (self args env) (head args)) bind) (macro (self args env) (eval
- (prepend (prepend (head args) (eval env (head (tail args)))) env)
- (head (tail (tail args))))))
+ (prepend ((macro (self args env) (head args)) bind)
+ (prepend (macro (self args env)
+ (eval
+ (prepend (prepend (head args) (prepend (eval env (head (tail args))) ())) env)
+ (head (tail (tail args))))) ()))
(prepend
- (prepend ((macro (self args env) (head args)) env) (macro (self args env) env))
+ (prepend ((macro (self args env) (head args)) env) (prepend (macro (self args env) env) ()))
((macro (self args env) env))
)
)
@@ -20,7 +22,7 @@
(bind binding (head bindings)
(bind name (head binding)
(bind value (eval env (head (tail binding)))
- (bind newenv (prepend (prepend name value) env)
+ (bind newenv (prepend (prepend name (prepend value ())) env)
(bind newbindings (tail bindings)
(bind newargs (prepend newbindings (tail args))
(eval newenv (prepend self newargs)))))))))))
@@ -55,7 +57,7 @@
closed-env
(let (
(value (eval fun-env (head actuals)))
- (new-closed-env (prepend (prepend (head formals) value) closed-env))
+ (new-closed-env (prepend (prepend (head formals) (prepend value ())) closed-env))
(new-args (prepend new-closed-env (prepend fun-env (prepend (tail formals) (prepend (tail actuals) ())))))
)
(eval env
View
35 module/term_0_1.robin
@@ -13,14 +13,39 @@
(else
sexp)))
(subst-r subst-r src dest sexp))))
- (subst-many
- (fun (alist sexp)
+ (subst-many (fun (alist sexp)
+ (fold (fun (binding sexp)
+ (subst (head binding) (head (tail binding)) sexp)) sexp alist)))
+ (literal-with (macro (self args env)
+ (let (
+ (alist (head args))
+ (body (head (tail args)))
+ )
(fold (fun (binding sexp)
- (subst (head binding) (head (tail binding)) sexp)) sexp alist)))
+ (subst (head binding) (eval env (head (tail binding))) sexp))
+ body alist))))
+ (cast-r (fun (self marker fn sexp)
+ (choose
+ ((empty? sexp)
+ ())
+ ((list? sexp)
+ (if (equal? (head sexp) marker)
+ (fn (head (tail sexp)))
+ ;else
+ (prepend (self self marker fn (head sexp))
+ (self self marker fn (tail sexp)))))
+ (else
+ sexp))))
+ (cast (macro (self args env)
+ (cast-r cast-r (head args)
+ (fun (e) (eval env e))
+ (head (tail args)))))
)
(list
- (list (literal subst) subst)
- (list (literal subst-many) subst-many)
+ (list (literal subst) subst)
+ (list (literal subst-many) subst-many)
+ (list (literal literal-with) literal-with)
+ (list (literal cast) cast)
)
)
)
View
10 test.sh
@@ -23,6 +23,8 @@ FILES="doc/Fundamental_Semantics.markdown \
doc/module/Miscellany.markdown \
doc/module/Bind-Args.markdown"
+FILES_NO_BUILTIN_SMALL="doc/module/Small.markdown"
+
# Hack for Robin & Falderal built with the ghc from Haskell Platform on Windows
if [ -e bin/robin.exe ]; then
falderal test -b \
@@ -31,10 +33,18 @@ if [ -e bin/robin.exe ]; then
-f 'Interpret Robin Program:shell command "bin\\robin.exe -m module -m fixture\\module %(test-file)"' \
-f 'Interpret Robin Program without output:shell command "bin\\robin.exe -m module -n %(test-file)"' \
${FILES}
+ falderal test -b \
+ -c "Interpret Robin Program" \
+ -f 'Interpret Robin Program:shell command "bin\\robin.exe -B small -m module -m fixture\\module %(test-file)"' \
+ ${FILES_NO_BUILTIN_SMALL}
rm -f results*
else
falderal test -b \
-c "Interpret Robin Program" \
-f 'Interpret Robin Program:shell command "bin/robin -m module -m fixture/module %(test-file)"' \
${FILES}
+ falderal test -b \
+ -c "Interpret Robin Program" \
+ -f 'Interpret Robin Program:shell command "bin\\robin.exe -B small -m module -m fixture\\module %(test-file)"' \
+ ${FILES_NO_BUILTIN_SMALL}
fi

No commit comments for this range

Something went wrong with that request. Please try again.