OSH Builtins: "Hello, world!" Example - oilshell/oil GitHub Wiki

About

This document describes the steps required to create a fictional OSH builtin called hw that prints "Hello, world!" when invoked.

Code changes are shown using the git-diff(1) command, so that the reader has all the information to understand:

  1. Which files were modified (filepaths)

  2. How files were modified (content changes)

  3. Where files were modified (line numbers)

Create Spec Test

spec/builtin-hw.test.sh
-----------------------
#!/bin/bash

#### `hw` prints "Hello, world!"
hw
## status: 0
## STDOUT:
Hello, world!
## END
  • The "####" directive provides a test title

  • From the end of this line until the start of the next "##" is a command block containing commands to be executed in top-down order. In this case, there is a single command to execute: hw

  • The first occurrence of the "##" directive asserts that the exit status code of the command block, after being executed, must be 0

  • The second occurrence of the "##" directive asserts that the standard output of the command block, after being executed, must be "Hello, world!"

  • The last occurrence of the "##" directive marks the end of the individual spec test

Update Test Harness

Now, we'll update the test harness with a new directive called "builtin-hw" that will run the hw spec test

$> git diff test/spec.sh
diff --git a/test/spec.sh b/test/spec.sh
index 9ea7d710..2e4559f6 100755
--- a/test/spec.sh
+++ b/test/spec.sh
@@ -432,6 +432,10 @@ builtin-times() {
   sh-spec spec/builtin-times.test.sh $BASH $ZSH $OSH_LIST "$@"
 }
 
+builtin-hw() {
+  sh-spec spec/builtin-hw.test.sh $OSH_LIST "$@"
+}
+
 command-parsing() {
   sh-spec spec/command-parsing.test.sh ${REF_SHELLS[@]} $OSH_LIST "$@"
 }

Confirm Failure

$> test/spec.sh builtin-hw
builtin-hw.test.sh
case    line    osh
  0       3     FAIL    `hw` prints "Hello, world!"


FATAL: 1 tests failed (1 osh failures)

Update OSH ASDL

The OSH ASDL uses a list of builtins to produce compiler artifacts. We'll need to add a hw enumerated type to this list

$> git diff osh/runtime.asdl
diff --git a/osh/runtime.asdl b/osh/runtime.asdl
index 238e4eab..1014124b 100644
--- a/osh/runtime.asdl
+++ b/osh/runtime.asdl
@@ -3,7 +3,7 @@
 module runtime
 {
   builtin = 
-    NONE | TIMES | READ | ECHO | PRINTF | SHIFT
+    NONE | HW | TIMES | READ | ECHO | PRINTF | SHIFT
   | CD | PWD | PUSHD | POPD | DIRS
   | EXPORT | READONLY | LOCAL | DECLARE | TYPESET 
   | UNSET | SET | SHOPT

Register hw Implementation

$> git diff bin/oil.py
diff --git a/bin/oil.py b/bin/oil.py
index 443470c9..b61195f4 100755
--- a/bin/oil.py
+++ b/bin/oil.py
@@ -446,6 +446,7 @@ def ShellMain(lang, argv0, argv, login_shell):
       builtin_e.DIRS: builtin.Dirs(mem, dir_stack, errfmt),
       builtin_e.PWD: builtin.Pwd(mem, errfmt),
 
+      builtin_e.HW: builtin.Hw(),
       builtin_e.TIMES: builtin.Times(),
       builtin_e.READ: builtin.Read(splitter, mem),
       builtin_e.HELP: builtin.Help(loader, errfmt),

Implement hw

$> git diff osh/builtin.py
diff --git a/osh/builtin.py b/osh/builtin.py
index f22c09c0..d68c14ca 100755
--- a/osh/builtin.py
+++ b/osh/builtin.py
@@ -89,6 +89,7 @@ _SPECIAL_ASSIGN_BUILTINS = {
 }
 
 _NORMAL_BUILTINS = {
+    "hw": builtin_e.HW,
     "read": builtin_e.READ,
     "echo": builtin_e.ECHO,
     "printf": builtin_e.PRINTF,
@@ -739,3 +740,12 @@ class History(object):
       item = readline_mod.get_history_item(i)
       print('%5d  %s' % (i, item))
     return 0
+
+HW_SPEC = _Register('hw')
+class Hw(object):
+  def __init__(self):
+    pass
+
+  def __call__(self, arg_vec):
+    print("Hello, world!")
+    return 0

Confirm Pass

Rebuild osh

$> build/dev.sh all
[...]

$> test/spec.sh builtin-hw
builtin-hw.test.sh
case    line    osh
  0       3     pass    `hw` prints "Hello, world!"