--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This programs interprets the input of a physical interface thought the
+# GPIO pins of a Raspberry Pi to control an MPD server.
+#
+# It suppot two inputs: a play/pause button and a rotary encoder to adjust
+# the volume.
+#
+# The each data output of the volume control are connected to the board
+# pins #3 and #5.
+module physical_interface_for_mpd_on_rpi
+
+import bcm2835
+import mpd
+
+redef class Object
+ fun mpd: MPDConnection do return once new MPDConnection("localhost", 6600, "password")
+end
+
+class RotaryEncoder
+ var pin_a: RPiPin
+ var pin_b: RPiPin
+ var old_a= false
+ var old_b= false
+
+ fun update: nullable Char
+ do
+ var new_a = pin_a.lev
+ var new_b = pin_b.lev
+ var res = null
+
+ if new_a != old_a or new_b != old_b then
+ if not old_a and not old_b then
+ # everything was on
+ if not new_a and new_b then
+ res = '<'
+ else if new_a and not new_b then
+ res = '>'
+ end
+ else if old_a and old_b then
+ # everything was off
+ if not new_a and new_b then
+ res = '>'
+ else if new_a and not new_b then
+ res = '<'
+ end
+ end
+
+ old_a = new_a
+ old_b = new_b
+ end
+
+ return res
+ end
+end
+
+class LCD
+ var rs: RPiPin
+ var en: RPiPin
+ var d4: RPiPin
+ var d5: RPiPin
+ var d6: RPiPin
+ var d7: RPiPin
+
+ var ds = new Array[RPiPin]# = [d4,d5,d6,d7]
+
+ # commands
+ fun flag_clear_display: Int do return 1
+ fun flag_return_home: Int do return 2
+ fun flag_entry_mode_set: Int do return 4
+ fun flag_display_control: Int do return 8
+ fun flag_cursor_shift: Int do return 16
+ fun flag_function_set: Int do return 32
+ fun flag_set_cgram_addr: Int do return 64
+ fun flag_set_ggram_addr: Int do return 128
+
+ # entry mode
+ fun flag_entry_right: Int do return 0
+ fun flag_entry_left: Int do return 2
+ fun flag_entry_shift_increment: Int do return 1
+ fun flag_entry_shift_decrement: Int do return 0
+
+ # display flags
+ fun flag_display_on: Int do return 4
+ fun flag_display_off: Int do return 0
+ fun flag_cursor_on: Int do return 2
+ fun flag_cursor_off: Int do return 0
+ fun flag_blink_on: Int do return 1
+ fun flag_blink_off: Int do return 0
+
+ # display/cursor shift
+ fun flag_display_move: Int do return 8
+ fun flag_cursor_move: Int do return 0
+ fun flag_move_right: Int do return 4
+ fun flag_move_left: Int do return 0
+
+ # function set
+ fun flag_8bit_mode: Int do return 16
+ fun flag_4bit_mode: Int do return 0
+ fun flag_2_lines: Int do return 8
+ fun flag_1_line: Int do return 0
+ fun flag_5x10_dots: Int do return 4
+ fun flag_5x8_dots: Int do return 0
+
+ fun function_set(bits, lines, dots_wide: Int)
+ do
+ var fs = flag_function_set
+ if bits == 8 then
+ fs = fs.bin_or(16)
+ else if bits != 4 then abort
+
+ if lines == 2 then
+ fs = fs.bin_or(8)
+ else if lines != 1 then abort
+
+ if dots_wide == 10 then
+ fs = fs.bin_or(4)
+ else if dots_wide != 8 then abort
+
+ write(true, fs)
+ end
+
+ fun display_control(on, cursor, blink: Bool)
+ do
+ var fs = flag_display_control
+
+ if on then
+ fs = fs.bin_or(flag_display_on)
+ else fs = fs.bin_or(flag_display_off)
+
+ if cursor then
+ fs = fs.bin_or(flag_cursor_on)
+ else fs = fs.bin_or(flag_cursor_off)
+
+ if blink then
+ fs = fs.bin_or(flag_blink_on)
+ else fs = fs.bin_or(flag_blink_off)
+
+ write(true, fs)
+ end
+
+ fun entry_mode(left, incr: Bool)
+ do
+ var fs = flag_entry_mode_set
+
+ if left then
+ fs = fs.bin_or(flag_entry_left)
+ else fs = fs.bin_or(flag_entry_right)
+
+ if incr then
+ fs = fs.bin_or(flag_entry_shift_increment)
+ else fs = fs.bin_or(flag_entry_shift_decrement)
+
+ write(true, fs)
+ end
+
+ fun setup
+ do
+ ds = [d4,d5,d6,d7]
+
+ rs.fsel = new FunctionSelect.outp
+ en.fsel = new FunctionSelect.outp
+ d4.fsel = new FunctionSelect.outp
+ d5.fsel = new FunctionSelect.outp
+ d6.fsel = new FunctionSelect.outp
+ d7.fsel = new FunctionSelect.outp
+
+ rs.write(false)
+ en.write(false)
+
+ # wait 20ms for power up
+ #50.bcm2835_delay
+
+ #write_4bits(true,true,false,false)
+ #write_4_bits(3)
+
+ #5.bcm2835_delay
+
+ #write_4bits(true,true,false,false)
+ #write_4_bits(3)
+
+ #5.bcm2835_delay
+
+ #write_4bits(true,true,false,false)
+ #write_4_bits(3)
+
+ #200.bcm2835_delay_micros
+
+ #write_4bits(false,true,false,false)
+ #write_4_bits(2)
+
+ # wait 5ms
+ #5.bcm2835_delay
+
+ # set interface
+ # 4bits, 2 lines
+ #write(true, flow)
+ #function_set(4, 2, 8)
+
+ # cursor
+ # don't shift & hide
+ #display_control(true, true, true)
+
+ # clear & home
+ #write(true, flag_)
+ #clear
+
+ # set cursor move direction
+ # move right
+ #write(true, 6)
+
+ # turn on display
+ #write(true, 4)
+
+ # set entry mode
+ #entry_mode(true, true)
+
+ write(true, "33".to_hex)
+ write(true, "32".to_hex)
+ write(true, "28".to_hex)
+ write(true, "0C".to_hex)
+ write(true, "01".to_hex)
+ end
+
+ fun write_4_bits(v: Int)
+ do
+ var lb = once [1,2,4,8]
+ for i in [0..4[ do
+ var b = lb[i]
+ var r = b.bin_and(v) != 0
+ var d = ds[i]
+ d.write(r)
+ end
+ pulse_enable
+ end
+
+ fun write_4bits(a,b,c,d:Bool)
+ do
+ d4.write(a)
+ d5.write(b)
+ d6.write(c)
+ d7.write(d)
+ pulse_enable
+ end
+
+ fun pulse_enable
+ do
+ en.write(false)
+ 1.bcm2835_delay_micros
+ en.write(true)
+ 100.bcm2835_delay_micros
+ en.write(false)
+ end
+
+ fun write(is_cmd: Bool, cmd: Int)
+ do
+ en.write(false)
+ rs.write(not is_cmd)
+
+ # high byte
+ var hb = once [16,32,64,128]
+ for i in [0..4[ do
+ var b = hb[i]
+ var r = b.bin_and(cmd) != 0
+ var d = ds[i]
+ d.write(r)
+ end
+
+ en.write(true)
+
+ # wait 450 ns
+ 1.bcm2835_delay_micros
+
+ en.write(false)
+
+ if is_cmd then
+ # wait 5ms
+ 5.bcm2835_delay
+ else
+ # wait 200us
+ 200.bcm2835_delay_micros
+ end
+
+ # low byte
+ var lb = once [1,2,4,8]
+ for i in [0..4[ do
+ var b = lb[i]
+ var r = b.bin_and(cmd) != 0
+ var d = ds[i]
+ d.write(r)
+ end
+
+ en.write(true)
+
+ # wait 450ns
+ 1.bcm2835_delay_micros
+
+ en.write(false)
+
+ if is_cmd then
+ # wait 5ms
+ 5.bcm2835_delay
+ else
+ # wait 200us
+ 200.bcm2835_delay_micros
+ end
+ end
+
+ fun clear
+ do
+ write(true,1)
+ 2.bcm2835_delay
+ end
+
+ fun return_home
+ do
+ write(true,2)
+ 2.bcm2835_delay
+ end
+
+ fun text=(v: String)
+ do
+ clear
+ return_home
+ for c in v do write(false, c.ascii)
+ end
+end
+
+fun hit_play_stop
+do
+ # get current status
+ var status = mpd.status
+ var playing = false
+ if status != null then
+ playing = status.state == "play"
+ end
+
+ if playing then
+ # stop
+ print "playing -> stop"
+ mpd.pause
+ else
+ print "stopped -> play"
+ mpd.play
+ end
+end
+
+assert bcm2835_init else print "Failed to init"
+
+# Debug LED
+var out = new RPiPin.p1_11
+out.fsel = new FunctionSelect.outp
+out.write(false)
+
+# Play button
+var inp = new RPiPin.p1_13
+inp.fsel = new FunctionSelect.inpt
+inp.pud = new PUDControl.down
+
+# Vol +
+var vol3 = new RPiPin.p1_03
+vol3.fsel = new FunctionSelect.inpt
+vol3.pud = new PUDControl.up
+
+# Vol -
+var vol5 = new RPiPin.p1_05
+vol5.fsel = new FunctionSelect.inpt
+vol5.pud = new PUDControl.up
+
+var vol = new RotaryEncoder(vol3,vol5)
+var vol_step = 2
+
+# LCD
+var lcd_rs = new RPiPin.p1_23
+var lcd_en = new RPiPin.p1_21
+var lcd_d4 = new RPiPin.p1_19
+var lcd_d5 = new RPiPin.p1_26
+var lcd_d6 = new RPiPin.p1_24
+var lcd_d7 = new RPiPin.p1_22
+var lcd = new LCD(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7)
+lcd.setup
+#lcd.write(false, 'a'.to_i.to_ascii)
+lcd.clear
+lcd.write(false, 'a'.ascii)
+lcd.write(false, 'C'.ascii)
+
+var last_in = false
+var led_on = false
+var tick = 0
+loop
+ # play button
+ var lev = inp.lev
+ if lev != last_in then
+ last_in = lev
+ if lev then
+ print "hps"
+ hit_play_stop
+ end
+ end
+
+ # volume
+ var s = vol.update
+ if s != null then
+ if s == '<' then
+ print "vol down"
+ mpd.relative_volume = -vol_step
+ else # >
+ print "vol up"
+ mpd.relative_volume = vol_step
+ end
+ end
+
+ if tick % 100 == 0 then
+ print tick
+ #var now_playing = mpd.status("")
+ #lcd.text = tick.to_s
+ end
+
+ 10.bcm2835_delay
+ tick += 1
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module blink
+
+import bcm2835
+
+assert bcm2835_init else print "Failed to init"
+
+var out = new RPiPin.p1_11
+out.fsel = new FunctionSelect.outp
+
+for i in [0..1000[ do
+ out.write(true)
+ 500.bcm2835_delay
+ out.write(false)
+ 500.bcm2835_delay
+end
+
+bcm2835_close
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module input
+
+import bcm2835
+
+assert bcm2835_init else print "Failed to init"
+
+var out = new RPiPin.p1_11
+out.fsel = new FunctionSelect.outp
+out.write(false)
+
+var inp = new RPiPin.p1_22
+inp.fsel = new FunctionSelect.inpt
+inp.pud = new PUDControl.down
+
+var last_in = false
+loop
+ var lev = inp.lev
+ if lev != last_in then
+ last_in = lev
+ if lev then print "button pressed"
+ end
+
+ out.write(true)
+ 100.bcm2835_delay
+ out.write(false)
+ 100.bcm2835_delay
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services to control the bcm2835 chipset as used in the Raspberry Pi
+# model B revision 1 Uses the C library by Mike McCauley from
+# http://www.airspayce.com/mikem/bcm2835/
+module bcm2835
+
+import gpio
+
+in "C Header" `{
+ #include <bcm2835.h>
+`}
+
+redef class Object
+ protected fun bcm2835_init: Bool `{ return bcm2835_init(); `}
+ protected fun bcm2835_close `{ bcm2835_close(); `}
+ protected fun bcm2835_debug=(v: Bool) `{ bcm2835_set_debug(v); `}
+end
+
+extern class RPiPin `{ RPiGPIOPin `}
+ super Pin
+
+ new p1_03 `{ return RPI_GPIO_P1_03; `}
+
+ new p1_05 `{ return RPI_GPIO_P1_05; `}
+ new p1_07 `{ return RPI_GPIO_P1_07; `}
+
+ new p1_11 `{ return RPI_GPIO_P1_11; `}
+ new p1_12 `{ return RPI_GPIO_P1_12; `}
+ new p1_13 `{ return RPI_GPIO_P1_13; `}
+
+ new p1_15 `{ return RPI_GPIO_P1_15; `}
+ new p1_16 `{ return RPI_GPIO_P1_16; `}
+
+ new p1_18 `{ return RPI_GPIO_P1_18; `}
+ new p1_19 `{ return RPI_GPIO_P1_19; `}
+
+ new p1_21 `{ return RPI_GPIO_P1_21; `}
+ new p1_22 `{ return RPI_GPIO_P1_22; `}
+ new p1_23 `{ return RPI_GPIO_P1_23; `}
+ new p1_24 `{ return RPI_GPIO_P1_24; `}
+
+ new p1_26 `{ return RPI_GPIO_P1_26; `}
+
+ # Select mode: input, output or alts
+ fun fsel=(mode: FunctionSelect) `{ bcm2835_gpio_fsel(recv, mode); `}
+
+ # Set output
+ redef fun write(high) `{ bcm2835_gpio_write(recv, high? HIGH: LOW); `}
+
+ # Set pull up mode
+ fun pud=(pud: PUDControl) `{ bcm2835_gpio_set_pud(recv, pud); `}
+
+ # Falling edge detect
+ # Do not use on raspbian, it is bugged!
+ fun fen `{ bcm2835_gpio_fen(recv); `}
+ fun clr_fen `{ bcm2835_gpio_clr_fen(recv); `}
+
+ # Raising edge detect
+ # Do not use on raspbian, it is bugged!
+ fun ren `{ bcm2835_gpio_ren(recv); `}
+ fun clr_ren `{ bcm2835_gpio_clr_ren(recv); `}
+
+ # High edge detect
+ # Do not use on raspbian, it is bugged!
+ fun hen `{ bcm2835_gpio_hen(recv); `}
+ fun clr_hen `{ bcm2835_gpio_clr_hen(recv); `}
+
+ # Low edge detect
+ # Do not use on raspbian, it is bugged!
+ fun len `{ bcm2835_gpio_len(recv); `}
+ fun clr_len `{ bcm2835_gpio_clr_len(recv); `}
+
+ fun set_eds `{ bcm2835_gpio_set_eds(recv); `}
+ fun eds: Bool `{ return bcm2835_gpio_eds(recv); `}
+
+ # Return input on pin, true for high and false for low
+ fun lev: Bool `{ return bcm2835_gpio_lev(recv); `}
+end
+
+extern class FunctionSelect `{ bcm2835FunctionSelect `}
+ # Input function
+ new inpt `{ return BCM2835_GPIO_FSEL_INPT; `}
+
+ # Output function
+ new outp `{ return BCM2835_GPIO_FSEL_OUTP; `}
+
+ new alt0 `{ return BCM2835_GPIO_FSEL_ALT0; `}
+ new alt1 `{ return BCM2835_GPIO_FSEL_ALT1; `}
+ new alt2 `{ return BCM2835_GPIO_FSEL_ALT2; `}
+ new alt3 `{ return BCM2835_GPIO_FSEL_ALT3; `}
+ new alt4 `{ return BCM2835_GPIO_FSEL_ALT4; `}
+ new alt5 `{ return BCM2835_GPIO_FSEL_ALT5; `}
+ new mask `{ return BCM2835_GPIO_FSEL_MASK; `}
+end
+
+extern class PUDControl `{ bcm2835PUDControl `}
+ new off `{ return BCM2835_GPIO_PUD_OFF; `}
+ new down `{ return BCM2835_GPIO_PUD_DOWN; `}
+ new up `{ return BCM2835_GPIO_PUD_UP; `}
+end
+
+redef universal Int
+ fun bcm2835_delay `{ bcm2835_delay(recv); `}
+ fun bcm2835_delay_micros `{ bcm2835_delayMicroseconds(recv); `}
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# GPIO related functionnalities
+module gpio
+
+interface Pin
+ fun write(high: Bool) is abstract
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Client for a MPD server
+module mpd
+
+import socket
+
+# Connection to a MPD server
+class MPDConnection
+ var socket: nullable Socket = null
+
+ var host: String
+ var port: Int
+ var password: nullable String
+
+ var error: nullable String = null
+
+ # Connect to the MPD server
+ fun connect
+ do
+ var p: nullable Socket = null
+
+ p = new Socket.stream_with_host(host, port)
+ p.connect
+
+ sys.nanosleep(0,5000)
+
+ var rep = p.read
+ assert not rep.is_empty
+ if not rep.has_prefix("OK") then
+ print "MPD responded {rep}"
+ abort
+ end
+
+ socket = p
+
+ var password = password
+ if password != null then
+ write("password {password}\n")
+ end
+ end
+
+ # Write a command to the MPD server
+ protected fun write(msg: String)
+ do
+ if socket == null then connect
+
+ socket.write(msg)
+ sys.nanosleep(0,5000)
+ var rep = socket.read
+ assert rep.has_prefix("OK") else print "MPD responded {rep}"
+ end
+
+ # Get MPD server status
+ fun status: nullable ServerStatus
+ do
+ if socket == null then connect
+
+ var volume: nullable Int = null
+ var state: nullable String = null
+
+ # get current status
+ socket.write("status\n")
+ var rep = socket.read
+ for l in rep.split_with("\n") do
+ var words = l.split_with(" ")
+ if words.length > 1 then
+ var key = words[0].to_lower
+ var first_space = l.index_of(' ')
+ var rest = l.substring_from(first_space+1)
+ if key == "volume:" then
+ volume = rest.to_i
+ if volume == -1 then volume = null
+ else if key == "volume:" then
+ state = rest
+ end
+ end
+ end
+
+ if state != null then
+ return new ServerStatus(volume, state)
+ else
+ return null
+ end
+ end
+
+ # Set the volume relatively
+ fun relative_volume=(diff: Int)
+ do
+ if socket == null then connect
+
+ var status = status
+ if status != null then
+ var vol = status.volume
+ if vol != null then
+ var new_vol = vol + diff
+ new_vol = new_vol.max(0).min(100)
+ volume = new_vol
+ return
+ end
+ end
+
+ error = "Cannot get volume"
+ end
+
+ fun volume=(vol: Int) do write("setvol {vol}\n")
+
+ # Pause music playing on the MPD server
+ fun pause do write("pause\n")
+
+ # Stop music playing on the MPD server
+ fun stop do write("stop\n")
+
+ # Play music playing on the MPD server
+ fun play do write("play\n")
+
+ # Get information on the currently playing song on the MPD server
+ fun current_song: nullable SongInfo
+ do
+ if socket == null then connect
+
+ var album: nullable String = null
+ var artist: nullable String = null
+ var title: nullable String = null
+
+ socket.write("currentsong\n")
+ var rep = socket.read
+ for l in rep.split_with("\n") do
+ var words = l.split_with(" ")
+ if words.length > 1 then
+ var key = words[0].to_lower
+ var first_space = l.index_of(' ')
+ var rest = l.substring_from(first_space+1)
+ if key == "album:" then
+ album = rest
+ else if key == "artist:" then
+ artist = rest
+ else if key == "title:" then
+ title = rest
+ end
+ end
+ end
+
+ if album != null and artist != null and title != null then
+ return new SongInfo(album, artist, title)
+ else
+ return null
+ end
+ end
+end
+
+# MPD song info
+class SongInfo
+ var album: String
+ var artist: String
+ var title: String
+end
+
+# MPD server status
+class ServerStatus
+ var volume: nullable Int
+ var state: String
+end
intrude import array # FIXME because of string.nit
import sorter
import hash_collection
+
+redef class Sequence[E]
+ fun subarray(start, len: Int): Array[E]
+ do
+ var a = new Array[E].with_capacity(len)
+ for i in [start .. start+len[ do a.add(self[i])
+ return a
+ end
+end
--- /dev/null
+123
+012345678
+456
+01234
+123L123truefalse
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_subarray
+
+print( [0,1,2,3,4,5,6,7,8,9,10].subarray(1, 3) )
+print( [0,1,2,3,4,5,6,7,8,9,10].subarray(0, 9) )
+print( [0,1,2,3,4,5,6,7,8,9,10].subarray(4, 3) )
+print( [0,1,2,3,4].subarray(0, 5) )
+print( new Array[Object].with_items("a", 123, 'L', [1,2,3], true, false).subarray(1,5) )