Not logged in. Login

Lisp-in-Lisp Representation: Small Lisp

GRAIL Grammar for Lisp in Lisp

The following grammar shows how Small Lisp programs can be represented as Lisp data objects. This uses the GRAIL (Grammars in Lisp) formalism, which is essentially an augmented BNF. Each nonterminal on the RHS has two parts: a name which is used to name a data access function for selecting the component, as well as the grammatical type (syntactic category). Plus marks in the nonterminals indicate lists of one or more elements.

<expression> 	::=	<identifier> | <numeric-atom-expr> | 
                        <symbolic-atom-expr> | <list-expr> | 
                        <fn-call> | <cond-expr> | <let-expr>
<identifier> 	::=	<id-name:symbolic atom>
<numeric-atom-expr> 	::=	<numeric-val:numeric atom>
<symbolic-atom-expr> 	::=	( quote <symbolic-val:symbolic-atom> )
<list-expr> 	::=	( quote <list-val:list> )
<fn-call> 	::=	( <callee:identifier> <arguments:expression+> )
<cond-expr> 	::=	( cond <clauses:clause+> )
<clause> 	::=	( <predicate:expression> <result:expression> )
<let-expr> 	::=	( let ( <local-defs:local-def+> ) <final-expr:expression> )
<local-def> 	::=	( <local-var:variable> <local-val:expression> )

Data Accesss Functions in Small Lisp

The functions required by the GRAIL grammar above can be implemented as follows.

Recognizers

;;; The recognizers for Small Lisp expressions.
;;;
identifier-p[expr] = symbolp[expr]

numeric-atom-expr-p[expr] = numberp[expr]

symbolic-atom-expr-p[expr] =
  [listp[expr] -->
     [eq[first[expr]; "quote"] --> symbolp[second[expr]];
      otherwise --> F];
   otherwise --> F]

list-expr-p[expr] =
  [listp[expr] -->
     [eq[first[expr]; "quote"] --> listp[second[expr]];
      otherwise --> F];
   otherwise --> F]

fn-call-p[expr] =
  [listp[expr] -->
     [eq[first[expr]; "quote"] --> F;
      eq[first[expr]; "cond"] --> F;
      eq[first[expr]; "let"] --> F;
      otherwise --> T];
   otherwise --> F]

cond-expr-p[expr] =
  [listp[expr] --> eq[first[expr]; "cond"];
   otherwise --> F]

let-expr-p[expr] =
  [listp[expr] --> eq[first[expr]; "let"];
   otherwise --> F]

Converters

These trivial functions convert identifiers and numeric atoms to appropriate Lisp representations. We use functions in case we decide to change the representation later.

id-name[ident] = ident

make-identifier[id-name] = id-name

numeric-val[num-atom] = num-atom

make-numeric-atom-expr[val] = val

Symbolic atoms and List exprs

The access and constructor functions for symbolic-atom-exprs and list-exprs are nontrivial.

symbolic-val[sym-atom-expr] = second[sym-atom-expr]

make-symbolic-atom-expr[sym] = list2["quote"; sym]

list-val[list-expr] = second[list-expr]

make-list-expr[list] = list2["quote"; list]

Function Calls

make-fn-call[func; args] = cons[func; args]

callee[fn-call] = first[fn-call]

arguments[fn-call] = rest[fn-call]

Conditional Expressions and Clauses

make-cond-expr[clauses] = cons["cond"; clauses]

clauses[cond-expr] = rest[cond-expr]

make-clause[pred; result] = list2[pred; result]

predicate[clause] = first[clause]

result[clause] = second[clause]

Let-expressions

local-defs[let-expr] = second[let-expr]

final-expr[let-expr] = third[let-expr]

make-let-expr[defs; expr] = list3["let"; defs; expr]

local-var[local-def] = first[local-def]

local-val[local-def] = second[local-def]

make-local-def[var; val] = list2[var; val]

Definitions

;;;
;;; The recognizers for Small Lisp definitions (i.e., function
;;; and constant definitions).
;;;
fn-def-p[def] = eq[first[def]; "defun"]

const-def-p[def] = eq[first[def]; "setc"]

;;;
;;; The access functions for function definitions.
;;;
fn-name[fndef] = second[fndef]

parameters[fndef] = third[fndef]

body[fndef] = fourth[fndef]

make-fn-def[name; parms; body] = list4["defun"; name; parms; body]

;;;
;;; The access functions for constant definitions.
;;;
const-name[constdef] = second[constdef]

const-val[constdef] = third[constdef]

make-const-def[name; value] = list3["setc"; name; value]
Updated Sat Sept. 01 2018, 18:24 by cameron.