SHS/Readme.md
2021-05-31 15:26:24 -07:00

8.5 KiB

SHS

Syntactically Homogeneous Shell

Overview

This shell was created to have extremely simple syntax. S-Expressions were chosen to represent statements and the scope of the language features were constrained to what could be considered practical for daily shell use. This program is meant to be practical for administrators and daily power users. It can be used for both system administration (as one might use bash or zsh) as well as for the creation of simple programs as one might use Python, Racket, or Visual Basic.

Basic Syntax

When in doubt the print_ast utility can be used to examine the output of the Lexing and Parsing process. Here you can spot any bugs regarding syntax.

$ print_ast (example invocation)

Lists

Any sequence of items within a set of parenthesis is a list.

For Example: (1 "two" three 4)

Lists can be infinitely nested: ("one" (2 3 4 (5)))

Data types

Elements in a list can be one of the following data types:

  • Number: A number can be written in one of the following formats:
    • integers: 1, 2, 3, etc...
    • rational numbers: 1.22, 2.24, etc...
  • String: Strings must use one of the following delimiters:
    • "this string is a proper string"
    • 'this is also a proper string'
    • ` this is another proper string`
  • Boolean: Boolean values can take the following forms:
    • true: "T"
    • false: "F"
  • Symbol:
    • A symbol looks like a string without delimiters.
    • A symbol denotes a variable or a function.
  • List: Any combination of multiple items seperated by a space. (See above.)

Function calls

Any list beginning in a symbol will be considered a function call. A user may typically expect to be able to start a list with a variable. However, given the current architecture this pattern must be maintained in order to enable shell functionality. Thus, from within the shs utility unknown symbols at the start of a list will be assumed to be system binaries.

Function call: (append () 1 2 3)

System binary call: (vim Readme.md)

; complex call 
(if (eq "example" (fread 'test_file'))
    (print "test worked")
    (rm -rf /))

There may be alternative REPLs, officially or from community members, that do not exhibit this behavior.

Variable declaration

Export is used to define variables.

(export NAME (value))

Example export:

(export vim 'nvim')

Users may also leverage the export function to create aliases in the SHS shell.

(export vim nvim)
(vim /path/to/file.c)

The above example will call exec with the arguments "nvim" and "/path/to/file.c".

Function declaration

Use the func function from the stdlib:

Example call: (func name (var1, var2, var3) (form_to_be_evaluated))

In the above example, a function is defined with the following attributes:

  • The function is named "name".
  • The function takes three arguments: arg1, arg2, and arg3.
  • When the function is called, "(form_to_be_evaluated)" is evaluated.

SHS will only lex (form_to_be_evaluated). It will not parse or execute it until the function is invoked.

In the below example a function named addthree is defined. The function takes three arguments, and returns the sum of them.

(func addthree (a b c) (+ a b c))
(addthree 2 3 4)
Output: 9

Control flow

SHS currently uses the following control flow forms for daily use. Although, it is not hard to create your own in the SHS source code.

if statements

The if form takes 3 arguments. It evaluates the first argument, the condition, and if it evaluates to true (T) it evaluates the second argument. If the first argument evaluates to false (F) the if routine then evaluates the third argument. The argument that is not used will not be parsed or evaluated. The whole statement will, however, be lexed.

(if (cond) (then) (else))
(if (eq 1 1.0) 
    (print "numbers are equal") 
    (print "numbers are not equal"))

while loops

The while loop takes N arguments. The first argument, the conditional, must evaluate to a boolean value (T or F). If the first argument is evaluated to T, all subsequent arguments are evaluated. If the first argument evaluates to F, while returns. Finally, the conditional is re-evaluated and the loop begins again.

(while (cond) (form1)....... (formN))

(export cond T)
(while cond 
    (print "this will only be printed once")
    (export cond F))

Output:
this will only be printed once

Comments

The standard delimiter for comments is ; Any characters after a semicolon will be ignored until end of line. Semicolons found in strings will not be considered comment delimiters.

; this comment explains the value in myvar
(export myvar "testvar")

How to build

Compiling/Installation

  1. follow Google's instructions to install Go
    • after these instructions you should have $GOPATH set
  2. run $ go get -u gitlab.com/whom/shs
  3. change directory to $GOPATH/src/gitlab.com/whom/shs
  4. run $ go install ./cmd/shs.go
    • if you have the Go binary directory in your path you can now call shs via the shell

Adding SHS to your application

Here are some important tips for integrating an SHS REPL into another codebase.

  • Make sure to set ast.SyncTablesWithOSEnviron and ast.ExecWhenFuncUndef, both of which control integrations with the underlying system.
    • If you do not want the user to be able to set environment variables set ast.SyncTablesWithOSEnviron to false.
    • If you do not want the user to be able to call binaries from the host system, set ast.ExecWhenFuncUndef to false.
  • Create a new VarTable and FuncTable (see ast/var_table.go and ast/func_table.go).
  • Make sure to adhere to the terms and conditions stated in the GPLv3.
  • OVERRIDE THE STDLIB GenFuncTable FUNCTION. You very likely do NOT want an available function to call system binaries in your embedded shell. Make sure the stdlib Call function is not included in your GenFuncTable inplementation.

Configuration

  • Variables exported in the REPL, if of type string or number, will result in a corresponding environment variable.
  • One can write arbitrary SHS script into .shsrc including function and variable declarations
  • Particularly useful are the following variables:
    • SH_LOGGING Sets the log level (from 0 to 3)
    • SHS_STATIC_PROMPT Sets the prompt
    • SH_HIST_FILE Sets the history file
    • SH_DEBUG_MODE Adds additional debug output for the lexer (high clutter)
  • Additionally, the REPL will evaluate any function you define as _SH_PROMPT before the shell prompt.
    • if defined, the function will be evaluated before printing the prompt
    • the function will be given 0 arguments
    • if the function does not return a string, its output will be discarded
    • afterwards, the repl will print the values in SHS_STATIC_PROMPT

Here is an example of a SHS configuration file:

(export GOPATH (concat HOME     "/go"))
(export GOBIN  (concat GOPATH  "/bin"))
(export PATH   (concat PATH ":" GOBIN))
(export GIT_TERMINAL_PROMPT 1)
(export SH_HIST_FILE (concat HOME "/.shs_hist"))
(export SH_LOGGING 0)
(export SHS_STATIC_PROMPT ">")
(func _SH_PROMPT () (concat (?) ($ basename ($ pwd)) "\n"))

The Docs

Godoc

What follows are links to documentation for the code and interfaces used by SHS. This documentation is automatically generated by godoc, and in times of transition may not be complete.

Contributing

  • Any contribution to this software is welcome as long as it adheres to the conduct guidelines specified in the Contributing.md file in this repository.

License

Copyright (C) 2019 Aidan Hahn.

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.