Skip to content

Commit

Permalink
Allow more customisation of artificial environments
Browse files Browse the repository at this point in the history
It is now allowed to supply a custom input stream and a custom exit
code handler to the user-exposed constructor of Env, allowing full
customisation of the artificial environment.
  • Loading branch information
Benoit Vey authored and SeanTAllen committed Apr 3, 2018
1 parent d022456 commit b1bf144
Show file tree
Hide file tree
Showing 12 changed files with 60 additions and 54 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ All notable changes to the Pony compiler and standard library will be documented
- Compile error when comparing sugared constructors with 'is' or 'isnt' (#2024) ([PR #2494](https://github.com/ponylang/ponyc/pull/2494))
- Support OpenSSL 1.1.0 ([PR #2415](https://github.com/ponylang/ponyc/pull/2415))
- Add U64 type to `cli` package. ([PR #2488](https://github.com/ponylang/ponyc/pull/2488))
- Allow customisation of `Env.input` and `Env.exitcode` in artificial environments

### Changed

Expand All @@ -46,6 +47,7 @@ All notable changes to the Pony compiler and standard library will be documented
- Rename Date to PosixDate ([PR #2436](https://github.com/ponylang/ponyc/pull/2436))
- Fix and re-enable dynamic scheduler scaling ([PR #2483](https://github.com/ponylang/ponyc/pull/2483))
- Expose OutStream rather than StdStream in Env ([PR #2463](https://github.com/ponylang/ponyc/pull/2463))
- Rename StdinNotify to InputNotify

## [0.21.3] - 2018-01-14

Expand Down
2 changes: 1 addition & 1 deletion examples/commandline/main.pony
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ actor Main
end

let cmd =
match CommandParser(cs).parse(env.args, env.vars())
match CommandParser(cs).parse(env.args, env.vars)
| let c: Command => c
| let ch: CommandHelp =>
ch.print_help(env.out)
Expand Down
2 changes: 1 addition & 1 deletion examples/gups_basic/main.pony
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class val Config
OptionSpec.i64("updaters", "Number of updaters." where default' = 8)
])?.>add_help()?
let cmd =
match CommandParser(cs).parse(env.args, env.vars())
match CommandParser(cs).parse(env.args, env.vars)
| let c: Command => c
| let ch: CommandHelp =>
ch.print_help(env.out)
Expand Down
2 changes: 1 addition & 1 deletion examples/gups_opt/main.pony
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class val Config
OptionSpec.i64("actors", "Log2 of the actor count." where default' = 2)
])?.>add_help()?
let cmd =
match CommandParser(cs).parse(env.args, env.vars())
match CommandParser(cs).parse(env.args, env.vars)
| let c: Command => c
| let ch: CommandHelp =>
ch.print_help(env.out)
Expand Down
2 changes: 1 addition & 1 deletion examples/httpget/httpget.pony
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class val Config
ArgSpec.string("url", "Url to query." where default' = None)
])?.>add_help()?
let cmd =
match CommandParser(cs).parse(env.args, env.vars())
match CommandParser(cs).parse(env.args, env.vars)
| let c: Command => c
| let ch: CommandHelp =>
ch.print_help(env.out)
Expand Down
2 changes: 1 addition & 1 deletion examples/mandelbrot/mandelbrot.pony
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class val Config
where short' = 'o', default' = "")
])?.>add_help()?
let cmd =
match CommandParser(cs).parse(env.args, env.vars())
match CommandParser(cs).parse(env.args, env.vars)
| let c: Command => c
| let ch: CommandHelp =>
ch.print_help(env.out)
Expand Down
2 changes: 1 addition & 1 deletion examples/message-ubench/main.pony
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ actor Main
ArgSpec.string_seq("", "")
])?.>add_help()?
let cmd =
match CommandParser(cs).parse(env.args, env.vars())
match CommandParser(cs).parse(env.args, env.vars)
| let c: Command => c
| let ch: CommandHelp =>
ch.print_help(env.out)
Expand Down
2 changes: 1 addition & 1 deletion examples/printargs/printargs.pony
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ actor Main

env.out.print(consume str)

let envvars = EnvVars(env.vars())
let envvars = EnvVars(env.vars)

for (k, v) in envvars.pairs() do
env.out.print(k + " = " + v)
Expand Down
2 changes: 1 addition & 1 deletion examples/yield/main.pony
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ actor Main
end

let cmd =
match CommandParser(cs).parse(_env.args, _env.vars())
match CommandParser(cs).parse(_env.args, _env.vars)
| let c: Command => c
| let ch: CommandHelp =>
ch.print_help(_env.out)
Expand Down
2 changes: 1 addition & 1 deletion packages/buffered/reader.pony
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Reader
use "buffered"
use "collections"
class Notify is StdinNotify
class Notify is InputNotify
let _env: Env
new create(env: Env) =>
_env = env
Expand Down
56 changes: 22 additions & 34 deletions packages/builtin/env.pony
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class val Env
Can be `None` for artificially constructed `Env` instances.
"""

let input: Stdin
let input: InputStream
"""
Stdin represented as an actor.
"""
Expand All @@ -24,8 +24,14 @@ class val Env
let args: Array[String] val
"""The command line used to start the program."""

let _envp: Pointer[Pointer[U8]] val
let _vars: (Array[String] val | None)
let vars: Array[String] val
"""The program's environment variables."""

let exitcode: {(I32)} val
"""
Sets the environment's exit code. The exit code of the root environment will
be the exit code of the application, which defaults to 0.
"""

new _create(
argc: U32,
Expand All @@ -44,14 +50,16 @@ class val Env
err = StdStream._err()

args = _strings_from_pointers(argv, argc.usize())
_envp = envp
_vars = None
vars = _strings_from_pointers(envp, _count_strings(envp))

exitcode = {(code: I32) => @pony_exitcode[None](code) }

new create(
new val create(
root': (AmbientAuth | None),
input': Stdin, out': OutStream,
input': InputStream, out': OutStream,
err': OutStream, args': Array[String] val,
vars': (Array[String] val | None))
vars': Array[String] val,
exitcode': {(I32)} val)
=>
"""
Build an artificial environment. A root capability may be supplied.
Expand All @@ -61,32 +69,14 @@ class val Env
out = out'
err = err'
args = args'
_envp = recover Pointer[Pointer[U8]] end
_vars = vars'
vars = vars'
exitcode = exitcode'

fun vars(): Array[String] val =>
"""
Return the environment variables as an array of strings of the form
"key=value".
"""
match _vars
| let v: Array[String] val => v
else
if not _envp.is_null() then
_strings_from_pointers(_envp, _count_strings(_envp))
else
recover Array[String] end
end
fun tag _count_strings(data: Pointer[Pointer[U8]] val): USize =>
if data.is_null() then
return 0
end

fun tag exitcode(code: I32) =>
"""
Sets the application exit code. If this is called more than once, the last
value set will be the exit code. The exit code defaults to 0.
"""
@pony_exitcode[None](code)

fun tag _count_strings(data: Pointer[Pointer[U8]] val): USize =>
var i: USize = 0

while
Expand All @@ -105,10 +95,8 @@ class val Env
let array = recover Array[String](len) end
var i: USize = 0

while
while i < len do
let entry = data._apply(i = i + 1)
not entry.is_null()
do
array.push(recover String.copy_cstring(entry) end)
end

Expand Down
38 changes: 27 additions & 11 deletions packages/builtin/stdin.pony
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ use @pony_asio_event_create[AsioEventID](
use @pony_asio_event_unsubscribe[None](event: AsioEventID)
use @pony_asio_event_destroy[None](event: AsioEventID)

interface StdinNotify
interface InputNotify
"""
Notification for data arriving via stdin.
Notification for data arriving via an input stream.
"""
fun ref apply(data: Array[U8] iso) =>
"""
Called when data is available on stdin.
Called when data is available on the stream.
"""
None

fun ref dispose() =>
"""
Called when no more data will arrive on stdin.
Called when no more data will arrive on the stream.
"""
None

Expand All @@ -30,19 +30,35 @@ interface tag DisposableActor
"""
be dispose()

interface tag InputStream
"""
Asynchronous access to some input stream.
"""
be apply(notify: (InputNotify iso | None), chunk_size: USize = 32)
"""
Set the notifier. Optionally, also sets the chunk size, dictating the
maximum number of bytes of each chunk that will be passed to the notifier.
"""

be dispose() =>
"""
Clear the notifier in order to shut down input.
"""
None

actor Stdin
"""
Asynchronous access to stdin. The constructor is private to ensure that
access is provided only via an environment.
Reading from stdin is done by registering a `StdinNotify`:
Reading from stdin is done by registering an `InputNotify`:
```pony
actor Main
new create(env: Env) =>
// do not forget to call `env.input.dispose` at some point
env.input(
object iso is StdinNotify
object iso is InputNotify
fun ref apply(data: Array[U8] iso) =>
env.out.write(String.from_iso_array(consume data))
Expand All @@ -54,7 +70,7 @@ actor Stdin
**Note:** For reading user input from a terminal, use the [readline](readline--index) package.
"""
var _notify: (StdinNotify | None) = None
var _notify: (InputNotify | None) = None
var _chunk_size: USize = 32
var _event: AsioEventID = AsioEvent.none()
let _use_event: Bool
Expand All @@ -65,7 +81,7 @@ actor Stdin
"""
_use_event = use_event

be apply(notify: (StdinNotify iso | None), chunk_size: USize = 32) =>
be apply(notify: (InputNotify iso | None), chunk_size: USize = 32) =>
"""
Set the notifier. Optionally, also sets the chunk size, dictating the
maximum number of bytes of each chunk that will be passed to the notifier.
Expand All @@ -79,7 +95,7 @@ actor Stdin
"""
_set_notify(None)

fun ref _set_notify(notify: (StdinNotify iso | None)) =>
fun ref _set_notify(notify: (InputNotify iso | None)) =>
"""
Set the notifier.
"""
Expand All @@ -99,7 +115,7 @@ actor Stdin
end
end

try (_notify as StdinNotify).dispose() end
try (_notify as InputNotify).dispose() end
_notify = consume notify

be _loop_read() =>
Expand Down Expand Up @@ -132,7 +148,7 @@ actor Stdin
ourself a resume message and stop reading to avoid starving other actors.
"""
try
let notify = _notify as StdinNotify
let notify = _notify as InputNotify
var sum: USize = 0

while true do
Expand Down

0 comments on commit b1bf144

Please sign in to comment.