Version 1 of Lispy

Updated 2009-09-23 17:29:24 by sarnold

Sarnold 2009-09-20 - Yet another week-end fun project: parsing S-EXP to Tcl.

Usage:

 lispy::parse string
% p "incr 'foo 3"
incr foo 3
% p "lindex '(1 2 3) 1"
lindex {1 2 3} 1
% p "add x y"
add $x $y
% p "+ w t"
+ $w $t

Lispy syntax

Each command starts with a command name and continues with arguments. A simple word (which starts with a letter or a colon) is taken as value (token becomes $token) if it is not the first word of the command. As in Lisp, parentheses match nested commands:

 add (mul x y) (mul z t)
 -> add [mul $x $y] [mul $z $t]

To give expressions, you may brace them, as in normal Tcl code.

 expr {$x +1.2}

Numbers are put verbatim

 1.2 -> 1.2

Options are put verbatim, as with command names starting with special chars.

 -textvariable
 ++
 *

To start the command with a variable value, you will have to put a dollar sign in front of it.

 $widget 'add 'circle 1 1 10 10
 -> $widget add circle 1 1 10 10

String interpolation is possible with double-quoted strings which are put verbatim. However it is currently not possible to put double-quotes inside strings.

 puts "Hello my $friend"

Lists are built with (single) quotes in front of parenthesized expressions, and can be nested.

 lindex '(1 2 3 4) 0
 -> lindex {1 2 3 4} 0

Blocks are built with the special prefix "begin":

 if {$d > 0} (begin (puts "Success"))
 -> if {$d > 0} {
    puts "Success"
 }

Comments are allowed but they start with semicolons and end at the end of line.Commands are not required to fit on one line.


package provide lispy 0.4
package provide lispy 0.3

namespace eval lispy {}

proc lispy::hasescape string {
        foreach {regex token} {
                {\-?[0-9]+(\.[0-9]*)?([eE][+-]?[0-9]+)?} number
                {".*"} string
                {[a-zA-Z_:][A-Za-z0-9_:]*} var
                {{.*}} string
                {\$[a-zA-Z_:][A-Za-z0-9_:]*} dvar
                {'[a-zA-Z_][A-Za-z0-9_]*} tag
                {[+\-\*/\!=<>%\|&A-Aa-z_:\.][+\-\*/\!=<>%\|&A-Aa-z_:\.0-9]*} cmd
                {[+\-\*/\!=<>\|&A-Aa-z_:][+\-\*/\!=<>\|&A-Aa-z_:0-9]*} cmd
                {\?\(} expr
                {\(} open
                {\)} close
                {;.*\n} comment
                {[\t ]+} blank
        } {
                if {[regexp ^$regex $code match]} {
                                        if {$token eq "string"} {
                        return [list $token $match]
        }
        error "invalid syntax at: '$code'"
}

proc lispy::ismath x {
        proc parse-$name var [string map [list %body [list $body]] {
                upvar 1 code $var
                set res ""
                while {$code ne ""} {
                        foreach {tag string} [lexeme $code] break
                        set code [string range $code [string length $string] end]
                        if {$tag eq "comment"} continue
                        switch -- $tag %body
                }
                return $res
        }]
}

lispy::make-parser command {
        blank - number - string - dvar {
        blank - number - string - var - dvar - cmd {
                append res $string
                return "$res[parse-arguments code]"
        }
        var - cmd {
                append res [string range $string 1 end]
                return "$res[parse-arguments code]"
        }
        open {
                append res [parse-command code]
                return "$res[parse-arguments code]"
        }
        close {
                return $res
        }
}

# start subcommands for math expressions
        blank {
                append res \n
                continue
        }
        number - string - var - cmd - tag - dvar {
                error "block expected"
        }
        open {
                append res \t[parse-command code]\n
        }
        close {
                return \{$res\}
        }
}
                
lispy::make-parser arguments {
        blank - number - string - cmd - dvar {
                # put verbatim
                append res $string
        }
        tag {
                append res [string range $string 1 end]
        }
        var {
                append res \$$string
        }
        open {
                append res "\[[parse-command code]\]"
                append res \[[parse-command code]\]
        expr {
                return $res
        }
        list {
                append res [parse-list code]
        }
        begin {
                append res [parse-block code]
        }
}
# math expressions
lispy::make-parser list {
        blank - number - string - cmd - var - dvar {
                # put verbatim
                append res $string
        }
        tag {
                append res [string range $string 1 end]
        }
        close {
                return \{$res\}
        }
        list - open {
                append res [parse-list code]
        }
}
                
proc lispy::parse code {
        lispy::parse-command code
}

interp alias "" p "" lispy::parse