diff --git a/gui-test.slo b/gui-test.slo index 0fcce63..3c10473 100644 --- a/gui-test.slo +++ b/gui-test.slo @@ -9,16 +9,16 @@ ; Set up the widgets (define l (widget-make-label "Log In" 0)) (define l1 (widget-make-label "Text")) -(define e1 (widget-make-entry "Link Text" #f (callback (lambda (s) (if (regex-match? ".+" s) #t "Invalid entry"))))) +(define e1 (widget-make-entry "Link Text" #f (lambda (s) (if (regex-match? ".+" s) #t "Invalid entry")))) (define l2 (widget-make-label "URL")) -(define e2 (widget-make-entry "Link URL" #f (callback (lambda (s) (if (regex-match? "[http|https|gemini|gopher|ftp]://.+\..+" s) #t "URL missing scheme"))))) +(define e2 (widget-make-entry "Link URL" #f (lambda (s) (if (regex-match? "[http|https|gemini|gopher|ftp]://.+\..+" s) #t "URL missing scheme")))) (define b (widget-make-button "Submit" - (callback (lambda () + (lambda () (define t (widget-get-text e1)) (define u (widget-get-text e2)) (widget-set-text e1 "") (widget-set-text e2 "") - (container-add-to contents (container "hbox" (widget-make-spacer) (widget-make-hyperlink t u))))) 0)) + (container-add-to contents (container "hbox" (widget-make-spacer) (widget-make-hyperlink t u)))) 0)) ; Set up the container(s) (set! contents (container "vbox" l (widget-make-separator) (container "form" l1 e1 l2 e2) b (widget-make-separator) (container "hbox" (widget-make-spacer) (widget-make-hyperlink "by sloum" "http://sloum.colorfield.space/")))) diff --git a/gui.go b/gui.go index e484b58..64bb983 100644 --- a/gui.go +++ b/gui.go @@ -2,11 +2,18 @@ package main +// TODO add: +// widget-make-text-grid +// widget-make-check +// widget-make-select + import ( "fmt" "image/color" "net/url" "os" + "strconv" + "strings" "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" @@ -23,15 +30,23 @@ type GUI struct { } func (g GUI) String() string { - return "GUI" + return "GUI-Root" } type guiWidget fyne.Widget + type guiContainer fyne.CanvasObject + type guiObject interface{} type slopeTheme struct{} +func (t slopeTheme) String() string { + return "GUI-Theme" +} + +// Dev note: no memory of why this is +// done this way. var _ fyne.Theme = (*slopeTheme)(nil) func (m slopeTheme) Color(name fyne.ThemeColorName, variant fyne.ThemeVariant) color.Color { @@ -91,7 +106,12 @@ var guiLib = vars{ }, "gui-use-light-theme": func(a ...expression) expression { if len(a) < 1 { - return exception("'gui-use-light-theme' expected a bool, but was given no arguments") + v := os.Getenv("FYNE_THEME") + if v == "light" { + return true + } else { + return false + } } b := AnythingToBool(a[0]).(bool) if b { @@ -104,20 +124,20 @@ var guiLib = vars{ "window-set-content": func(a ...expression) expression { // (window-set-content window-id content...) if len(a) < 3 { - return exception("'window-set-content' expects a GUI and a string as arguments, too few arguments were given") + return exception("'window-set-content' expects a gui-root and a string as arguments, too few arguments were given") } g, ok := a[0].(*GUI) if !ok { - return exception("'window-set-content' was given a non-GUI value as its first argument") + return exception("'window-set-content' was given a non-gui-root value as its first argument") } s := String(a[1], false) w, ok := g.windows[s] if !ok { - return exception("'window-show-and-run' was given a window ID that does not exist: " + s) + return exception("'window-set-content' was given a window-id that does not exist: " + s) } content, ok := a[2].(fyne.CanvasObject) if !ok { - return exception("'window-show-and-run' was given invalid content") + return exception("'window-set-content' was given invalid content") } w.SetContent(content) return true @@ -133,7 +153,7 @@ var guiLib = vars{ s := String(a[1], false) w, ok := g.windows[s] if !ok { - return exception("'window-resize' was given a window ID that does not exist: " + s) + return exception("'window-resize' was given a window-id that does not exist: " + s) } width, ok := a[2].(number) height, ok2 := a[3].(number) @@ -154,7 +174,7 @@ var guiLib = vars{ s := String(a[1], false) w, ok := g.windows[s] if !ok { - return exception("'window-show' was given a window ID that does not exist: " + s) + return exception("'window-show' was given a window-id that does not exist: " + s) } w.Show() return true @@ -170,7 +190,7 @@ var guiLib = vars{ s := String(a[1], false) w, ok := g.windows[s] if !ok { - return exception("'window-hide' was given a window ID that does not exist: " + s) + return exception("'window-hide' was given a window-id that does not exist: " + s) } w.Hide() return true @@ -186,7 +206,7 @@ var guiLib = vars{ s := String(a[1], false) w, ok := g.windows[s] if !ok { - return exception("'window-close' was given a window ID that does not exist: " + s) + return exception("'window-close' was given a window-id that does not exist: " + s) } w.Close() return true @@ -202,7 +222,7 @@ var guiLib = vars{ s := String(a[1], false) w, ok := g.windows[s] if !ok { - return exception("'window-set-title' was given a window ID that does not exist: " + s) + return exception("'window-set-title' was given a window-id that does not exist: " + s) } w.SetTitle(String(a[2], false)) return true @@ -218,7 +238,7 @@ var guiLib = vars{ s := String(a[1], false) w, ok := g.windows[s] if !ok { - return exception("'window-set-fullscreen' was given a window ID that does not exist: " + s) + return exception("'window-set-fullscreen' was given a window-id that does not exist: " + s) } w.SetFullScreen(AnythingToBool(a[2]).(bool)) return true @@ -235,7 +255,7 @@ var guiLib = vars{ s := String(a[1], false) w, ok := g.windows[s] if !ok { - return exception("'window-show-and-run' was given a window ID that does not exist: " + s) + return exception("'window-show-and-run' was given a window-id that does not exist: " + s) } w.ShowAndRun() return true @@ -251,7 +271,7 @@ var guiLib = vars{ s := String(a[1], false) w, ok := g.windows[s] if !ok { - return exception("'window-center' was given a window ID that does not exist: " + s) + return exception("'window-center' was given a window-id that does not exist: " + s) } w.CenterOnScreen() return true @@ -267,7 +287,7 @@ var guiLib = vars{ s := String(a[1], false) w, ok := g.windows[s] if !ok { - return exception("'window-allow-resize' was given a window ID that does not exist: " + s) + return exception("'window-allow-resize' was given a window-id that does not exist: " + s) } w.SetFixedSize(!AnythingToBool(a[2]).(bool)) return true @@ -364,12 +384,12 @@ var guiLib = vars{ // Handle validation if len(a) > 2 { - cb, ok := a[2].(Callback) + cb, ok := a[2].(proc) if !ok { - return exception("'widget-make-entry' expects a callback as its optional third argument, a non-callback value was given") + return exception("'widget-make-entry' expects a lambdaas its optional third argument, a non-lambda value was given") } e.(*widget.Entry).Validator = func(in string) error { - result := apply(cb.p, []expression{in}) + result := apply(cb, []expression{in}) switch t := result.(type) { case string: return fmt.Errorf(t) @@ -400,12 +420,12 @@ var guiLib = vars{ // Handle validation if len(a) > 2 { - cb, ok := a[2].(Callback) + cb, ok := a[2].(proc) if !ok { return exception("'widget-make-entry' expects a callback as its optional third argument, a non-callback value was given") } e.(*widget.Entry).Validator = func(in string) error { - result := apply(cb.p, []expression{in}) + result := apply(cb, []expression{in}) switch t := result.(type) { case string: return fmt.Errorf(t) @@ -436,12 +456,12 @@ var guiLib = vars{ // Handle validation if len(a) > 2 { - cb, ok := a[2].(Callback) + cb, ok := a[2].(proc) if !ok { return exception("'widget-make-entry' expects a callback as its optional third argument, a non-callback value was given") } e.(*widget.Entry).Validator = func(in string) error { - result := apply(cb.p, []expression{in}) + result := apply(cb, []expression{in}) switch t := result.(type) { case string: return fmt.Errorf(t) @@ -475,10 +495,7 @@ var guiLib = vars{ } } if len(a) >= 3 { - wrap, ok := a[2].(bool) - if !ok { - return exception("'widget-make-label' expects a bool as its optional third argument (wrap), a non-bool value was given") - } + wrap := AnythingToBool(a[0]).(bool) if wrap { l.(*widget.Label).Wrapping = fyne.TextWrapWord } @@ -496,16 +513,15 @@ var guiLib = vars{ return guiWidget(widget.NewHyperlink(String(a[0], false), u)) }, "widget-make-button": func(a ...expression) expression { - // (widget-label text:string alignment:number wrap:bool) if len(a) < 2 { - return exception("'widget-make-button' expects at least one argument, a string representing the text of the label, no arguments were given") + return exception("'widget-make-button' expects at least two argument, a string representing the text of the label and a lambda, too few arguments were given") } var b guiWidget switch f := a[1].(type) { case func(...expression) expression: b = widget.NewButton(String(a[0], false), func() { f() }) - case Callback: - b = widget.NewButton(String(a[0], false), func() { apply(f.p, []expression{}) }) + case proc: + b = widget.NewButton(String(a[0], false), func() { apply(f, []expression{}) }) default: return exception("'widget-make-button' was given a non-procedure value as its second argument") } @@ -542,8 +558,10 @@ var guiLib = vars{ return wid.Text case *widget.Entry: return wid.Text + case *widget.Hyperlink: + return wid.Text default: - return exception("'widget-set-text' expected a valid widget") + return exception("'widget-get-text' received a widget that it does not support") } }, "widget-set-text": func(a ...expression) expression { @@ -562,6 +580,8 @@ var guiLib = vars{ wid.SetText(txt) case *widget.Entry: wid.SetText(txt) + case *widget.Hyperlink: + wid.SetText(txt) default: return exception("'widget-set-text' expected a valid widget") } @@ -576,11 +596,11 @@ var guiLib = vars{ }, "container": func(a ...expression) expression { if len(a) < 2 { - return exception("'container-with-layout' expects at least two arguments, a string representing the layout type and at least one widget or layout, no values were given") + return exception("'container' expects two arguments, a string representing the layout type and at least one widget or layout, no values were given") } list, err := ExpToCanvasObjectList(a[1:]) if err != nil { - return exception("'container-with-layout' was given a list that contained a non widget value") + return exception("'container' was given a non-widget value") } switch String(a[0], false) { case "none": @@ -632,7 +652,7 @@ var guiLib = vars{ }, "dialog-open-file": func(a ...expression) expression { if len(a) < 3 { - return exception("'dialog-open-file' expected three arguments: a GUI, a string representing a gui-window ID and a callback that takes a string as an argument; an insufficient number of arguments was given") + return exception("'dialog-open-file' expected three arguments: a GUI, a string representing a gui-window-id and a callback that takes a string as an argument; an insufficient number of arguments was given") } app, ok := a[0].(*GUI) if !ok { @@ -640,9 +660,9 @@ var guiLib = vars{ } window := String(a[1], false) if _, ok := app.windows[window]; !ok { - return exception("'dialog-open-file' was given an invalid window ID: " + window) + return exception("'dialog-open-file' was given an invalid window-id: " + window) } - cb, ok := a[2].(Callback) + cb, ok := a[2].(proc) if !ok { return exception("'dialog-open-file' expected a callback as its second argument, a non-callback value was given") } @@ -652,7 +672,7 @@ var guiLib = vars{ } defer reader.Close() u := reader.URI().Path() - apply(cb.p, []expression{u}) + apply(cb, []expression{u}) return }, app.windows[window]) fd.Show() @@ -660,7 +680,7 @@ var guiLib = vars{ }, "dialog-save-file": func(a ...expression) expression { if len(a) < 3 { - return exception("'dialog-save-file' expected three arguments: a GUI, a string representing a gui-window ID and a callback that takes a string as an argument; an insufficient number of arguments was given") + return exception("'dialog-save-file' expected three arguments: a GUI, a string representing a gui-window-id and a callback that takes a string as an argument; an insufficient number of arguments was given") } app, ok := a[0].(*GUI) if !ok { @@ -668,9 +688,9 @@ var guiLib = vars{ } window := String(a[1], false) if _, ok := app.windows[window]; !ok { - return exception("'dialog-save-file' was given an invalid window ID: " + window) + return exception("'dialog-save-file' was given an invalid window-id: " + window) } - cb, ok := a[2].(Callback) + cb, ok := a[2].(proc) if !ok { return exception("'dialog-save-file' expected a callback as its second argument, a non-callback value was given") } @@ -680,7 +700,7 @@ var guiLib = vars{ } defer writer.Close() u := writer.URI().Path() - apply(cb.p, []expression{u}) + apply(cb, []expression{u}) return }, app.windows[window]) fd.Show() @@ -688,7 +708,7 @@ var guiLib = vars{ }, "dialog-info": func(a ...expression) expression { if len(a) < 4 { - return exception("'dialog-info' expected four arguments: a GUI, a string representing a gui-window ID, a title string, and a message string; an insufficient number of arguments was given") + return exception("'dialog-info' expected four arguments: a GUI, a string representing a gui-window-id, a title string, and a message string; an insufficient number of arguments was given") } app, ok := a[0].(*GUI) if !ok { @@ -696,14 +716,14 @@ var guiLib = vars{ } window := String(a[1], false) if _, ok := app.windows[window]; !ok { - return exception("'dialog-info' was given an invalid window ID: " + window) + return exception("'dialog-info' was given an invalid window-id: " + window) } dialog.ShowInformation(String(a[2], false), String(a[3], false), app.windows[window]) return []expression{} }, "dialog-error": func(a ...expression) expression { if len(a) < 3 { - return exception("'dialog-error' expected three arguments: a GUI, a string representing a gui-window ID, an error string; an insufficient number of arguments was given") + return exception("'dialog-error' expected three arguments: a GUI, a string representing a gui-window-id, an error string; an insufficient number of arguments was given") } app, ok := a[0].(*GUI) if !ok { @@ -711,13 +731,15 @@ var guiLib = vars{ } window := String(a[1], false) if _, ok := app.windows[window]; !ok { - return exception("'dialog-error' was given an invalid window ID: " + window) + return exception("'dialog-error' was given an invalid window-id: " + window) } dialog.ShowError(fmt.Errorf(String(a[2], false)), app.windows[window]) return []expression{} }, } +// - BEGIN helpers + func ExpToCanvasObjectList(e []expression) ([]fyne.CanvasObject, error) { out := make([]fyne.CanvasObject, len(e)) for i, v := range e { @@ -738,3 +760,64 @@ func ExpToCanvasObjectList(e []expression) ([]fyne.CanvasObject, error) { } return out, nil } + +// String converts any value to a string +// if rawString is true escapes will be +// shown visually and quotes will be around +// a string. rawString has no effect on +// values that are not already strings +func String(v expression, rawString bool) string { + switch v := v.(type) { + case []expression: + l := make([]string, len(v)) + for i, x := range v { + l[i] = String(x, true) + } + return "(" + strings.Join(l, " ") + ")" + case string: + if rawString { + return fmt.Sprintf("\"%s\"", escapeString(v)) + } + return v + case exception: + return string(v) + case bool: + if v { + return "#t" + } + return "#f" + case number: + return strconv.FormatFloat(float64(v), 'f', -1, 64) + case proc: + var b strings.Builder + b.WriteString("(lambda ") + b.WriteString(String(v.params, true)) + b.WriteRune(' ') + body := String(v.body, true) + if strings.HasPrefix(body, "(begin ") { + body = body[7:] + } + b.WriteString(body) + return b.String() + case func(...expression) expression: + return fmt.Sprint("Built-in: ", &v) + case *IOHandle: + return fmt.Sprintf("%+v", v) + case guiWidget: + return "gui-widget" + case fyne.Widget: + return "gui-widget" + case fyne.CanvasObject: + return "gui-widget" + case fyne.Container: + return "gui-container" + case guiContainer: + return "gui-container" + case GUI: + return "gui-root" + case *GUI: + return "gui-root" + default: + return fmt.Sprint(v) + } +} diff --git a/guiStatic.go b/guiStatic.go new file mode 100644 index 0000000..b176134 --- /dev/null +++ b/guiStatic.go @@ -0,0 +1,44 @@ +//+build !nogui + +package main + +const guiIsOn = "(gui)" + +var guiUsageStrings = map[string]string{ + "gui-create": "(gui-create) => GUI-Root\n\nCreates the application root for a gui application. See: `gui-add-window` for information about adding content to a gui-root. In general you will always want to use `define` when creating a GUI-Root as everything is done by passing it as a reference later on in the development process for GUI applications", + "gui-add-window": "(gui-add-window [app: gui-root] [name: string])\n\nAdds a window to the given gui-root as returned by `gui-create`. Name will be used to refer to the newly created window in other procedures. A string is prefered and called for in the procedure signature, but any other value can be used and will be stringified", + "gui-list-windows": "(gui-list-windows [app: gui-root]) => list\n\nReturns a list containing the name of each window associated with the given gui-root", + "gui-use-light-theme": "(gui-use-light-theme [[bool]]) => bool\n\nIf no arguments are given `gui-use-light-theme` will check is a light theme is being used and return #t if so, #f if not. Any value passed to `gui-use-light-theme` will be coerced to a bool. If the given value is #t then the gui will be set to use the light them, otherwise the dark theme will be used", + "window-set-content": "(window-set-content [app: gui-root] [window-name: string] [content: gui-widget|gui-container]) => #t\n\nSets the content for the given window. This is usually a `container`, but could be a `widget`. #t or an exception will be returned", + "window-resize": "(window-resize [app: gui-root] [window-name: string] [width: number] [height: number]) => #t\n\nResizes the given app's given window to the width and height that were given, if possible. This is a finicky function in the underlying library and seems to behave unexpectedly or not at all", + "window-show": "(window-show [app: gui-root] [window-name: string]) => #t\n\nShows the given window. There is no effect when called on an already visible window", + "window-hide": "(window-hide [app: gui-root] [window-name: string]) => #t\n\nHides the given window. There is no effect when called on an already hidden window", + "window-close": "(window-close [app: gui-root] [window-name: string]) => #t\n\nCloses the given window. When called on an already closed window the behavior is undefined", + "window-set-title": "(window-set-title [app: gui-root] [window-name: string] [title: string]) => #t\n\nSets the title of the window to the given title string. If a non-string value is passed as title a stringified version of the value will be used", + "window-set-fullscreen": "(window-set-fullscreen [app: gui-root] [window-name: string] [set-to-full-screen: bool]) => #t\n\nSets the given window to full screen if #t is given and removes it from full screen if #f is given", + "window-show-and-run": "(window-show-and-run [app: gui-root] [window-name: string]) => #t\n\nRuns the gui with the given window visible. This is the main procedure for starting the gui and will block once evaluated until the window is closed", + "window-center": "(window-center [app: gui-root] [window-name: string]) => #t\n\nCenters the given window on the screen", + "window-allow-resize": "(window-allow-resize [app: gui-root] [window-name: string] [bool]) => #t\n\nSets whether or not to allow the user to resize the given window", + "widget-show": "(widget-show [gui-widget]) => #t\n\nShows the given gui-widget. The widget will only become visible if it is in a visible grouping (window, container, layout, etc). Calling on an already visible widget will have no effect", + "widget-hide": "(widget-show [gui-widget]) => #t\n\nHides the given gui-widget. Calling on an already hidden widget will have no effect", + "widget-resize": "(widget-resize [gui-widget] [width: number] [height: number]) => gui-widget\n\nIn general the size of widgets is defined by the container/layout. However, in cases where it is not, `widget-resize` can be used. If used in a situation where it cannot be used, there will be no effect. Returns the widget with the new size", + "widget-size": "(widget-size [gui-widget]) => list\n\nReturns the given widget's current width and height, in that order, in a two item list", + "widget-add-to-size": "(widget-add-to-size [gui-widget] [width-change: number] [height-change: number]) => gui-widget\n\nUpdates the width and height of the given widget by adding the width-change value to the current width and the height-change value to the current height. Returns the updated widget", + "widget-make-entry": "(widget-make-entry [placeholder-text: string] [[wrap-text: bool]] [[validation-callback: lambda]]) => gui-widget\n\nCreates an entry widget with the given placeholder value. For accessibility purposes it is always advised to also have a label widget describing this widget. If a validation lambda is given it should take a string value as its argument and should validate based on that string. If it successfully validates `#t` or any non-string value should be returned, otherwise a string explaining the error should be returned", + "widget-make-password": "(widget-make-password [placeholder-text: string] [[wrap-text: bool]] [[validation-callback: lambda]]) => gui-widget\n\nCreates an password widget with the given placeholder value. For accessibility purposes it is always advised to also have a label widget describing this widget. If a validation lambda is given it should take a string value as its argument and should validate based on that string. If it successfully validates `#t` or any non-string value should be returned, otherwise a string explaining the error should be returned", + "widget-make-multiline": "(widget-make-multiline [placeholder-text: string] [[wrap-text: bool]] [[validation-callback: lambda]]) => gui-widget\n\nCreates a multiline text entry widget with the given placeholder value. For accessibility purposes it is always advised to also have a label widget describing this widget. If a validation lambda is given it should take a string value as its argument and should validate based on that string. If it successfully validates `#t` or any non-string value should be returned, otherwise a string explaining the error should be returned", + "widget-make-label": "(widget-make-label [text: string] [[alignment: number]] [[wrap-text: bool]]) => gui-widget\n\nCreates a label widget with the given text. If alignment is given it should be one of:\n\n\t-1: Align Leading\n\t 0: Align Center\n\t 1: Align Trailing", + "widget-make-hyperlink": "(widget-make-hyperlink [text: string] [url: string]) => gui-widget\n\nCreates a hyperlink widget with the given text linking to the given URL. If the URL cannot be parsed as a valid URL an exception will be returned", + "widget-make-button": "(widget-make-button [text: string] [callback: lambda] [[alignment: number]]) => gui-widget\n\nCreates a button widget with the given text. The callback should accept no arguments and should only create side effects (the return value will be discarded). If alignment is given it should be one of:\n\n\t-1: Align Leading\n\t 0: Align Center\n\t 1: Align Trailing", + "widget-get-text": "(widget-get-text [gui-widget]) => string\n\nRetrieves the text of the given widget", + "widget-set-text": "(widget-set-text [gui-widget] [string]) => string\n\nSets the text of the given widget to the given string and returns the string", + "widget-make-separator": "(widget-make-separator) => gui-widget\n\nReturns a separator widget, which takes the form of a separator line that will go horizontal or vertical automatically depending on the container/layout", + "widget-make-spacer": "(widget-make-spacer) => gui-widget\n\nReturns a spacer widget which will fill up the available space in the container, pushing other widgets away from it in a direction decided based on the container/layou", + "container": "(container [layout: string] [contents: gui-widget|container...])\n\n`container` is the basic layout unit of the slope gui system. It can hold any number of other widgets or containers. The available layout strings are:\n\n\tnone - No layout\n\n\thbox - arranges items in a horizontal row. Every element will have the same height (the height of the tallest item in the container) and objects will be left-aligned at their minimum width\n\n\tvbox - arranges items in a vertical column. Every element will have the same width (the width of the widest item in the container) and objects will be top-aligned at their minimum height\n\n\tform - arranges items in pairs where the first column is at minimum width. This is normally useful for labelling elements in a form, where the label is in the first column and the item it describes is in the second. You should always add an even number of elements to a form layout\n\n\tcenter - positions all container elements in the center of the container. Every object will be set to it’s minimum size\n\n\tmax - positions all container elements to fill the available space. The objects will all be full-sized and drawn in the order they were added to the container (last-most is on top)\n\n\tpadded - positions all container elements to fill the available space but with a small padding around the outside. The size of the padding is theme specific. The objects will all be drawn in the order they were added to the container (last-most is on top)", + "container-size": "(container-size [gui-container]) => list\n\nReturns a two item list containing the given gui-container's width and height (in that order)", + "container-add-to": "(container-add-to [parent: gui-container] [child: gui-widget|gui-container]) => #t\n\nAdds the given child widget/container to the given parent container", + "dialog-open-file": "", + "dialog-save-file": "", + "dialog-info": "", + "dialog-error": "", +} diff --git a/helpers.go b/helpers.go index d5a4d7d..1159880 100644 --- a/helpers.go +++ b/helpers.go @@ -580,4 +580,7 @@ func addGUIToLib() { for k, v := range guiLib { stdLibrary[k] = v } + for k, v := range guiUsageStrings { + usageStrings[k] = v + } } diff --git a/interpreter.go b/interpreter.go index 612ec8a..da7a1c2 100644 --- a/interpreter.go +++ b/interpreter.go @@ -261,15 +261,6 @@ func eval(exp expression, en *env) (value expression) { } } value = val - case "callback": - if len(e) <= 1 { - return exception("'callback' expects a value, no value was given") - } - p, ok := eval(e[1], en).(proc) - if !ok { - return exception("'callback' expects a procedure as its first argument, a non-procedure value was given") - } - value = Callback{en, p} case "apply": if len(e) < 3 { value = exception("'apply' expects two arguments: a procedure and an argument list, too few arguments were given") diff --git a/main.go b/main.go index 7089a90..5d939d8 100644 --- a/main.go +++ b/main.go @@ -7,7 +7,6 @@ import ( "os" "os/signal" "path/filepath" - "strconv" "strings" "syscall" @@ -37,58 +36,6 @@ func RecoverError() { } } -func String(v expression, rawString bool) string { - switch v := v.(type) { - case []expression: - l := make([]string, len(v)) - for i, x := range v { - l[i] = String(x, true) - } - return "(" + strings.Join(l, " ") + ")" - case string: - if rawString { - return fmt.Sprintf("\"%s\"", escapeString(v)) - } - return v - case exception: - return string(v) - case bool: - if v { - return "#t" - } - return "#f" - case number: - return strconv.FormatFloat(float64(v), 'f', -1, 64) - case proc: - var b strings.Builder - b.WriteString("(lambda ") - b.WriteString(String(v.params, true)) - b.WriteRune(' ') - body := String(v.body, true) - if strings.HasPrefix(body, "(begin ") { - body = body[7:] - } - b.WriteString(body) - return b.String() - case func(...expression) expression: - return fmt.Sprint("Built-in: ", &v) - case *IOHandle: - return fmt.Sprintf("%+v", v) - // case guiWidget: - // return "Widget" - // case fyne.Widget: - // return "Widget" - // case fyne.CanvasObject: - // return "Widget" - // case fyne.Container: - // return "Container" - // case guiContainer: - // return "Container" - default: - return fmt.Sprint(v) - } -} - func Init() { openFiles = make([]*IOHandle, 0, 5) addGUIToLib() // Will combine guiLib and stdLib for use in creation of global env @@ -279,7 +226,7 @@ func PrintVersion() { └─┐│ │ │├─┘├┤ └─┘┴─┘└─┘┴ └─┘` if VersionHash != "" { - fmt.Printf("\033[1m%s\033[0m\nBuild Version Hash/Commit: %s\n(c) 2021 sloum, see license with: (license)\n", logo, VersionHash) + fmt.Printf("\033[1m%s\033[0m\nBuild Version Hash/Commit: %s %s\n(c) 2021 sloum, see license with: (license)\n", logo, VersionHash, guiIsOn) } else { fmt.Printf("\033[1m%s\033[0m\nBuild Version Hash/Commit: Custom Build (not hash based)\n(c) 2021 sloum, see license with: (license)\n", logo) } diff --git a/noGuiStatic.go b/noGuiStatic.go new file mode 100644 index 0000000..d31d207 --- /dev/null +++ b/noGuiStatic.go @@ -0,0 +1,7 @@ +//+build nogui + +package main + +const guiIsOn = "(no gui)" + +var guiUsageStrings = map[string]string{} diff --git a/nogui.go b/nogui.go index a2c02de..f29ace0 100644 --- a/nogui.go +++ b/nogui.go @@ -2,4 +2,52 @@ package main +import ( + "fmt" + "strconv" + "strings" +) + var guiLib = vars{} + +func String(v expression, rawString bool) string { + switch v := v.(type) { + case []expression: + l := make([]string, len(v)) + for i, x := range v { + l[i] = String(x, true) + } + return "(" + strings.Join(l, " ") + ")" + case string: + if rawString { + return fmt.Sprintf("\"%s\"", escapeString(v)) + } + return v + case exception: + return string(v) + case bool: + if v { + return "#t" + } + return "#f" + case number: + return strconv.FormatFloat(float64(v), 'f', -1, 64) + case proc: + var b strings.Builder + b.WriteString("(lambda ") + b.WriteString(String(v.params, true)) + b.WriteRune(' ') + body := String(v.body, true) + if strings.HasPrefix(body, "(begin ") { + body = body[7:] + } + b.WriteString(body) + return b.String() + case func(...expression) expression: + return fmt.Sprint("Built-in: ", &v) + case *IOHandle: + return fmt.Sprintf("%+v", v) + default: + return fmt.Sprint(v) + } +} diff --git a/parser.go b/parser.go index d1079f6..c2ff24e 100644 --- a/parser.go +++ b/parser.go @@ -14,11 +14,6 @@ type IOHandle struct { Kind string } -type Callback struct { - e *env - p proc -} - func (i IOHandle) String() string { state := "CLOSED" if i.Open {