> a = []
-> []
> a = [5, 6, "hi", 'symb', 3.4]
-> [5, 6, "hi", 'symb', 3.4]
> a[3]
-> symb
> subseq(a, 3)
-> ['symb', 3.4]
> subseq(a, 1, 3)
-> [6, "hi"]
> a.last()
-> 3.4
> a.insert(1, "new")
-> [5, "new", 6, "hi", 'symb', 3.4]
> a.append("newer")
-> [5, "new", 6, "hi", 'symb', 3.4, "newer"]
> a.unappend()
-> "newer"
> a
-> [5, "new", 6, "hi", 'symb', 3.4]
> a.setlen(3)
-> [5, "new", 6]
> len(a)
-> 3
> a.uninsert(1)
-> [5, 6]
> a.reverse()
-> [6, 5]
> a
-> [6, 5]
> b = a
-> [6, 5]
> a is b
-> t
> a.append(4)
-> [6, 5, 4] a is changed
> b
-> [6, 5, 4] note that b was also changed
> b = a.copy()
-> [6, 5, 4]
> a is b
-> nil now, b is a different array
> a.reverse()
-> [4, 5, 6] changing a does not change b
> b
-> [6, 5, 4]
> d = {}
-> {}
> d['address'] = "5000 Forbes Ave."
-> 5000 Forbes Ave.
> d['city'] = "Pittsburgh"
-> Pittsburgh
> d['state'] = "PA"
-> PA
> d['zip'] = "15213"
-> 15213
> print d['address']; "\n"; d['city']; ",", d['state'], d['zip']
5000 Forbes Ave.
Pittsburgh, PA 15213
-> nil
> d.keys()
-> ['address', 'city', 'state', 'zip']
> d['country']
runtime exception handler called
exception is: bad key
frame variables: {}
frame pc: 4
frame method: <immediate command 0>
frame class: nil
bad key, debugger invoked.
Method: <immediate command 0>, PC: 4, Line: 10, File: <stdin>
1> >
debugger reads >
Resume execution...
> >
> d.get('country', "USA") // provides default for attribute
-> USA
>
def chars()
# make characters from numbers
var char_a = ord("a") // ascii code for "a"
var alphabet = ""
for i = 0 to 26
alphabet = alphabet + chr(char_a + i)
print alphabet
# strings can be accessed as arrays
display "string access", "abcd"[2]
# some more string functions (apply to chars too of course)
display "string fns", toupper("abcd"), tolower("NeXT")
# find a substring
display "find", find(alphabet, "qrst"), find(alphabet, "qed")
# make a number from a string
display "convert to number", int("123"), real("123.456")
data = ["This", "is", "an", "array", "of", "words", "to", "search."]
# linear search with for loop, return index of word in data or -1
#
def search_with_for(data, word)
for i = 0 to len(data)
if data[i] == word
return i
return -1
# for-in loop
#
def search_with_for_in(data, word)
var i = 0
for w in data
if w == word
return i
i = i + 1
return -1
# special form of for eliminates call to len
#
def search_with_for_at(data, word)
for w at i in data // i is the index of w in data
if w == word
return i
return -1
# for simple equality searching, use index:
#
def search_with_index(data, word)
return data.index(word)
# test it
#
display "test 1", search_with_for(data, "of")
display "test 2", search_with_for_in(data, "of")
display "test 3", search_with_for_at(data, "of")
display "test 4", search_with_index(data, "of")
# --- init.srp ---You can of course split your program into multiple modules and load multiple files from init.srp.
load "debug" // the ".srp" extension is assumed
load "sequencer"
# --- sequencer.srp ---If all modules use this convention, strparse.srp will be loaded the first time it is needed and no more, making initialization faster.
require "strparse" // the ".srp" extension is assumed
... other requires ...
... class and function definitions ...
class Account:
var balance
# init() is called automatically when an Account is created
def init(initial_deposit)
balance = initial_deposit
def deposit(x)
balance = balance + x
# note: you could also write this.balance = ...
def withdraw(x)
if balance >= x
balance = balance - x
return balance
else
return false
# test
#
def account_test()
account = Account(0)
account.deposit(5)
display "account_test", account, account.balance
if not account.withdraw(10)
print "don't have $10"
# run it ...
> account_test()
account_test: account = <obj@0x205a48>, account.balance = 5
don't have $10
-> nil
>
class Account:
var balance
def init(initial_deposit)
balance = initial_deposit
def deposit(x)
balance = balance + x
def withdraw(x)
if balance >= x
balance = balance - x
return balance
else
return false
# extend the Account class with a name and show method
class Named_account(Account)
var name
def init(a_name, initial_deposit)
# first call superclass's init
super.init(initial_deposit)
name = a_name
def show()
print name, "has a balance of $"; balance
# test
#
def account_test()
account = Account(0)
account.deposit(5)
display "account_test", account, account.balance
if not account.withdraw(10)
print "don't have $10"
def named_account_test()
named_account = Named_account("Klaatu", 1000)
named_account.show()
named_account.withdraw(300) // inherited method
named_account.show()
# run it ...
> account_test()
account_test: account = <obj@0x205a48>, account.balance = 5
don't have $10
-> nil
> named_account_test()
Klaatu has a balance of $1000
Klaatu has a balance of $700
(setq auto-mode-alist (cons
'("\\.srp$" . serpent-mode) auto-mode-alist))(setq interpreter-mode-alist (cons '("serpent" .
serpent-mode) interpreter-mode-alist))(autoload 'serpent-mode "serpent-mode" "Serpent editing
mode." t)def string_gtr(s1, s2)Example input (unsorted.txt) and output files (sorted.txt and by-length.txt) are online.
s1 > s2
def filesort(in_file, out_file)
var inf = open(in_file, "r")
if not inf
return "Could not open " + in_file
var outf = open(out_file, "w")
if not outf
return "Could not open " + out_file
var content = inf.readlines()
inf.close()
content.sort('string_gtr')
outf.writelines(content)
outf.close()
# test it
#
filesort("unsorted.txt", "sorted.txt")
# another version that sorts by line length
#
def longer(s1, s2)
len(s1) > len(s2)
def filesort_by_len(in_file, out_file)
var inf = open(in_file, "r")
if not inf
return "Could not open " + in_file
var outf = open(out_file, "w")
if not outf
return "Could not open " + out_file
var content = inf.readlines()
inf.close()
content.sort('longer')
outf.writelines(content)
outf.close()
# test it
#
filesort_by_len("unsorted.txt", "by-length.txt")
# optargs.srp -- optional parameter examples
#
# Roger B. Dannenberg
# Jan, 2010
#---- example of "rest" parameter ----
#
# form sum of all arguments
#
def sum(rest a)
var s = 0
for x in a
s = s + x
s // just "s", or you can say "return s"
# test it
#
display "call sum", sum(1, 2, 3, 4, 5)
#---- example of optional parameter ----
#
# convert to string and print with arbitrary "quote" strings
#
def print_quoted(s, optional quote = "\"")
print quote + str(s) + quote
# test it
#
def print_quoted_tests()
print "test: print_quoted(23) prints ... ";
print_quoted(23)
print "test: print_quoted(23, \"'\") prints ... ";
print_quoted(23, "'")
print "test: print_quoted(23, \"\") prints ... ";
print_quoted(23, "")
print "test: print_quoted(\"hello world\", \"|\") prints ... ";
print_quoted("hello world", "|")
print_quoted_tests()
#---- example of keyword parameter ----
#
# print the time
#
def print_within(s, keyword prefix = "", keyword suffix = "")
print prefix; str(s); suffix
# test it
#
def print_within_tests()
print "test: print_within(23) prints ... ";
print_within(23)
print "test: print_within(\"My Paragraph\", prefix = \"<p>\") prints ... ";
print_within("My Paragraph", prefix = "<p>")
print "test: print_within(\"My Heading\", "
print " prefix = \"<h1>\", suffix = \"</h1>\") prints ... ";
print_within(23, prefix = "<h1>", suffix = "</h1>")
print_within_tests()
#---- example of dictionary parameter ----
#
# print keywords and their values
#
def print_args(required_parameter, dictionary d)
var keys = d.keys()
print "required_parameter =", required_parameter
for k in keys
print k, "=", d[k]
# test it
#
def print_args_test()
print "calling print_args(a = 1, b = 2, c = 3) ..."
print_args(123, a = 1, b = 2, c = 3)
print "... done"
print_args_test()
// String_parser is not built-in, so you must load the
// code if it has not already been loaded...
require "strparse"
# return an array of fields from an input string
#
def fields(s)
var sp = String_parse(s) // create and initialize the
// String_parser object
var result = [] // create empty array to accumulate results
sp.skip_space() // this is not necessary -- just illustrating
var field = sp.get_nonspace() // parse out a field
while field != "" // get all the fields
result.append(field) // append each field to array
field = sp.get_nonspace()
return result
# test it
#
print "fields(\"This is a test 123 %$&!\") returns",
print fields("This is a test 123 %$&!")
> def adder(x, y) // define a simple function
> x + y
> apply('adder', [5, 7]) // function is a Symbol, args are in an array
-> 12
> funcall('adder', 5, 7) // function is a Symbol, args in ordinary list
-> 12
> fn = 'adder' // you can of course use variables so that functions
-> 'adder'
> args = [4, 9] // and arguments depend on computation...
-> [4, 9]
> apply(fn, args)
-> 13
> require "strparse" // load a class
> s = String_parse("test string")
> meth = 'get_nonspace'
> send(s, meth)
-> test
> sendapply(s, meth, []) // pass an empty array if there are no arguments
-> string
# the fast way to build a big string
def flatten_example()
var text = []
for i = 0 to 1000
text.append(str(i)) // appending to arrays is efficient
text.append(",")
// now "flatten" the strings in the array to one string
return flatten(text)
# test it
#
print flatten_example()
# nested arrays can be flattened too
print flatten(["a", "b", ["c", "d"], "e"])
def str_repr()Try it out:
display "str", str("abc"), len(str("abc"))
display "repr", repr("abc"), len(repr("abc"))
// the backslash is used to insert special characters in a string
// \n is newline, \t is tab, \\ is one backslash, \r is return
// \" is a double quote, \' is a single quote
var tricky = "quoted: \"abc\"" // the string is: quoted: "abc"
display "embedded quotes problem", repr(tricky)
// repr(tricky) does not escape the quotes, so the result is not
// machine readable
// print a quoted string with escaped quotes:
display "a solution", string_escape("quoted:\"abc\"", "\"")
// how to print a symbol with embedded escaped quotes:
display "symbol", string_escape(str('it\'s'), "'")
> load "str-repr"
str: str("abc") = abc, len(str("abc")) = 3
repr: repr("abc") = "abc", len(repr("abc")) = 5
embedded quotes problem: repr(tricky) = "quoted: "abc""
a solution: string_escape("quoted:\"abc\"", "\"") = "quoted:\"abc\""
symbol: string_escape(str('it\'s'), "'") = 'it\'s'
-> nil
require "wxserpent"Buttons can invoke methods as well as call functions:
// WXS_DEFAULT_WINDOW is the initial window created automatically
// when you run wxserpent. It's the parent of my_button.
// Parameters are parent, label, x, y, width, height:
my_button = Button(WXS_DEFAULT_WINDOW, "Click Me", 25, 5, 70, 25)
// Tell button who to call when pressed:
my_button.method = 'my_button_handler' // function to call
// Every handler takes 4 parameters:
def my_button_handler(obj, event, x, y)
// but for buttons, parameters are not so useful
print "my_button was clicked. As if you didn't know."
require "wxserpent"
// declare a simple class and instantiate an object
class Counter
var count
def init()
count = 0
def increment(rest ignore) // ignore all parameters
count = count + 1
display "Counter", count
counter = Counter() // create an instance
// make a button to call the increment method
// notice that increment method must accept the 4 parameters
// passed to all graphical control handlers
obj_button = Button(WXS_DEFAULT_WINDOW, "Invoke a Method", 25, 75, 150, 25)
obj_button.target = counter
obj_button.method = 'increment'
require "wxserpent"Notice that 'my_slider_handler' is used (assigned to my_slider.method) before the function is defined. This is valid because 'my_slider_handler' is just a symbol. It exists as soon as the compiler sees it. The slider cannot generate any events or handler calls until the file is fully loaded and compiled because Serpent is non-preemptive: there is no other thread to make the call. By the time a handler can be called, the function my_slider_handler() will be defined.
// WXS_DEFAULT_WINDOW is the initial window created automatically
// when you run wxserpent. It's the parent of my_slider, but you
// could open another window and put the slider there instead.
// Parameters are parent, minimum value (an integer), maximum value
// (an integer), initial value (an integer), x, y, width, height
// (all coordinates and sizes are integers):
my_slider = Slider(WXS_DEFAULT_WINDOW, 0, 127, 0, 5, 150, 100, 20)
my_slider.method = 'my_slider_handler' // function to call
def my_slider_handler(obj, event, x, y)
print "my_slider hit", event, "value:", x
my_gauge.set_value(x) // forward reference to object created next...
// A Gauge is an "analog" value indicator something like one bar of a
// bar graph. The parameters are parent window, range (an integer), and
// x, y, width, height bounding box coordinates (all integers):
my_gauge = Gauge(WXS_DEFAULT_WINDOW, 127, 5, 175, 100, 10)
require "debug"
require "wxserpent"
MY_CANVAS_WIDTH = 500
MY_CANVAS_HEIGHT = 2000
// create a subclass of Scrolled_canvas
class My_canvas (Scrolled_canvas)
def init(parent, x, y, w, h)
super.init(parent, x, y, w, h)
set_virtualsize(MY_CANVAS_WIDTH, MY_CANVAS_HEIGHT)
def paint(flag):
for x = 50 to MY_CANVAS_WIDTH by 150:
for y = 50 to MY_CANVAS_HEIGHT by 100:
set_brush_color("YELLOW") // so text is visible
draw_rectangle(x, y, 100, 20)
draw_text(x + 3, y + 3, "(" + str(x) + ", " + str(y) + ")")
// implement the action of the button. Catch all parameters in "rest" so
// you can call do_jump() with no parameters, OR call it as an event
// handler that gets obj, event, x, and y parameters...
def do_jump(rest ignore):
scroll(int(MY_CANVAS_WIDTH / 2), int(MY_CANVAS_HEIGHT / 2))
my_canvas = My_canvas(WXS_DEFAULT_WINDOW, 0, 0, 200, 200)
// make a button that jumps to a middle location
jump = Button(WXS_DEFAULT_WINDOW, "Jump", 220, 20, 100, 30)
jump.method = 'do_jump'
jump.target = my_canvas
require "debug"
require "wxserpent"
// create a subclass of Canvas -- you need to subclass Canvas in
// order to override the paint() method, which is how you make
// custom graphics
//
class Drawing (Canvas)
// the state is an array of lines, where each line is an array
// of x1, y1, x2, y2 coordinates for the endpoints of lines
var lines
var mouse_is_down
// override init() in order to intialize the state of this
// subclass, but pass the usuall parent, x, y, w, h parameters
// to the init() method defined in the Canvas class so that
// the Canvas state is also initialized.
//
def init(parent, x, y, w, h)
super.init(parent, x, y, w, h)
lines = []
// override paint to do custom drawing
//
def paint(x)
for line in lines
draw_line(line[0], line[1], line[2], line[3])
// override handle to handle mouse events
//
def handle(obj, event, x, y)
var line
if event == WXS_LEFT_DOWN
if mouse_is_down
return
line = [x, y, x, y]
lines.append(line)
mouse_is_down = true
elif event == WXS_MOVE and mouse_is_down
// change the end-point of the last line to follow the mouse
line = lines.last()
line[2] = x
line[3] = y
// It might be possible to receive a mouse up without a mouse down
// E.g. the mouse down might be captured by the window system to
// change the focus to this canvas, and the mouse down might not
// be delivered to the canvas. In that case, ignore the mouse-up
// event because we have not received a mouse down yet.
elif event == WXS_LEFT_UP and mouse_is_down
line = lines.last()
line[2] = x
line[3] = y
mouse_is_down = false
else // could be keyboard input
return // no state change, so return
// State has changed. Request redraw.
refresh(true)
// create a canvas to draw on
drawing = Drawing(WXS_DEFAULT_WINDOW, 0, 0, 200, 200)
require "debug"
require "wxserpent"
class My_window (Window):
var canvas
def init(title, x, y, w, h):
super.init(title, x, y, w, h)
canvas = My_canvas(this.id, 0, 0, w, h)
canvas.refresh(t)
def on_size(x, y)
display "on_size", x, y
canvas.set_size(x, y)
canvas.refresh(t)
// create a subclass of Canvas
class My_canvas (Canvas)
def paint(flag):
var x = get_width()
var y = get_height()
draw_text(30, 3, "(" + str(x) + ", " + str(y) + ")")
// draw an arrow pointing to lower right corner
draw_line(0, 0, x, y)
draw_line(x - 30, y - 15, x, y)
draw_line(x - 15, y - 30, x, y)
my_window = My_window("Resizable Window With Canvas", 100, 100, 400, 400)
def main()
zmq_init() // prepare to use zmq
var socket = zmq_open_reply()
display "bind step", zmq_bind(socket, "tcp", "*", 5555)
for i = 0 to 10
var req = zmq_recv_block(socket)
print "Server:", req, "-> World"
zmq_send(socket, "World")
zmq_close(socket)
zmq_term()
main()
exit()
def main()
zmq_init() // prepare to use zmq
var socket = zmq_open_request()
display "connect step", zmq_connect(socket, "tcp", "localhost", 5555)
for i = 0 to 10
zmq_send(socket, "Hello")
var result = zmq_recv_block(socket)
print "Client: Hello ->", result
zmq_close(socket)
zmq_term()
main()
exit()
ZeroMQ assumes that communication patterns are regular. The
Request/Reply pattern illustrated by the client/server code above
is one example. Other patterns supported by ZeroMQ are the
Push/Pull pattern, where a sender produces messages that are
consumed by a receiver, and the Publish/Subscribe pattern, where
messages are "broadcast" to any number of receivers that can
selectively receive messages by topic. Examples of these can be
found in serpent/programs/zmq*.srp. Some details on the
corresponding functions can be found in the Serpent documentation.
Most of these functions correspond closely to the ZeroMQ API for
the C programming language, and extensive ZeroMQ documentation is
available online.