#* A [nom] parser/compiler for simple logo-like drawing language. ABOUT This script is a parser and (hopefully) a transpiler (to python turtle graphics and maybe even postscript) for a simple drawing language. The idea is to demonstrate creating a simple language with [nom] and pep. Also the language should be easy and fun to use. The idea for the language came about after using ucblogo which is an oldish implementation of [logo] that runs on Linux. However ucblogo throws segmentation faults and also seems to have bugs in the way it renders to postscript. So it is not that useful for creating printable graphics. The [python] turtle graphics package, on the other hand produces nice printable output in postscript but I find the syntax a bit clunky. Also, I don't see the need for the object-oriented urbandict://malarkey with several turtles running in concurrent threads and sending messages to each other. NOTES It is possible that I could cut-and-paste the expression parser that is in /eg/maths.parse.pss or /eg/maths.tolatex.pss and put it into this script. Another option is just to pass-through expressions straight to python urbandict://holusbolus. Since this is a sort of toy language, I think that might be the best option. This script is following the help/error token system, that has the advantage of making the compiler or transpiler "self-documenting" I will make commands end in a semi-colon so that I can include expressions in this language like 'forward x/2;' or 'right (y+1)/2;' STATUS Starting to transpile to python. Basic commands transpiling with minimal testing. "if" etc. simple for with range is working TOKENS In my *nom-writing* experience it is a good idea to make a list of parse tokens that you are using in your grammar because it helps you to think about grammar token sequences. Some of these parse-tokens contain dots which is a nice thing about the *nom* language. You can put any char in there that is not the delimiter. char literals: ;* end of statement =* assignment ==* >=* <=* comparison word literals: if for while then do done end endif in step "..*" for ranges eg: 1..5 op.compare* a compare operator like "==" or ">=" op.add* plus/minus operator + or - op.mul* times/div operator * or / exp* an expression like "x==4" or "x+1" or "(x+1)*2" comment* text starting with # until \n number* positive integer range* a range of numbers from x to y step 1 colour* a colour name like 'blue' 'red' not quoted mod* colour modifiers like "light" or "dark" etc direction* north, south, east, NNW, etc position* eg: topleft, home, centre, center turn* eg: "around" (i.e 180 degrees) name* var and function names like "size" "time" etc quoted* quoted text command* a sort of logo command like 'forward' 'right' 'turn' statement* eg forward 20; right 90; statementset* a list of statements eg: rt 45; bk 20; if.start* eg: if x>y then forward 10 endif for.start* while.start* ERROR CHECKING ETC I would like to make the language sort-of "tolerant" so that little mistakes don't matter if they are not ambiguous. eg no semicolon if not needed. But for assignments and expressions a ; is required. SYNTAX OF THE DRAW LANGUAGE I would like to make this a bit simpler than [logo] and python turtle graphics. That means less brackets than [python] and no weird syntax like logo (eg param: ). So I would like the language to be more English-like So semicolon terminates statements (or can terminate them?). And "end" endif endwhile/endfor/... terminate blocks (but they are all synonyms) For commands like "forward 90" we could probably dispense with the terminating semi-colon, or make it optional. But if we want a syntax like "turn 90;" and "turn 90 degrees;" and "turn 2 radians;" then we need the semi-colon. * examples of the drawing language -------- head north; # set heading to north turn 90; # turn turtle 90 deg clockwise head ssw; # set heading to south south west turn 2 radians; turnback 90; # same as turn -90 turn around; # turn 180 degrees turn 90 degrees; # default is degrees print red "hello"; print "hello"; colour red; # set pen colour to red color blue; # the same but with gringo spelling colour light red; # use a colour with a modifier clr green # no semicolon required colour 22,33,44; # rgb format colour 22 33 44; # rgb format no commas? colour 22,44,255/x # rgb with expressions. text "hello"; for x in 1..5 do right 90 fd 100 done for x in 1..5 { right 90 fd 100 } # curly brace as well ? for x in 1..5 do right 90 fd 100 end for x in 1..5 do right 90 fd 100 endfor for x in 1 to 20 step 3 do done for x in 1..20 step 3 do done if x==2 then head north draw 30 endif while x>3 do x++; x=x+1; forward x; done if y >= 4 then head south end def spiral x y z : # code enddef ,,,, EXAMPLE PYTHON DRAWING CODE This is an example of what the drawlang should produce when it is transpiling to python import turtle import random import subprocess #x = float(input("Angle: ")) #y = float(input("Step: ")) #scale = int(input("Scale: ")) # size 700x650 canvas window = turtle.Screen() window.bgcolor("white") t = turtle.Turtle() t.color("tan3"); t.width(3); t.speed(500); t.penup(); t.goto(500,-300); t.pendown() t.lt(90) def scallopRow(ii, size): for i in range(ii): t.circle(size, 180) t.rt(180) t.rt(90); t.penup(); t.forward(size*2*ii-size); t.left(90); t.forward(size); t.pendown(); for i in range(12): scallopRow(20, 25) scallopRow(20, 25) t.right(90); t.penup(); t.fd(50); t.pendown(); t.left(90) #size = 0 #for i in range(scale): # size += y # t.left(x) # t.forward(size) t.begin_fill() t.fillcolor("yellow") # draw head #t.circle(100) t.end_fill() canvas = window.getcanvas() filename = 'out.ps' canvas.postscript(file=filename) #subprocess.call(['lpr', filename]) turtle.mainloop() HISTORY 26 mar 2025 improving the help and error checking. not comprehensive. 12 mar 2025 started to transpile to python. many commands working. 'for' with ranges (1..100 step 2 , or 1..100..2) 11 mar 2025 Just started this. Sketching out the script. The tricky bits might be trying to type check and resolving expressions with precedence. Some commands are recognised. The parser was partially written in a few hours. *# # a trick. an empty statement to get things started begin { add "statement*"; push; } read; [\n] { nochars; } # ignore whitespace [:space:] { while [:space:]; clear; !(eof) { .restart } } # literal char tokens eg <= >= == := etc # ; is end of statement [;] { put; add "*"; push; .reparse } [<>:!=] { while [<>:!=]; put; "=",":=","<","<=",">",">=","==","!=","<>" { # synonyms "<>" { clear; add "!="; } ":=" { clear; add "="; } put; add "*"; push; .reparse } clear; add " unknown operator '"; get; add "'\n"; put; clear; add "draw.error*"; push; .reparse } [.] { # .* for decimals? while [.]; "." { add "*"; push; .reparse } clear; add "..*"; push; .reparse } # comments [#] { whilenot [\n]; put; clear; add "comment*"; push; .reparse } # numbers (positive integers?) [:digit:] { while [:digit:]; put; clear; add "number*"; push; .reparse } [:alpha:] { while [:alpha:]; put; # literal word tokens, def defines functions "if","for","while","def","in","step" { put; add "*"; push; .reparse } # synonyms for then. Might be good to put line/char num # in then for nesting errors. "then","do","begin" { put; clear; add "then*"; push; .reparse } # all synonyms for end "end","endif","fi","endfor","endwhile","done" { put; clear; add "end*"; push; .reparse } # classic logo commands and not so classic. Lots of synonyms # todo: no keep my commands until transpile time. eg # turn, head etc. better. # move: forward without drawing, same direction # goto: go to absolute position and draw, eg: goto 10 10 # jumpto: go to absolute pos, dont draw. # head: is an absolute direction not relative like right/left/turn # aim: set heading to an absolute position?. "help","home", "forward","fd","go","move","mv","jump","back","bk","moveback","mb", "goto","jumpto","right","rt","left","lt","turn","turnback","head", "penup","pu","pendown","pd", "background","bg","bgcolor","cls","clear","clean","clearscreen", "color","colour","clr","pensize","width", "arc","circle","ellipse" { # resolve synonyms # methods.fk clear; add "#"; get; add "#"; replace "#fd#" "#forward#"; replace "#go#" "#forward#"; replace "#mv#" "#move#"; replace "#jump#" "#move#"; replace "#bk#" "#back#"; replace "#mb#" "#moveback#"; replace "#head#" "#setheading#"; replace "#rt#" "#turn#"; replace "#right#" "#turn#"; replace "#left#" "#turnback#"; replace "#lt#" "#left#"; replace "#pu#" "#penup#"; replace "#pd#" "#pendown#"; replace "#bg#" "#background#"; replace "#bgcolor#" "#background#"; replace "#cls#" "#clear#"; replace "#clean#" "#clear#"; replace "#clearscreen#" "#clear#"; replace "#color#" "#colour#"; replace "#clr#" "#colour#"; replace "#pensize#" "#width#"; clip; clop; put; clear; add "command*"; push; .reparse } # colours "blue","green","black","red","grey","gray" { # synonyms clear; add "#"; get; add "#"; replace "#grey#" "#grey#"; clip; clop; put; clear; add "colour*"; push; .reparse } # colour modifiers "light","dark","pastel" { clear; add "mod*"; push; .reparse } # directions "north","east","south","west", "northeast","northwest","southeast","southwest", "NE","NW","SE","SW" { clear; add "direction*"; push; .reparse } # positions "home","centre","center","topleft","topmiddle","topright" { clear; add "position*"; push; .reparse } # turn "around" { clear; add "turn*"; push; .reparse } # variable and function names clear; add "name*"; push; .reparse } # quoted text "'" { clear; until "'"; clip; unescape "'"; put; clear; add "quoted*"; push; .reparse } !"" { put; clear; add " Strange character '"; get; add "'\n"; put; clear; add "draw.error*"; push; .reparse } parse> # watch stack code here add "# line "; lines; add " char "; chars; add ": "; print; clear; unstack; print; stack; add "\n"; print; clear; #-------------------- # error parsing and checking. #---------------- # 1 token errors and help pop; "draw.error*" { # get the parse stack here as well clear; add "! Draw-lang syntax:"; add " near line:"; lines; add " char:"; chars; add "\n"; get; add "\n"; print; # provide help from the help* token if one was put on the stack. clear; pop; "draw.help*" { push; .reparse } quit; } # using a parse token to display help "draw.help*" { # the command or category to display help for is in the # attribute clear; swap; #"help","home", #"forward","fd","go","move","mv","jump","back","bk","moveback","mb", #"goto","jumpto","right","rt","left","lt","turn","turnback","head", #"penup","pu","pendown","pd", #"background","bg","bgcolor","cls","clear","clean","clearscreen", #"color","colour","clr","pensize","width", #"arc","circle","ellipse" { # flow control: for,(while),if,begin,then # functions: def #* document all of these structures in the draw language. char literals: ;* end of statement =* assignment ==* >=* <=* comparison word literals: if for while then do done end endif in step "..*" for ranges eg: 1..5 op.compare* a compare operator like "==" or ">=" op.add* plus/minus operator + or - op.mul* times/div operator * or / exp* an expression like "x==4" or "x+1" or "(x+1)*2" comment* text starting with # until \n number* positive integer range* a range of numbers from x to y step 1 colour* a colour name like 'blue' 'red' not quoted mod* colour modifiers like "light" or "dark" etc direction* north, south, east, NNW, etc position* eg: topleft, home, centre, center turn* eg: "around" (i.e 180 degrees) name* var and function names like "size" "time" etc quoted* quoted text command* a sort of logo command like 'forward' 'right' 'turn' statement* eg forward 20; right 90; statementset* a list of statements eg: rt 45; bk 20; if.start* eg: if x>y then forward 10 endif for.start* while.start* *# "commands.general","commands","all" { swap; add " commands: There are several built-in commands in the draw-language such as: forward, back, penup, pendown, right. Many of these command names are based on the command names for the orginal 'turtle drawing' language LOGO or on the name of the python turtle drawing package. Some commands require one or more arguments, either a number or some text (in quotes) or an expression like 360/6 Commands should be terminated with a semicolon. eg: turn 360/10; home; "; } "arguments","syntax","commands","all" { swap; add " arguments: Some commands like *forward* , *right* etc require one or more arguments, either a number or some text (in quotes) or an expression like 360/6 Commands should be terminated with a semicolon. eg: turn 360/10; home; "; } "numbers","syntax","commands","all" { swap; add " numbers: Some commands like *forward* , *right* etc require one or more numerical arguments. The number comes after the command and can also be part of a numerical expression like (100/10 + 20). eg: forward 100+20; "; } # movement and all are categories, forward is the command "home","movement","all" { swap; add " home <x>; move the turtle to the centre of the screen drawing if the 'pen' is down. The orientation or direction of the turtle is unchanged. Synonyms none eg: home or home; "; } # or just use "commands.summary" category "commandlist","list","commands","all" { swap; add " # (help) # head x - set absolute heading # goto x y - move to position (x,y) and draw # moveto x y - move to position (x,y) dont draw # more ... "; } # a short summary of the command. usable in a summary list. "forward.short","commands.summary","all" { add " forward <x>; # move and maybe draw forwards x pixels "; } # movement and all are categories, forward is the command "forward","go","movement","all" { swap; add " forward <x>; go <x>; move the turtle forward <x> pixels (or some other unit), drawing if the 'pen' is down. The orientation or direction of the turtle is unchanged. Synonyms 'fd' 'go' eg: forward 20; "; } "back.short","commands.summary","all" { add " back <x>; # move and maybe draw backards x pixels "; } # bk is an alias for back "back","bk","movement","all" { swap; add " back <x>; bk <x>; move the turtle back <x> pixels (or some other unit), drawing if the 'pen' is down. The orientation or direction of the turtle is unchanged. eg: forward 20; "; } "move.short","commands.summary","all" { add " move <x>; # move x pixels but dont draw"; } "move","jump","movement","all" { swap; add " move <x>; jump <x>; move the turtle forward <x> pixels (or some other unit), in the current direction, without drawing even if the 'pen' is currently 'down'. The orientation or direction of the turtle is unchanged. Synonyms 'jump' 'mv' eg: move 20; "; } "moveback.short","commands.summary","all" { add " moveback <x>; # moveback x pixels but dont draw"; } "moveback","mb","movement","all" { swap; add " moveback <x>; jumpback <x>; move the turtle backwards <x> pixels (in the opposite direction to the way the turtle is currently facing) without drawing even if the 'pen' is currently 'down'. The orientation or direction of the turtle is unchanged. "; } "goto.short","commands.summary","all" { add " goto <x> <y>; # goto position x y and draw if pendown."; } "goto","jumpto","movement","all" { swap; add " goto <x> <y>; jumpto <x> <y>; move the turtle backwards <x> pixels (in the opposite direction to the way the turtle is currently facing) without drawing even if the 'pen' is currently 'down'. The orientation or direction of the turtle is unchanged. "; } "end","endif","fi","endfor","endwhile","done", "flow","blocks","all" { swap; add " end,endif,fi,endfor,endwhile,done: are all ways to end a block of code in the drawing language. Actually all these words are synonyms and interchangable. eg: # draw a hexagon for 1..6 begin fd 100; turn 60; endfor "; } "then","do","begin", "flow","blocks","all" { swap; add " then,do,begin: are all ways to start a block of code in the drawing language. Actually all these words are synonyms and interchangable. eg: # draw a hexagon for 1..6 begin fd 100; turn 60; endfor "; } "for","flow","all" { swap; add " for for is a loop flowcontrol word in drawbasic. eg: for 1..6 do fd 50; rt 60; done eg: for y in 1..100 step 5 do fd y; rt y; done "; } "if","flow","all" { swap; add " for if is a conditional flow control word in drawbasic. eg: if x==5 then fd 20; endif "; } "semicolon",";","flow","all" { swap; add " semicolon ; is the way that most commands in the draw-language are terminated. It is possible that some simple commands such as 'head north' (ie where the parameter is a named direction) will not have to be terminated with ; eg: fd 20; "; } print; # if this help is called by an error token we dont have to # quit immediately. quit; } #---------------- # 2 token errors pop; # a semicolon cant start a token sequence B";*".!";*" { clear; add "semicolon"; put; clear; add "draw.help*"; push; clear; add "# misplaced semicolon ? \n"; put; clear; add "draw.error*"; push; .reparse } # an endblock cant start a token sequence B"end*".!"end*" { clear; add "end"; put; clear; add "draw.help*"; push; clear; add "# misplaced end or poorly formed block ? \n"; put; clear; add "draw.error*"; push; .reparse } "command*number*" { clear; get; "home","penup","pendown","clear" { clear; add "# command '"; get; add "' does not take argument"; put; clear; add "draw.error*"; push; .reparse } clear; add "command*number*"; } "command*direction*" { clear; get; "turn" { clear; add "setheading"; } !"setheading" { clear; add "# command '"; get; add "' does not take direction argument. "; put; clear; add "draw.error*"; push; .reparse } clear; add "command*direction*"; } "statementset*number*","statement*number*","number*" { clear; add "numbers"; put; clear; add "draw.help*"; push; clear; add "# number argument '"; get; add "' with no command? "; put; clear; add "draw.error*"; push; .reparse } #"statement*number*","statementset*number*" { # clear; add "# misplaced number? \n"; put; # clear; add "draw.error*"; push; .reparse #} #---------------- # 3 token errors pop; # end of error parsing #---------------- push;push;push; #---------------- # grammar token parsing #---------------- # expression parsing. I will parse expressions separately for # the sake of readability #---------------- # 1 token arithmetic expressions pop; #---------------- # 2 token arithmetic expressions pop; #---------------- # 3 token arithmetic expressions pop; # this is just for testing if. Actually for expressions we need # some 'lookahead' "name*==*number*" { clear; get; ++; get; ++; get; --; --; put; clear; add "exp*"; push; .reparse } #---------------- # 4 token arithmetic expressions pop; #---------------- # 5 token arithmetic expressions pop; push;push;push;push;push; # end of arithmetic expression parsing #---------------- # general parsing #---------------- # 1 token reductions or compilations pop; "command*" { clear; get; "penup","pendown","home","clear" { clear; add "t."; get; replace "clear" "clearscreen"; add "();"; put; clear; add "statement*"; push; .reparse } clear; add "command*"; } #---------------- # 2 token reductions pop; "command*;*" { clear; get; # print what every command does. Help must have a semicolon "help" { clear; add "# (help) \n"; add "# head x - set absolute heading \n"; add "# goto x y - move to position (x,y) and draw\n"; add "# moveto x y - move to position (x,y) dont draw \n"; add "# more ... \n"; put; clear; add "statement*"; push; .reparse } "home","penup","pendown","clear" { clear; add "statement*"; push; .reparse } # all other commands require one or more arguments. # move this to the error block? clear; add "arguments"; put; clear; add "draw.help*"; push; clear; add "# missing argument for command '"; get; add "'? "; put; clear; add "draw.error*"; push; .reparse } # just pass comments through E"comment*".!"comment*" { # this trick in case we have: comment*comment* replace "*comment*" "*"; push; --; get; add "\n"; ++; get; --; put; ++; clear; .reparse } # lets allow superfluous semicolons, just ignore them "statement*;*","statementset*;*" { clip; clip; push; .reparse } "statement*statement*","statementset*statement*" { clear; get; add "\n"; ++; get; --; put; clear; add "statementset*"; push; .reparse } # eg: colour green or color green or colour green; "command*colour*" { clear; get; # the drawing pen colour "colour" { clear; # python takes colour strings or rgb i.e: t.pencolor(22,33,44); add 't.pencolor("'; ++; get; --; add '");'; put; clear; add "statement*"; push; .reparse } # background colour "background" { clear; # python takes colour strings or rgb i.e: t.pencolor(22,33,44); add 'turtle.Screen().bgcolor("'; ++; get; --; add '");'; put; clear; add "statement*"; push; .reparse } clear; add "# incorrect colour command: \n"; put; clear; add "draw.error*"; push; .reparse } # eg: turn north, not requiring semicolon here "command*direction*" { clear; get; # allow eg: turn north; "turn" { clear; add "setheading"; } "setheading" { clear; ++; get; # convert directions like north to angles "north" { clear; add "0"; } "east" { clear; add "90"; } "south" { clear; add "180"; } "west" { clear; add "270"; } "northeast","NE" { clear; add "45"; } "northwest","NW" { clear; add "315"; } "southeast","SE" { clear; add "135"; } "southwest","SW" { clear; add "225"; } ![0-9] { clear; add "# incorrect compass direction '"; get; add "'"; put; clear; add "draw.error*"; push; .reparse } put; --; clear; add "t.setheading("; ++; get; --; add ");"; put; clear; add "statement*"; push; .reparse } clear; add "# incorrect direction command: \n"; put; clear; add "draw.error*"; push; .reparse } # eg: goto centre / goto topleft todo: "command*position*" { clear; get; "goto","jumpto" { add " "; ++; get; add ";"; --; put; clear; add "statement*"; push; .reparse } clear; add "# incorrect command: \n"; put; clear; add "draw.error*"; push; .reparse } #---------------- # 3 token reductions pop; # just make later reductions less wordy. "then*statement*end*" { clear; add "then*statementset*end*"; } # ranges eg: 1..7 # compile immediately to python "number*..*number*" { clear; add "range("; get; add ","; ++; ++; get; add ")"; --; --; put; clear; add "range*"; push; .reparse } # ranges eg: 1..100 step 5 or 1..100..5 # add a step to code 'range(1,50) -> range(1,50,2) "range*..*number*","range*step*number*" { clear; get; clip; add ","; ++; ++; get; add ")"; --; --; put; clear; add "range*"; push; .reparse } # eg: colour light green "command*mod*colour*" { clear; get; "colour" { add " "; ++; get; add " "; ++; get; --; --; add ";"; put; clear; add "statement*"; push; .reparse } clear; add "# incorrect colour command: \n"; put; clear; add "draw.error*"; push; .reparse } # "forward","fd","move","mv","jump","back","bk","moveback","mb", # "goto","jumpto","right","rt","left","lt","turn","head", # "penup","pu","pendown","pd", # "color","colour","clr","pensize","width", # if not ambiguous resolve simple commands with no colon "command*number*;*" { clear; get; "forward","back","turn","turnback","head","width" { clear; add "t."; get; replace "t.turnback" "t.left"; replace "t.turn" "t.right"; replace "t.head" "t.setheading"; add "("; ++; get; add ");"; --; put; clear; add "statement*"; push; .reparse } "move" { add "t.penup(); t.forward("; ++; get; add "); t.pendown();"; --; put; clear; add "statement*"; push; .reparse } "moveback" { add "t.penup(); t.back("; ++; get; add "); t.pendown();"; --; put; clear; add "statement*"; push; .reparse } # allowing traditional colour numbers "colour" { clear; ++; get; replace "0" "black"; replace "1" "blue"; replace "2" "green"; replace "3" "cyan"; replace "4" "rereplace"; replace "5" "magenta"; replace "6" "yellow"; replace "7" "white"; replace "8" "brown"; replace "9" "tan"; replace "10" "forest"; replace "11" "aqua"; replace "12" "salmon"; replace "13" "purple"; replace "14" "orange"; replace "15" "grey"; put; clear; add 't.pencolor("'; get; --; add '");'; put; clear; add "statement*"; push; .reparse } clear; add "command*number*"; } #---------------- # 4 token reductions pop; # simple assignments "name*=*number*;*" { clear; get; add " "; ++; get; add " "; ++; get; add ";"; --; --; put; clear; add "statement*"; push; .reparse } # we need look ahead here because of precedence # eg x in 1..10 "name*in*range*then*" { clear; get; add " in "; ++; ++; get; --; --; put; # dont need to transfer 'then' attribute clear; add "name.range*then*"; } # eg: goto 20 20; # but this should have a semi-colon because the number could # be an expression "command*number*number*;*" { clear; get; "goto","jumpto" { add " "; ++; get; add " "; ++; get; add ";"; --; --; put; clear; add "statement*"; push; .reparse } clear; add "# incorrect command: \n"; put; clear; add "draw.error*"; push; .reparse } #---------------- # 5 token reductions pop; # allow anonymous range # eg: for x in 1..10 do fd 20; rt 90; done "for*name.range*then*statementset*end*" { # indent python code. clear; ++; ++; ++; add "\n"; get; replace "\n" "\n "; put; --; --; --; clear; add "for "; ++; get; add ":"; ++; ++; get; --; --; --; put; clear; add "statement*"; push; .reparse } # allow anonymous range # eg: for 1..10 do fd 20; rt 90; done "for*range*then*statementset*end*" { # indent statementset code. clear; ++; ++; ++; add "\n"; get; replace "\n" "\n "; put; --; --; --; clear; add "for _ in "; ++; get; add ":"; ++; ++; get; --; --; --; put; clear; add "statement*"; push; .reparse } # reducing if blocks "if*exp*then*statementset*end*" { # indent python code. clear; ++; ++; ++; add "\n"; get; replace "\n" "\n "; put; --; --; --; clear; add "if "; ++; get; add ":"; ++; ++; get; --; --; --; put; clear; add "statement*"; push; .reparse } #---------------- # 6 token reductions pop; # function definitions, todo "def*name*parameters*then*statementset*end*" { # indent python code. clear; ++; ++; ++; add "\n"; get; replace "\n" "\n "; put; --; --; --; clear; add "if "; ++; get; add ":"; ++; ++; get; --; --; --; put; clear; add "statement*"; push; .reparse } #---------------- # 7 token reductions pop; push;push;push;push;push;push;push; (eof) { pop; pop; # python code cannot have an initial indent apparently. "statement*","statementset*" { clear; add ' # Python turtle drawing code transpiled by "nomlang.org/eg/drawlang.pss" # eg: pep -f eg/drawlang.pss -i "colour green forward 100" > test.py # eg: pep -f eg/drawlang.pss drawing.txt > test.py # (drawlang syntax seems ok) # # install python-tk first then run this code with # python thisfile.py # An image of the drawing is saved in out.ps # the python turtle canvas size maybe = 700x650 # the python turtle starts pointing east, but the logo turtle # points north import turtle import random import subprocess window = turtle.Screen() window.bgcolor("white") t = turtle.Turtle() # make 0 north turtle.mode("logo"); t.color("tan3"); t.width(3); t.speed(500); #t.penup(); t.goto(500,-300); t.pendown(); #t.lt(90) #---------------------- # start of transpiled code '; get; add ' # end of transpiled code #---------------------- # a python fill example (like "closepath" in postscript?) # t.begin_fill(); t.fillcolor("yellow"); t.circle(100); t.end_fill() # same drawing in file "out.ps" canvas = window.getcanvas(); filename = "out.ps" canvas.postscript(file=filename) # dont close the python tk drawing window. turtle.mainloop() # python turtle drawing code transpiled by # nomlang.org/eg/drawlang.pss # (drawlang syntax seems ok) \n'; add "\n"; print; quit; } push; push; add " drawlang: script didnt parse well.\n"; put; clear; add "draw.error*"; push; .reparse }