First commit
This commit is contained in:
parent
edeb5222dc
commit
913ff916e1
@ -0,0 +1,6 @@
|
||||
= dev-expr
|
||||
Simple program for testing purpose on the go-pkg/expr module,
|
||||
|
||||
== First build
|
||||
Currently, _dev-expr_ is all contained in the main.go source file, but the version infos that are imported from the version.go file, not included in this repository
|
||||
Use the build.bash script to create the version.go file and build the program, at least for the first time.
|
173
build.bash
Executable file
173
build.bash
Executable file
@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
# All rights reserved.
|
||||
|
||||
|
||||
RESOURCE_FILE=".build.rc"
|
||||
BUILD_REGISTER=".build_register"
|
||||
|
||||
PROGRAM_NAME=""
|
||||
PROGRAM_VERSION=""
|
||||
|
||||
function usage() {
|
||||
prog=$(basename "${0}")
|
||||
msgln "USAGE:"
|
||||
#msgln " ${prog} <program-name> <program-version> <os>/<platform>..."
|
||||
msgln " ${prog} <os>/<platform>..."
|
||||
msgln " ${prog} --dev Build the local exec"
|
||||
msgln " ${prog} --init Create the resource file in the current directory"
|
||||
msgln
|
||||
if [ -r "${RESOURCE_FILE}" ]; then
|
||||
msgln "Resource file '${RESOURCE_FILE}' content:"
|
||||
cat >&2 ${RESOURCE_FILE}
|
||||
else
|
||||
msgln "Resource file '${RESOURCE_FILE}' not found"
|
||||
fi
|
||||
}
|
||||
|
||||
function msgln() {
|
||||
echo >&2 "${1}"
|
||||
}
|
||||
|
||||
function exitUsage() {
|
||||
echo >&2 "${1}"
|
||||
usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
function exitMsg() {
|
||||
echo >&2 "${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# CMDLINE: help
|
||||
if [ "${1}" == "help,," ] || [ "${1,,}" == "--help" ] || [ "${1,,}" == "-h" ]; then
|
||||
usage
|
||||
exit
|
||||
fi
|
||||
|
||||
# CMDLINE: init
|
||||
if [ "${1,,}" == "--init" ]; then
|
||||
cat >"${RESOURCE_FILE}" <<eot
|
||||
# Builder resource file
|
||||
# Created on $(date)
|
||||
|
||||
# Program name
|
||||
PROGRAM_NAME="name"
|
||||
|
||||
# Program version
|
||||
PROGRAM_VERSION="version"
|
||||
|
||||
# Preset platform
|
||||
platform=("linux/amd64" "darwin/arm64")
|
||||
|
||||
#--- end of file ---
|
||||
|
||||
eot
|
||||
msgln "Resource file '${RESOURCE_FILE}' create"
|
||||
${EDITOR-vi} "${RESOURCE_FILE}"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -r "${RESOURCE_FILE}" ]; then
|
||||
if ! source "${RESOURCE_FILE}"; then
|
||||
exitMsg "Can't load build resource file '${RESOURCE_FILE}'"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${PROGRAM_NAME}" ]; then
|
||||
exitUsage "Missing program name"
|
||||
fi
|
||||
|
||||
if [ -z "${PROGRAM_VERSION}" ]; then
|
||||
exitUsage "Missing program version"
|
||||
fi
|
||||
|
||||
|
||||
function getBuildCount() {
|
||||
local reg ver count
|
||||
if [ -r "${BUILD_REGISTER}" ]; then
|
||||
reg=$(<"${BUILD_REGISTER}")
|
||||
else
|
||||
reg="${PROGRAM_VERSION} 0"
|
||||
fi
|
||||
read ver count <<<"${reg}"
|
||||
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
|
||||
count=0
|
||||
fi
|
||||
count=$((count+1))
|
||||
echo >"${BUILD_REGISTER}" "${PROGRAM_VERSION} ${count}"
|
||||
echo ${count}
|
||||
}
|
||||
|
||||
function build() {
|
||||
local target=${1} ext cmd
|
||||
IFS=/ read os cpu <<<"${p}"
|
||||
#msgln "OS=${os}; CPU=${cpu}"
|
||||
ext=""
|
||||
if [ "${os}" == 'windows' ]; then
|
||||
ext=".exe"
|
||||
fi
|
||||
cmd="GOOS='${os}' GOARCH='${cpu}' go build -o '${PROGRAM_NAME}_v${PROGRAM_VERSION}_${os}_${cpu}${ext}'"
|
||||
eval "${cmd}"
|
||||
}
|
||||
|
||||
function buildLocal() {
|
||||
local ext cmd
|
||||
ext=""
|
||||
if [[ "${OSTYPE}" =~ win.* ]]; then
|
||||
ext=".exe"
|
||||
fi
|
||||
cmd="go build -o '${PROGRAM_NAME}${ext}'"
|
||||
eval "${cmd}"
|
||||
}
|
||||
|
||||
function exprPath() {
|
||||
local path
|
||||
path=$(grep -m1 -F "go-pkg/expr" go.work)
|
||||
echo ${path}
|
||||
}
|
||||
|
||||
function gitTag() {
|
||||
local expr_path
|
||||
expr_path=$(exprPath)
|
||||
tag=$(git -C "${expr_path}" tag -l --sort=-version:refname "v*"|head -1)
|
||||
echo ${tag}
|
||||
}
|
||||
|
||||
function createVersionSource() {
|
||||
local tag
|
||||
tag=$(gitTag)
|
||||
cat >version.go <<eot
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// version.go
|
||||
package main
|
||||
|
||||
const (
|
||||
PROGNAME = "${PROGRAM_NAME}"
|
||||
VERSION = "v${PROGRAM_VERSION}(build $(getBuildCount)),$(date +"%Y/%m/%d") (celestino.amoroso@portale-stac.it)"
|
||||
EXPR_VERSION = "${tag}"
|
||||
)
|
||||
eot
|
||||
}
|
||||
|
||||
## -- MAIN -- ##
|
||||
createVersionSource
|
||||
|
||||
if [ "${1}" == "--dev" ]; then
|
||||
buildLocal
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ${#} -gt 0 ]; then
|
||||
for p; do
|
||||
build "${p}"
|
||||
done
|
||||
else
|
||||
for p in ${platform[@]}; do
|
||||
build "${p}"
|
||||
done
|
||||
fi
|
||||
|
84
clean-older.bash
Executable file
84
clean-older.bash
Executable file
@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
# All rights reserved
|
||||
|
||||
RESOURCE_FILE=".build.rc"
|
||||
BUILD_REGISTER=".build_register"
|
||||
|
||||
|
||||
PASSWORD=
|
||||
GITEA_USER="camoroso"
|
||||
GITEA_PASSWORD_FILE="${HOME}/.gitea_password"
|
||||
GITEA_OWNER="go-pkg"
|
||||
GITEA_HOST="https://git.portale-stac.it"
|
||||
GITEA_BASE_PATH="api/v1/packages"
|
||||
GITEA_PKG_TYPE="generic"
|
||||
|
||||
|
||||
function msgln() {
|
||||
echo >&2 "${1}"
|
||||
}
|
||||
|
||||
function exitMsg() {
|
||||
echo >&2 "${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function readBuildCount() {
|
||||
local reg ver count
|
||||
if [ -r "${BUILD_REGISTER}" ]; then
|
||||
reg=$(<"${BUILD_REGISTER}")
|
||||
else
|
||||
reg="${PROGRAM_VERSION} 0"
|
||||
fi
|
||||
read ver count <<<"${reg}"
|
||||
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
|
||||
count=0
|
||||
fi
|
||||
echo ${count}
|
||||
}
|
||||
|
||||
if [ -r "${GITEA_PASSWORD_FILE}" ]; then
|
||||
if ! PASSWORD=$(<"${GITEA_PASSWORD_FILE}"); then
|
||||
exitMsg "Can're password file '${GITEA_PASSWORD_FILE}'"
|
||||
fi
|
||||
else
|
||||
exitMsg "Password file '${GITEA_PASSWORD_FILE}' not found"
|
||||
fi
|
||||
if [ -z "${PASSWORD}" ]; then
|
||||
exitMsg "Empty password. Please, check file '${GITEA_PASSWORD_FILE}'"
|
||||
fi
|
||||
|
||||
if [ -r "${RESOURCE_FILE}" ]; then
|
||||
source "${RESOURCE_FILE}"
|
||||
else
|
||||
exitMsg "resource file '${RESOURCE_FILE}' not found"
|
||||
fi
|
||||
|
||||
if [ -r "${BUILD_REGISTER}" ]; then
|
||||
BUILD_TAG=$(<"${BUILD_REGISTER}")
|
||||
else
|
||||
exitMsg "build register file '${BUILD_REGISTER}' not found"
|
||||
fi
|
||||
url="${GITEA_HOST}/${GITEA_BASE_PATH}/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/files"
|
||||
#echo "URL: ${url}"
|
||||
#echo $(curl --user "${GITEA_USER}:${PASSWORD}" -X GET ${url}|jq '.[]."name"')
|
||||
|
||||
declare -a files=(
|
||||
$(curl --no-progress-meter --user "${GITEA_USER}:${PASSWORD}" -X GET ${url}|jq '.[]."name"')
|
||||
)
|
||||
|
||||
for name in ${files[@]}; do
|
||||
filename=${name:1:${#name}-2}
|
||||
name_terminal=${filename##*_}
|
||||
filever=${name_terminal%%.*}
|
||||
|
||||
if [ "${BUILD_TAG}" != "${PROGRAM_VERSION} ${filever}" ]; then
|
||||
msgln "Deleting ${name}"
|
||||
curl --no-progress-meter --user "${GITEA_USER}:${PASSWORD}" -X DELETE ${GITEA_HOST}/api/packages/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/${filename}
|
||||
# else
|
||||
# echo "most recent version"
|
||||
fi
|
||||
done
|
||||
|
||||
#curl --user "${GITEA_USER}:${PASSWORD}" -X GET https://git.portale-stac.it/api/v1/packages/go-pkg/generic/dev-expr/1.7.0/files
|
14
go.mod
Normal file
14
go.mod
Normal file
@ -0,0 +1,14 @@
|
||||
module dev-expr
|
||||
|
||||
go 1.21.7
|
||||
|
||||
require (
|
||||
git.portale-stac.it/go-pkg/expr v0.17.0
|
||||
git.portale-stac.it/go-pkg/utils v0.2.0
|
||||
github.com/ergochat/readline v0.1.1
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
)
|
22
go.sum
Normal file
22
go.sum
Normal file
@ -0,0 +1,22 @@
|
||||
git.portale-stac.it/go-pkg/expr v0.1.0 h1:7xGEuUhdh6RRFaRbRnLVqVJBmHJWHfqjDBm2K0fIW2s=
|
||||
git.portale-stac.it/go-pkg/expr v0.1.0/go.mod h1:kUFEQkUMCJ1IiUKkL0P5/vznaAIzFI26Xf5P0rTXqR0=
|
||||
git.portale-stac.it/go-pkg/expr v0.2.0 h1:AAaVsV0uaC4EikKU91VuubIpbIN7wuya7t4avyFgg+0=
|
||||
git.portale-stac.it/go-pkg/expr v0.2.0/go.mod h1:DZqqZ3A9h4qEOs7yMvG4VZq7B/xhFsYqC3IKd3M2VKc=
|
||||
git.portale-stac.it/go-pkg/expr v0.17.0 h1:4ANGwJfwJO3AmnKka4Cf1AO9/ckGLMj8RIWeoDFKawQ=
|
||||
git.portale-stac.it/go-pkg/expr v0.17.0/go.mod h1:DZqqZ3A9h4qEOs7yMvG4VZq7B/xhFsYqC3IKd3M2VKc=
|
||||
git.portale-stac.it/go-pkg/utils v0.2.0 h1:2l4IVUhElzjaIUJlahPG2DZTGb9x7OXuFTO4z1K6LmY=
|
||||
git.portale-stac.it/go-pkg/utils v0.2.0/go.mod h1:PebQ45Qbe89aMTd3wcbcx1bkpNRW4/frNLnpuyZYovU=
|
||||
github.com/ergochat/readline v0.1.0 h1:KEIiAnyH9qGZB4K8oq5mgDcExlEKwmZDcyyocgJiABc=
|
||||
github.com/ergochat/readline v0.1.0/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
|
||||
github.com/ergochat/readline v0.1.1 h1:C8Uuo3ybB23GWOt0uxmHbGzKM9owmtXary6Clrj84s0=
|
||||
github.com/ergochat/readline v0.1.1/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
613
main.go
Normal file
613
main.go
Normal file
@ -0,0 +1,613 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// main.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr"
|
||||
"git.portale-stac.it/go-pkg/utils"
|
||||
|
||||
// https://pkg.go.dev/github.com/ergochat/readline#section-readme
|
||||
"github.com/ergochat/readline"
|
||||
)
|
||||
|
||||
const (
|
||||
intro = PROGNAME + ` -- Expressions calculator ` + VERSION + `
|
||||
Based on the Expr package ` + EXPR_VERSION + `
|
||||
Type help to get the list of available commands
|
||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||
`
|
||||
mainPrompt = ">>> "
|
||||
contPrompt = "... "
|
||||
|
||||
historyFile = "~/.expr_history"
|
||||
)
|
||||
|
||||
type commandFunction func(ctx expr.ExprContext, args []string) (err error)
|
||||
|
||||
type command struct {
|
||||
name string
|
||||
description string
|
||||
code commandFunction
|
||||
}
|
||||
|
||||
func (cmd *command) exec(ctx expr.ExprContext, args []string) (err error) {
|
||||
return cmd.code(ctx, args)
|
||||
}
|
||||
|
||||
type commandHandler struct {
|
||||
commands map[string]*command
|
||||
// ctx expr.ExprContext
|
||||
}
|
||||
|
||||
func NewCommandHandler() *commandHandler {
|
||||
return &commandHandler{
|
||||
commands: make(map[string]*command),
|
||||
}
|
||||
}
|
||||
|
||||
// func (h *commandHandler) setContext(ctx expr.ExprContext) {
|
||||
// h.ctx = ctx
|
||||
// }
|
||||
|
||||
func (h *commandHandler) add(name, description string, f commandFunction) {
|
||||
h.commands[name] = &command{name: name, description: description, code: f}
|
||||
}
|
||||
|
||||
func (h *commandHandler) get(cmdLine string) (cmd *command, args []string) {
|
||||
if len(cmdLine) > 0 {
|
||||
tokens := strings.Split(cmdLine, " ")
|
||||
name := tokens[0]
|
||||
args = make([]string, 0, len(tokens)-1)
|
||||
if cmd = h.commands[name]; cmd != nil && len(tokens) > 1 {
|
||||
for _, tk := range tokens[1:] {
|
||||
if tk != "" {
|
||||
args = append(args, tk)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ------
|
||||
|
||||
type Options struct {
|
||||
printTree bool
|
||||
printPrefix bool
|
||||
forceInteractive bool
|
||||
builtin []any
|
||||
expressions []io.Reader
|
||||
formOpt expr.FmtOpt
|
||||
baseVerb string
|
||||
base int
|
||||
output bool
|
||||
rcCount int
|
||||
}
|
||||
|
||||
func NewOptions() *Options {
|
||||
return &Options{
|
||||
expressions: make([]io.Reader, 0),
|
||||
builtin: make([]any, 0),
|
||||
formOpt: expr.Base10,
|
||||
baseVerb: "%d",
|
||||
base: 10,
|
||||
output: true,
|
||||
rcCount: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func errOptValueRequired(opt string) error {
|
||||
return fmt.Errorf("option %q requires a value", opt)
|
||||
}
|
||||
|
||||
func about() string {
|
||||
return PROGNAME + " -- " + VERSION + "; Expr package " + EXPR_VERSION
|
||||
}
|
||||
|
||||
func (opt *Options) loadRc() {
|
||||
var rcPath string
|
||||
var fh *os.File
|
||||
var err error
|
||||
if rcPath, err = utils.ExpandPath("~/.dev-expr.rc"); err != nil {
|
||||
return
|
||||
}
|
||||
if fh, err = os.Open(rcPath); err == nil {
|
||||
opt.expressions = append(opt.expressions, fh)
|
||||
opt.rcCount++
|
||||
}
|
||||
}
|
||||
|
||||
func (opt *Options) parseArgs() (err error) {
|
||||
for i := 1; i < len(os.Args) && err == nil; i++ {
|
||||
arg := os.Args[i]
|
||||
switch arg {
|
||||
case "-i":
|
||||
opt.forceInteractive = true
|
||||
case "-t":
|
||||
opt.printTree = true
|
||||
case "-p":
|
||||
opt.printPrefix = true
|
||||
case "-e":
|
||||
if i+1 < len(os.Args) {
|
||||
i++
|
||||
spec := os.Args[i]
|
||||
if strings.HasPrefix(spec, "@") {
|
||||
var f *os.File
|
||||
if f, err = os.Open(spec[1:]); err == nil {
|
||||
opt.expressions = append(opt.expressions, f)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
opt.expressions = append(opt.expressions, strings.NewReader(spec))
|
||||
}
|
||||
} else {
|
||||
err = errOptValueRequired(arg)
|
||||
}
|
||||
case "-b":
|
||||
if i+1 < len(os.Args) {
|
||||
i++
|
||||
specs := strings.Split(os.Args[i], ",")
|
||||
if len(specs) == 1 {
|
||||
opt.builtin = append(opt.builtin, specs[0])
|
||||
} else {
|
||||
opt.builtin = append(opt.builtin, specs)
|
||||
}
|
||||
} else {
|
||||
err = errOptValueRequired(arg)
|
||||
}
|
||||
case "-m", "--modules":
|
||||
expr.IterateBuiltinModules(func(name, description string, _ bool) bool {
|
||||
fmt.Printf("%20q: %s\n", name, description)
|
||||
return true
|
||||
})
|
||||
os.Exit(0)
|
||||
case "--noout":
|
||||
opt.output = false
|
||||
case "-h", "--help", "help":
|
||||
cmdHandler.help()
|
||||
os.Exit(0)
|
||||
case "-v", "--version", "version", "about":
|
||||
fmt.Println(about())
|
||||
os.Exit(0)
|
||||
default:
|
||||
err = fmt.Errorf("invalid option nr %d %q", i+1, arg)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ------
|
||||
var cmdHandler *commandHandler
|
||||
|
||||
func (h *commandHandler) help() {
|
||||
fmt.Fprintln(os.Stderr, `--- REPL commands:`)
|
||||
for _, cmd := range h.commands {
|
||||
fmt.Fprintf(os.Stderr, "%12s -- %s\n", cmd.name, cmd.description)
|
||||
}
|
||||
fmt.Fprint(os.Stderr, `
|
||||
--- Command line options:
|
||||
-b <builtin> Import builtin modules.
|
||||
<builtin> can be a list of module names or a glob-pattern.
|
||||
Use the special value 'all' or the pattern '*' to import all modules.
|
||||
-e <expression> Evaluate <expression> instead of standard-input
|
||||
-i Force REPL operation when all -e occurences have been processed
|
||||
-h, --help, help Show this help menu
|
||||
-m, --modules List all builtin modules
|
||||
--noout Disable printing of expression results
|
||||
-p Print prefix form
|
||||
-t Print tree form
|
||||
-v, --version Show program version
|
||||
`)
|
||||
|
||||
}
|
||||
|
||||
func importBuiltins(opt *Options) (err error) {
|
||||
for _, spec := range opt.builtin {
|
||||
if moduleSpec, ok := spec.(string); ok {
|
||||
if moduleSpec == "all" {
|
||||
moduleSpec = "*"
|
||||
}
|
||||
_, err = expr.ImportInContextByGlobPattern(moduleSpec)
|
||||
} else if moduleSpec, ok := spec.([]string); ok {
|
||||
notFoundList := make([]string, 0)
|
||||
for _, name := range moduleSpec {
|
||||
if !expr.ImportInContext(name) {
|
||||
notFoundList = append(notFoundList, name)
|
||||
}
|
||||
}
|
||||
if len(notFoundList) > 0 {
|
||||
err = fmt.Errorf("not found modules: %s", strings.Join(notFoundList, ","))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func initReadlineConfig(cfg *readline.Config) {
|
||||
if histfile, err := utils.ExpandPath(historyFile); err == nil {
|
||||
cfg.HistoryFile = histfile
|
||||
}
|
||||
cfg.Undo = true
|
||||
cfg.DisableAutoSaveHistory = true
|
||||
|
||||
}
|
||||
|
||||
func goInteractiveReadline(opt *Options, ctx expr.ExprContext, r io.Reader) {
|
||||
var sb strings.Builder
|
||||
var cfg readline.Config
|
||||
initReadlineConfig(&cfg)
|
||||
rl, err := readline.NewFromConfig(&cfg)
|
||||
if err != nil {
|
||||
goInteractive(opt, ctx, r)
|
||||
return
|
||||
}
|
||||
defer rl.Close()
|
||||
|
||||
fmt.Print(intro)
|
||||
rl.SetPrompt(mainPrompt)
|
||||
for line, err := rl.ReadLine(); err == nil; line, err = rl.ReadLine() {
|
||||
if len(line) > 0 && line[len(line)-1] == '\\' {
|
||||
sb.WriteString(line[0 : len(line)-1])
|
||||
rl.SetPrompt(contPrompt)
|
||||
continue
|
||||
}
|
||||
rl.SetPrompt(mainPrompt)
|
||||
sb.WriteString(line)
|
||||
source := strings.TrimSpace(sb.String())
|
||||
if source != "" && !strings.HasPrefix(source, "//") {
|
||||
if cmd, args := cmdHandler.get(source); cmd != nil {
|
||||
rl.SaveToHistory(source)
|
||||
if err = cmd.exec(ctx, args); err != nil {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
rl.SaveToHistory(source)
|
||||
r := strings.NewReader(source)
|
||||
compute(opt, ctx, r, true)
|
||||
|
||||
}
|
||||
}
|
||||
sb.Reset()
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func goInteractive(opt *Options, ctx expr.ExprContext, r io.Reader) {
|
||||
var sb strings.Builder
|
||||
fmt.Print(intro)
|
||||
fmt.Print(mainPrompt)
|
||||
reader := bufio.NewReaderSize(r, 1024)
|
||||
for line, err := reader.ReadString('\n'); err == nil && line != "exit\n"; line, err = reader.ReadString('\n') {
|
||||
if strings.HasSuffix(line, "\\\n") {
|
||||
sb.WriteString(line[0 : len(line)-2])
|
||||
fmt.Print(contPrompt)
|
||||
continue
|
||||
}
|
||||
sb.WriteString(line)
|
||||
source := strings.TrimSpace(sb.String())
|
||||
// fmt.Printf("source=%q\n", source)
|
||||
if source != "" && !strings.HasPrefix(source, "//") {
|
||||
if cmd, args := cmdHandler.get(source); cmd != nil {
|
||||
if err = cmd.exec(ctx, args); err != nil {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
r := strings.NewReader(source)
|
||||
compute(opt, ctx, r, true)
|
||||
}
|
||||
}
|
||||
sb.Reset()
|
||||
fmt.Print(mainPrompt)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func goBatch(opt *Options, ctx expr.ExprContext, r io.Reader) {
|
||||
var sb strings.Builder
|
||||
reader := bufio.NewReaderSize(r, 1024)
|
||||
for line, err := reader.ReadString('\n'); err == nil && line != "exit\n"; line, err = reader.ReadString('\n') {
|
||||
if strings.HasSuffix(line, "\\\n") {
|
||||
sb.WriteString(line[0 : len(line)-2])
|
||||
continue
|
||||
}
|
||||
sb.WriteString(line)
|
||||
source := strings.TrimSpace(sb.String())
|
||||
// fmt.Printf("source=%q\n", source)
|
||||
if source != "" && !strings.HasPrefix(source, "//") {
|
||||
if cmd, args := cmdHandler.get(source); cmd != nil {
|
||||
if err = cmd.exec(ctx, args); err != nil {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
r := strings.NewReader(source)
|
||||
compute(opt, ctx, r, false)
|
||||
}
|
||||
}
|
||||
sb.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func compute(opt *Options, ctx expr.ExprContext, r io.Reader, outputEnabled bool) {
|
||||
scanner := expr.NewScanner(r, expr.DefaultTranslations())
|
||||
parser := expr.NewParser()
|
||||
|
||||
if ast, err := parser.Parse(scanner); err == nil {
|
||||
if opt.printPrefix {
|
||||
fmt.Println(ast)
|
||||
}
|
||||
if opt.printTree {
|
||||
r := expr.NewExprReticle(ast)
|
||||
fmt.Println(r.String())
|
||||
}
|
||||
|
||||
outputEnabled = outputEnabled && opt.output
|
||||
if result, err := ast.Eval(ctx); err == nil {
|
||||
if outputEnabled {
|
||||
printResult(opt, result)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, "Eval Error:", err)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, "Parse Error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func printResult(opt *Options, result any) {
|
||||
if f, ok := result.(expr.Formatter); ok {
|
||||
fmt.Println(f.ToString(opt.formOpt))
|
||||
} else if expr.IsInteger(result) {
|
||||
fmt.Printf(opt.baseVerb, result)
|
||||
fmt.Println()
|
||||
} else if expr.IsString(result) {
|
||||
fmt.Printf("\"%s\"\n", result)
|
||||
} else {
|
||||
fmt.Println(result)
|
||||
}
|
||||
}
|
||||
|
||||
func isReaderTerminal(r io.Reader) bool {
|
||||
if fh, ok := r.(*os.File); ok {
|
||||
return utils.StreamIsTerminal(fh)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func registerLocalFunctions(ctx expr.ExprContext) {
|
||||
aboutFunc := func(ctx expr.ExprContext, name string, args []any) (result any, err error) {
|
||||
result = about()
|
||||
return
|
||||
}
|
||||
|
||||
ctrlListFunc := func(ctx expr.ExprContext, name string, args []any) (result any, err error) {
|
||||
vars := ctx.EnumVars(func(name string) bool {
|
||||
return len(name) > 0 && name[0] == '_'
|
||||
})
|
||||
result = expr.ListFromStrings(vars)
|
||||
return
|
||||
}
|
||||
|
||||
ctrlFunc := func(ctx expr.ExprContext, name string, args []any) (result any, err error) {
|
||||
varName, _ := args[0].(string)
|
||||
if len(args) == 1 {
|
||||
result = expr.GlobalCtrlGet(varName)
|
||||
} else {
|
||||
result = expr.GlobalCtrlSet(varName, args[1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
envSetFunc := func(ctx expr.ExprContext, name string, args []any) (result any, err error) {
|
||||
var varName, value string
|
||||
var ok bool
|
||||
if varName, ok = args[0].(string); !ok {
|
||||
err = expr.ErrExpectedGot(name, expr.TypeString, args[0])
|
||||
return
|
||||
}
|
||||
if value, ok = args[1].(string); !ok {
|
||||
err = expr.ErrExpectedGot(name, expr.TypeString, args[1])
|
||||
return
|
||||
}
|
||||
if err = os.Setenv(varName, value); err == nil {
|
||||
result = value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
envGetFunc := func(ctx expr.ExprContext, name string, args []any) (result any, err error) {
|
||||
var varName string
|
||||
var ok bool
|
||||
if varName, ok = args[0].(string); !ok {
|
||||
err = expr.ErrExpectedGot(name, expr.TypeString, args[0])
|
||||
return
|
||||
}
|
||||
|
||||
if result, ok = os.LookupEnv(varName); !ok {
|
||||
err = fmt.Errorf("environment variable %q does not exist", varName)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.RegisterFunc("about", expr.NewGolangFunctor(aboutFunc), expr.TypeString, []expr.ExprFuncParam{})
|
||||
ctx.RegisterFunc("ctrlList", expr.NewGolangFunctor(ctrlListFunc), expr.TypeListOfStrings, []expr.ExprFuncParam{})
|
||||
ctx.RegisterFunc("ctrl", expr.NewGolangFunctor(ctrlFunc), expr.TypeAny, []expr.ExprFuncParam{
|
||||
expr.NewFuncParam("prop"),
|
||||
expr.NewFuncParamFlag(expr.ParamValue, expr.PfOptional),
|
||||
})
|
||||
ctx.RegisterFunc("envSet", expr.NewGolangFunctor(envSetFunc), expr.TypeString, []expr.ExprFuncParam{
|
||||
expr.NewFuncParam(expr.ParamName),
|
||||
expr.NewFuncParam(expr.ParamValue),
|
||||
})
|
||||
ctx.RegisterFunc("envGet", expr.NewGolangFunctor(envGetFunc), expr.TypeString, []expr.ExprFuncParam{
|
||||
expr.NewFuncParam(expr.ParamName),
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
var opt *Options
|
||||
|
||||
func main() {
|
||||
opt = NewOptions()
|
||||
opt.loadRc()
|
||||
if err := opt.parseArgs(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := importBuiltins(opt); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx := expr.NewSimpleStore()
|
||||
registerLocalFunctions(ctx)
|
||||
|
||||
if len(opt.expressions) == opt.rcCount || opt.forceInteractive {
|
||||
opt.expressions = append(opt.expressions, os.Stdin)
|
||||
}
|
||||
|
||||
for _, input := range opt.expressions {
|
||||
if isReaderTerminal(input) {
|
||||
goInteractiveReadline(opt, ctx, input)
|
||||
} else {
|
||||
goBatch(opt, ctx, input)
|
||||
if f, ok := input.(*os.File); ok {
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
if opt.output {
|
||||
printResult(opt, ctx.GetLast())
|
||||
}
|
||||
}
|
||||
|
||||
// --------
|
||||
|
||||
func cmdExit(ctx expr.ExprContext, args []string) (err error) {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
func cmdHelp(ctx expr.ExprContext, args []string) (err error) {
|
||||
cmdHandler.help()
|
||||
return
|
||||
}
|
||||
|
||||
func cmdMultiLine(ctx expr.ExprContext, args []string) (err error) {
|
||||
if opt.formOpt&expr.MultiLine == 0 {
|
||||
opt.formOpt |= expr.MultiLine
|
||||
} else {
|
||||
opt.formOpt &= ^expr.MultiLine
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cmdTty(ctx expr.ExprContext, args []string) (err error) {
|
||||
if opt.formOpt&expr.TTY == 0 {
|
||||
opt.formOpt |= expr.TTY
|
||||
} else {
|
||||
opt.formOpt &= ^expr.TTY
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cmdSource(ctx expr.ExprContext, args []string) (err error) {
|
||||
var fh *os.File
|
||||
for _, arg := range args {
|
||||
length := len(arg)
|
||||
if length == 0 {
|
||||
continue
|
||||
}
|
||||
if length >= 2 {
|
||||
if (arg[0] == '"' && arg[length-1] == '"') || arg[0] == '\'' && arg[length-1] == '\'' {
|
||||
arg = arg[1 : length-1]
|
||||
}
|
||||
}
|
||||
if fh, err = os.Open(arg); err == nil {
|
||||
goBatch(opt, ctx, fh)
|
||||
fh.Close()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cmdModules(ctx expr.ExprContext, args []string) (err error) {
|
||||
expr.IterateBuiltinModules(func(name, description string, imported bool) bool {
|
||||
var check rune = ' '
|
||||
if imported {
|
||||
check = '*'
|
||||
}
|
||||
fmt.Printf("%c %20q: %s\n", check, name, description)
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func cmdBase(ctx expr.ExprContext, args []string) (err error) {
|
||||
if len(args) == 0 {
|
||||
fmt.Println(opt.base)
|
||||
} else if args[0] == "2" {
|
||||
opt.baseVerb = "0b%b"
|
||||
opt.base = 2
|
||||
} else if args[0] == "8" {
|
||||
opt.baseVerb = "0o%o"
|
||||
opt.base = 8
|
||||
} else if args[0] == "10" {
|
||||
opt.baseVerb = "%d"
|
||||
opt.base = 10
|
||||
} else if args[0] == "16" {
|
||||
opt.baseVerb = "0x%x"
|
||||
opt.base = 16
|
||||
} else {
|
||||
err = fmt.Errorf("invalid number base %s", args[0])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cmdOutput(ctx expr.ExprContext, args []string) (err error) {
|
||||
var outputArg string
|
||||
if len(args) == 0 {
|
||||
outputArg = "status"
|
||||
} else {
|
||||
outputArg = strings.ToLower(args[0])
|
||||
}
|
||||
switch outputArg {
|
||||
case "on":
|
||||
opt.output = true
|
||||
case "off":
|
||||
opt.output = false
|
||||
case "status":
|
||||
if opt.output {
|
||||
fmt.Println("on")
|
||||
} else {
|
||||
fmt.Println("off")
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("output: unknown option %q", outputArg)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//------------------
|
||||
|
||||
func init() {
|
||||
cmdHandler = NewCommandHandler()
|
||||
cmdHandler.add("base", "Set the integer output base: 2, 8, 10, or 16", cmdBase)
|
||||
cmdHandler.add("exit", "Exit the program", cmdExit)
|
||||
cmdHandler.add("help", "Show command list", cmdHelp)
|
||||
cmdHandler.add("ml", "Enable/Disable multi-line output", cmdMultiLine)
|
||||
cmdHandler.add("mods", "List builtin modules", cmdModules)
|
||||
cmdHandler.add("output", "Enable/Disable printing expression results. Options 'on', 'off', 'status'", cmdOutput)
|
||||
cmdHandler.add("source", "Load a file as input", cmdSource)
|
||||
cmdHandler.add("tty", "Enable/Disable ansi output", cmdTty)
|
||||
}
|
79
publish.bash
Executable file
79
publish.bash
Executable file
@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
# All rights reserved.
|
||||
|
||||
RESOURCE_FILE=".build.rc"
|
||||
BUILD_REGISTER=".build_register"
|
||||
PASSWORD=
|
||||
GITEA_USER="camoroso"
|
||||
GITEA_PASSWORD_FILE="${HOME}/.gitea_password"
|
||||
GITEA_OWNER="go-pkg"
|
||||
GITEA_HOST="https://git.portale-stac.it"
|
||||
GITEA_BASE_PATH="api/packages"
|
||||
GITEA_PKG_TYPE="generic"
|
||||
|
||||
function exitMsg() {
|
||||
echo >&2 "${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function readBuildCount() {
|
||||
local reg ver count
|
||||
if [ -r "${BUILD_REGISTER}" ]; then
|
||||
reg=$(<"${BUILD_REGISTER}")
|
||||
else
|
||||
reg="${PROGRAM_VERSION} 0"
|
||||
fi
|
||||
read ver count <<<"${reg}"
|
||||
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
|
||||
count=0
|
||||
fi
|
||||
echo ${count}
|
||||
}
|
||||
|
||||
if [ -r "${GITEA_PASSWORD_FILE}" ]; then
|
||||
if ! PASSWORD=$(<"${GITEA_PASSWORD_FILE}"); then
|
||||
exitMsg "Can're password file '${GITEA_PASSWORD_FILE}'"
|
||||
fi
|
||||
else
|
||||
exitMsg "Password file '${GITEA_PASSWORD_FILE}' not found"
|
||||
fi
|
||||
if [ -z "${PASSWORD}" ]; then
|
||||
exitMsg "Empty password. Please, check file '${GITEA_PASSWORD_FILE}'"
|
||||
fi
|
||||
|
||||
if ! ./build.bash; then
|
||||
exitMsg "Build program failed"
|
||||
fi
|
||||
|
||||
if ! source "${RESOURCE_FILE}"; then
|
||||
exitMsg "Loading resource file failed"
|
||||
fi
|
||||
|
||||
if ! exeList=$(echo 2>/dev/null ${PROGRAM_NAME}_v${PROGRAM_VERSION}_*); then
|
||||
exitMsg "No executable found"
|
||||
fi
|
||||
|
||||
buildCount=$(readBuildCount)
|
||||
fileCount=0
|
||||
for exe in ${exeList}; do
|
||||
if [ "${exe/tar.gz/}" != "${exe}" ]; then
|
||||
continue
|
||||
fi
|
||||
((fileCount++))
|
||||
dir="${exe}_${buildCount}"
|
||||
dist="${dir}.tar.gz"
|
||||
rm -f "${dist}"
|
||||
printf "%2d: %-30s --> %s\n" "${fileCount}" "${exe}" "${dist}"
|
||||
mkdir "${dir}"
|
||||
cp "${exe}" "${dir}/${PROGRAM_NAME}"
|
||||
tar czf "${dist}" "${dir}"
|
||||
rm -fR "${dir}"
|
||||
|
||||
url="${GITEA_HOST}/${GITEA_BASE_PATH}/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/${dist}"
|
||||
# echo "${url}"
|
||||
curl --user "${USER}:${PASSWORD}" --upload-file "${dist}" "${url}"
|
||||
|
||||
rm -f "${dist}"
|
||||
done
|
||||
|
1
version.txt
Normal file
1
version.txt
Normal file
@ -0,0 +1 @@
|
||||
1.10.0
|
Loading…
Reference in New Issue
Block a user