[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 <>Category Syntax | Category Language