Better error handling, early return from procs working better - though needs more testing

This commit is contained in:
sloum 2023-06-01 22:42:09 -07:00
parent 547bb46295
commit c96e2fc0d9
14 changed files with 501 additions and 34 deletions

257
LICENSE Normal file
View File

@ -0,0 +1,257 @@
Floodgap Free Software License
The author of your software has chosen to distribute it under the
Floodgap Free Software License. Although this software is without cost,
it is not released under Copyleft or GPL, and there are differences
which you should read. Your use of this software package constitutes
your binding acceptance without restriction.
This software is without cost
The Floodgap Free Software License (FFSL) has one overriding mandate:
that software using it, or derivative works based on software that uses
it, must be free. By free we mean simply "free as in beer" -- you may
put your work into open or closed source packages as you see fit,
whether or not you choose to release your changes or updates publicly,
but you must not ask any fee for it. (There are certain exceptions for
for-profit use which we will discuss below.)
Definitions and terms
Author
The declared copyright owner of this software package.
Binary
A pre-compiled or pre-interpreted bytecode or machine language
representation of a software package not designed for further
modification and tied to a particular platform or architecture.
Derivative work
Any distribution (q.v.) that contains any modification to or
deviation from the official reference distribution (q.v.); or
any software package significantly based on or integrally
including the source code for its features, including but not
limited to supersets; subsets of a significant proportion;
in-place patched changes to source or binary files; linking in
as a library; binary-only distributions if the original package
included source (even if the source was not modified prior to
compilation); or translations to another programming language,
architecture or operating system environment. Derivative works
of packages released under this license are also considered
subject to this license.
However, a software package that requires this package but does
not include it or is not based upon it, even if it will not
operate without it, is not considered a derivative work. For
example, interpreted programs requiring an interpreter issued
under this license, assuming they are not distributed with any
portion of the interpreter, are not derivative works.
Distribution
A packaged release of this software, either the author's
original work (the "reference distribution") or a derivative
work based upon it.
Reference distribution
A packaged release of this software explicitly designated as the
official release, written by or on behalf of the Author with his
or her explicit designation as official. Only exact copies of
the reference distribution may be called reference
distributions; all other forms are derivative works.
Source code
The human-readable programming instructions of the package which
might be easily read as text and subsequently edited, but
requiring compilation or interpretation into binary before being
directly useable.
What you are permitted to do under this license
Pursuant to the remainder of the terms below,
* You may freely use, copy, and disseminate this software package for
any non-commercial purpose as well as the commercial purposes
permitted below.
* You may freely modify this package, including source code if
available. Your modifications need not be released, although you
are encouraged to do so.
* You may release your derivative works based upon this software in
purely binary (non-source) form if you choose. You are not
obligated to release any portion of your source code openly,
although you are encouraged to do so.
* If this package is a tool used for generation, compilation or
maintenance of works, including but not limited to readable
documents, software packages or images (for example, compilers,
interpreters, translators, linkers, editors, assemblers or
typesetters), you may freely use it for that purpose, commercial or
otherwise, as the works made by this package are not considered
subject to this license unless specified otherwise within and may
be distributed under any desired license and/or offered for sale or
rental. Any run-time library or run-time code section linked into
the output by a compiler or similar code-generating tool governed
by this license is considered to be an integral part of the output,
and its presence does not subject the generated work to this
license either. (This is, of course, assuming you are not using
said tools to generate a derivative work based on this package in
violation of the other license terms.)
However, if you are linking or including a separately distributed
library that is under this license, no matter what tool you are
using to do the linking or inclusion, you are then considered to be
making a derivative work based on that library and your work does
fall under this license. To avoid this, do not include the library
with your work (even though it needs the library to function) and
instead offer the library separately without cost.
* In addition to non-commercial use and the uses permitted above, you
may use this software package in any for-profit endeavour as long
as it does not involve the specific sale or rental of this package.
Some specific but by no means exhaustive examples are listed below.
Note that some of these situations may require additional action be
taken to ensure compliance.
+ If this package or a derivative work allows you to serve data
or make data available to others (for example, web servers,
mail servers, gopher servers, etc.), you may use it to serve
any commercial content or in any commercial setting whether
you choose to charge a fee or not, as you are considered to be
earning income from the content you serve and/or the services
facilitated by your business and not from the sale of this
package itself. (This is, of course, assuming that you are not
charging a fee for sale or rental of this package or a
derivative work based on this package in violation of the
other license terms.) Similarly, any data you may acquire from
the use of this package is yours, and not governed by this
license in any way even if for-profit.
+ If you are selling a product that includes this package or a
derivative work either as part of your product's requirements
for function or as a bundled extra, such as an operating
system distribution, you may charge a fee for your product as
long as you also make this package or said derivative work
available for free separately (such as by download or link
back to this package's site), as you are considered to be
requesting a fee for your own product and the package is
merely included as a convenience to your users.
+ If you offer installation of this package or a derivative work
as a service, you may charge a fee for the act of installation
as long as you also make this package or said derivative work
available for free (such as by download or link back to this
package's site), as you are considered to be requesting a fee
for the act of installation and not for the software you are
installing.
+ The Author may also grant, in writing, other specified
exemptions for your particular commercial purpose that do not
contravene the spirit of this license or any license terms
this package additionally carries.
* In your derivative works based on this package, you may choose to
offer warranty support or guarantees of performance. This does not
in any way make the original Author legally, financially or in any
other respect liable for claims issued under your warranty or
guarantee, and you are solely responsible for the fulfillment of
your terms even if the Author of the work you have based your work
upon offers his or her own.
* In your derivative works based on this package, you may further
restrict the acceptable uses of your package or situations in which
it may be employed as long as you clearly state that your terms
apply only to your derivative work and not to the original
reference distribution. However, you may not countermand or ignore,
directly or otherwise, any restriction already made in the
reference distribution's license, including in this document
itself, in similar fashion to other licenses allowing compatible
licenses to co-govern a particular package's use.
What you must not do under this license
Remember that these limits apply only to redistribution of a reference
distribution, or to a true derivative work. If your project does not
include this package or code based upon it, even if it requires this
package to function, it is not considered subject to this license or
these restrictions.
* You must not charge a fee for purchase or rental of this package or
any derivative work based on this package. It is still possible to
use this package in a commercial environment, however -- see What
you are permitted to do under this license.
* You must not countermand or ignore, directly or otherwise, the
restrictions already extant in this package's license in your
derivative work based on it. As a corollary, you must not place
your derivative work under a secondary license or description of
terms that conflicts with it (for example, this license is not
compatible with the GNU Public License).
* You must not label any modified distribution of this package as a
reference or otherwise official distribution without the permission
of the original Author or Authors. You must clearly specify that
your modified work is a derivative work, including binary-only
releases if the original included source code and you do not even
if you did not modify the source prior to compilation.
What you must do under this license
* You must agree to all terms specified (agreement to which is
unconditionally signified by your usage, modification or
repurposing of this package), or to remove the package from your
computer and not use it further.
* In the absence of any specific offer for redress or assistance
under warranty or guarantee of performance that the Author of this
package might make, you must agree to accept any and all liability
that may come from the use of this package, proper or improper,
real or imagined, and certify without condition that you use this
product at your own risk with no guarantee of function,
merchantability or fitness for a particular purpose. If such offer
of redress or assistance is extended, it is fulfillable only by the
Author who extended the offer, which might not necessarily be this
Author, nor might it be the Authors of any packages it might be
based upon.
* If you choose to publicly redistribute this package or create a
derivative work based on this package, you must make it available
without any purchase or rental fee of any kind.
* If you choose to create a derivative work based on this package,
your derivative work must be copyrighted, and must be governed
under (at a minimum) the original package's license, which will
necessarily include all terms noted here. As such, if you choose to
distribute your derivative work, you must include a human-readable
license in your distribution containing all restrictions of use,
necessarily including this license, and any additional restrictions
the Author has mandated that do not contravene this license which
you and users of your derivative work must also honour.
* If you choose to create and distribute a derivative work based on
this package, your derivative work must clearly make reference to
this package, any other packages your work or the original work
might be based on, and all applicable copyrights, either in your
documentation, your work's standard human-readable output, or both.
A suggested method might be
Contains or is based on the Foo software package.
Copyright (C) 2112 D. Original Author. All rights reserved.
http://their.web.site.invalid/
Additional notes
Enforcement is the responsibility of the Author. However, violation of
this license may subject you to criminal and civil penalties depending
on your country.
This package is bound by the version of license that accompanies it.
Future official versions of a particular package may use a more updated
license, and you should always review the license before use. This
license's most current version is always available from the following
locations:
[1]http://www.floodgap.com/software/ffsl/
[2]gopher://gopher.floodgap.com/1/ffsl/
This license is version 1, dated 19 November 2006.
This license is copyright © 2006 Cameron Kaiser. All rights reserved.
The text of this license is available for re-use and re-distribution
under the Creative Commons. The use of the term "Floodgap Free Software
License" does not imply endorsement of packages using this license by
Floodgap Systems or by Cameron Kaiser. Modified licenses using portions
of these terms may refer to themselves as modified FFSL, with the
proviso that their modifications be clearly marked in accordance with
the Creative Commons Attribution-ShareAlike 2.5 License.
Only the text of this license, and not programs covered by this
license, is so offered under Creative Commons.
References
1. http://www.floodgap.com/software/ffsl/
2. gopher://gopher.floodgap.com/1/ffsl/

View File

@ -2,5 +2,45 @@
_felise_ is a [concatenative](https://en.wikipedia.org/wiki/Concatenative_programming_language) programming language. It is a hobby language designed as a successor to [nimf](https://git.rawtext.club/nimf-lang/nimf) (an earlier programming language by the same author). The language is implemented as a basic AST walking interpreter interpreter (though a compiler, JIT, or bytecode/vm would also be possible) in [golang](https://golang.org). It is not dynamically typed and all variables need to be declared as a given type (even control structures like `if` and `while` are technically types as well). Type checks are generally handled at runtime, with a few handling in the lex/parse phases.
More details coming when I have time to finish writing this.
## Example
``` forth
proc fib
| calculates the nth fibonacci number
where n is an INT on TOS at call time |
INT var fib#
set fib#
fib# 0 = if 1 .
fib# 1 = if 3 .
fib# 1 > if
INT var grandparent 1 set grandparent
INT var parent 3 set parent
INT var i 2 set i
INT var me
proc continue-loop?
i fib# < i
.
<=fib#
while
3 parent *
grandparent -
set me
parent set grandparent
me set parent
i 1 + set i
<=fib#
.
me
.
.
5 fib println
```

12
env.go
View File

@ -1,3 +1,15 @@
/*
Copyright (C) 2023 Brian Evans (aka sloum). All rights reserved.
This source code is available under the terms of the ffsl, or,
Floodgap Free Software License. A copy of the license has been
provided as the file 'LICENSE' in the same folder as this source
code file. If for some reason it is not present, you can find the
terms of version 1 of the FFSL at the following URL:
https://www.floodgap.com/software/ffsl/license.html
*/
package main
import (

BIN
felise Executable file

Binary file not shown.

View File

@ -1,3 +1,15 @@
/*
Copyright (C) 2023 Brian Evans (aka sloum). All rights reserved.
This source code is available under the terms of the ffsl, or,
Floodgap Free Software License. A copy of the license has been
provided as the file 'LICENSE' in the same folder as this source
code file. If for some reason it is not present, you can find the
terms of version 1 of the FFSL at the following URL:
https://www.floodgap.com/software/ffsl/license.html
*/
package main
import (

View File

@ -1,3 +1,15 @@
/*
Copyright (C) 2023 Brian Evans (aka sloum). All rights reserved.
This source code is available under the terms of the ffsl, or,
Floodgap Free Software License. A copy of the license has been
provided as the file 'LICENSE' in the same folder as this source
code file. If for some reason it is not present, you can find the
terms of version 1 of the FFSL at the following URL:
https://www.floodgap.com/software/ffsl/license.html
*/
package main
const (
@ -24,4 +36,8 @@ const (
var (
debug bool = false
// This is the main stack for the language
globalStack stack = NewStack()
globalRetStack stack = NewStack()
)

View File

@ -1,3 +1,15 @@
/*
Copyright (C) 2023 Brian Evans (aka sloum). All rights reserved.
This source code is available under the terms of the ffsl, or,
Floodgap Free Software License. A copy of the license has been
provided as the file 'LICENSE' in the same folder as this source
code file. If for some reason it is not present, you can find the
terms of version 1 of the FFSL at the following URL:
https://www.floodgap.com/software/ffsl/license.html
*/
package main
import (
@ -111,10 +123,10 @@ func isKeyword(s string) bool {
<- add value from array to TOS
*/
switch s {
case "var", "set", "+", "-", "*", "/",
case "var", "set", "+", "-", "*", "/", "proc",
"length", "ref", "return", "print", "println", "dup",
"drop", "over", "swap", "cast", "type", "while", "dowhile",
"if", "else", "&&", "||", ">", "<", "=", "stackdump",
"if", "else", "and", "or", ">", "<", "=", "stackdump",
"clearstack":
return true
default:

View File

@ -1,3 +1,15 @@
/*
Copyright (C) 2023 Brian Evans (aka sloum). All rights reserved.
This source code is available under the terms of the ffsl, or,
Floodgap Free Software License. A copy of the license has been
provided as the file 'LICENSE' in the same folder as this source
code file. If for some reason it is not present, you can find the
terms of version 1 of the FFSL at the following URL:
https://www.floodgap.com/software/ffsl/license.html
*/
package main
import (
@ -5,12 +17,13 @@ import (
"fmt"
)
// This is the main stack for the language
var globalStack stack = NewStack()
func interpret(tokens []token, en *env) error {
r := NewTokenReader(tokens)
for t, err := r.Read(); err == nil; t, err = r.Read() {
if globalRetStack.Depth() > 0 {
return nil
}
ReplacedSymbol:
switch t.kind {
case FLOAT, INT, STRING, BOOL, TYPE:
@ -83,13 +96,18 @@ ReplacedSymbol:
return fmt.Errorf("ERROR │ Line %d\n │ Could not find symbol `%s`\n", t.line, t.val.(string))
}
t = foundIn.vars[t.val.(string)]
// Run a procedure
if t.kind == PROC {
err = interpret(t.val.(proc).body, NewEnv(en))
if err != nil {
// This join is meant to create a callstack trace of sorts
// so that if an error was found multiple procs deep each
// _should_ adds it proc name and line to the list.
return errors.Join(err, fmt.Errorf(" ├ In procedure `%s` on line %d", t.val.(proc).name, t.line))
return errors.Join(err, fmt.Errorf(" - In procedure `%s` on line %d", t.val.(proc).name, t.line))
}
if globalRetStack.Depth() > 0 {
globalRetStack.Pop()
}
} else {
goto ReplacedSymbol
@ -97,6 +115,10 @@ ReplacedSymbol:
case KEYWORD:
// Handle early exit from a proc
if (t.val.(string) == "return" && en.parent != nil) || t.val.(string) == "break" {
err := globalRetStack.Push(t)
if err != nil {
return fmt.Errorf(stackErrFmt, line, err.Error())
}
return nil
}
err := callKeyword(t, r, en)

View File

@ -1,3 +1,15 @@
/*
Copyright (C) 2023 Brian Evans (aka sloum). All rights reserved.
This source code is available under the terms of the ffsl, or,
Floodgap Free Software License. A copy of the license has been
provided as the file 'LICENSE' in the same folder as this source
code file. If for some reason it is not present, you can find the
terms of version 1 of the FFSL at the following URL:
https://www.floodgap.com/software/ffsl/license.html
*/
package main
import (
@ -7,6 +19,13 @@ import (
"strings"
)
/*
TODO / Notes on broken things
- Add `not`
*/
func callKeyword(kw token, r *tokenReader, en *env) error {
switch kw.val.(string) {
case "+":
@ -22,22 +41,22 @@ func callKeyword(kw token, r *tokenReader, en *env) error {
case "set":
return libSet(kw.line, r, en)
case "print":
return libPrint(false)
return libPrint(false, kw.line)
case "println":
return libPrint(true)
return libPrint(true, kw.line)
case "dup":
return libDuplicate()
return libDuplicate(kw.line)
case "swap":
return libSwap()
return libSwap(kw.line)
case "over":
return libOver()
return libOver(kw.line)
case "drop":
return libDrop(kw.line)
case "cast":
return libConvertType(kw.line)
case "&&":
case "and":
return libLogicalAnd(kw.line)
case "||":
case "or":
return libLogicalOr(kw.line)
case "=":
return libEqual(kw.line)
@ -193,7 +212,7 @@ func libSet(line int, r *tokenReader, en *env) error {
return nil
}
func libPrint(newline bool) error {
func libPrint(newline bool, line int) error {
v1, err := globalStack.Pop()
if err != nil {
return fmt.Errorf(stackErrFmt, line, err.Error())
@ -206,12 +225,15 @@ func libPrint(newline bool) error {
return nil
}
func libDuplicate() error {
globalStack.Push(globalStack.Peek())
func libDuplicate(line int) error {
err := globalStack.Push(globalStack.Peek())
if err != nil {
return fmt.Errorf(stackErrFmt, line, err.Error())
}
return nil
}
func libSwap() error {
func libSwap(line int) error {
v2, err := globalStack.Pop()
if err != nil {
return fmt.Errorf(stackErrFmt, line, err.Error())
@ -225,7 +247,7 @@ func libSwap() error {
return nil
}
func libOver() error {
func libOver(line int) error {
v2, err := globalStack.Pop()
if err != nil {
return fmt.Errorf(stackErrFmt, line, err.Error())
@ -311,21 +333,24 @@ func libGreaterThan(line int) error {
if v1.kind == v2.kind {
switch v1.kind {
case STRING:
globalStack.Push(token{BOOL, v1.val.(string) > v1.val.(string), line})
err = globalStack.Push(token{BOOL, v1.val.(string) > v2.val.(string), line})
case INT:
globalStack.Push(token{BOOL, v1.val.(int) > v1.val.(int), line})
err = globalStack.Push(token{BOOL, v1.val.(int) > v2.val.(int), line})
case FLOAT:
globalStack.Push(token{BOOL, v1.val.(float64) > v1.val.(float64), line})
err = globalStack.Push(token{BOOL, v1.val.(float64) > v2.val.(float64), line})
default:
return fmt.Errorf("ERROR | Line %d\n %s is not a comparable type\n", line, kindToString(v1.kind))
}
} else if (v1.kind == INT && v2.kind == FLOAT) {
globalStack.Push(token{BOOL, float64(v1.val.(int)) > v1.val.(float64), line})
err = globalStack.Push(token{BOOL, float64(v1.val.(int)) > v2.val.(float64), line})
} else if (v1.kind == FLOAT && v2.kind == INT) {
globalStack.Push(token{BOOL, v1.val.(float64) > float64(v1.val.(int)), line})
err = globalStack.Push(token{BOOL, v1.val.(float64) > float64(v2.val.(int)), line})
} else {
return fmt.Errorf("ERROR | Line %d\n %s and %s cannot be compared\n", line, kindToString(v1.kind), kindToString(v2.kind))
}
if err != nil {
return fmt.Errorf(stackErrFmt, line, err.Error())
}
return nil
}
@ -341,21 +366,24 @@ func libLessThan(line int) error {
if v1.kind == v2.kind {
switch v1.kind {
case STRING:
globalStack.Push(token{BOOL, v1.val.(string) < v1.val.(string), line})
err = globalStack.Push(token{BOOL, v1.val.(string) < v2.val.(string), line})
case INT:
globalStack.Push(token{BOOL, v1.val.(int) < v1.val.(int), line})
err = globalStack.Push(token{BOOL, v1.val.(int) < v2.val.(int), line})
case FLOAT:
globalStack.Push(token{BOOL, v1.val.(float64) < v1.val.(float64), line})
err = globalStack.Push(token{BOOL, v1.val.(float64) < v2.val.(float64), line})
default:
return fmt.Errorf("ERROR | Line %d\n %s is not a comparable type\n", line, kindToString(v1.kind))
}
} else if (v1.kind == INT && v2.kind == FLOAT) {
globalStack.Push(token{BOOL, float64(v1.val.(int)) < v1.val.(float64), line})
err = globalStack.Push(token{BOOL, float64(v1.val.(int)) < v2.val.(float64), line})
} else if (v1.kind == FLOAT && v2.kind == INT) {
globalStack.Push(token{BOOL, v1.val.(float64) < float64(v1.val.(int)), line})
err = globalStack.Push(token{BOOL, v1.val.(float64) < float64(v2.val.(int)), line})
} else {
return fmt.Errorf("ERROR | Line %d\n %s and %s cannot be compared\n", line, kindToString(v1.kind), kindToString(v2.kind))
}
if err != nil {
return fmt.Errorf(stackErrFmt, line, err.Error())
}
return nil
}

View File

@ -1,3 +1,15 @@
/*
Copyright (C) 2023 Brian Evans (aka sloum). All rights reserved.
This source code is available under the terms of the ffsl, or,
Floodgap Free Software License. A copy of the license has been
provided as the file 'LICENSE' in the same folder as this source
code file. If for some reason it is not present, you can find the
terms of version 1 of the FFSL at the following URL:
https://www.floodgap.com/software/ffsl/license.html
*/
package main
import (

32
main.go
View File

@ -1,3 +1,15 @@
/*
Copyright (C) 2023 Brian Evans (aka sloum). All rights reserved.
This source code is available under the terms of the ffsl, or,
Floodgap Free Software License. A copy of the license has been
provided as the file 'LICENSE' in the same folder as this source
code file. If for some reason it is not present, you can find the
terms of version 1 of the FFSL at the following URL:
https://www.floodgap.com/software/ffsl/license.html
*/
package main
import (
@ -28,12 +40,13 @@ var linerTerm ln.ModeApplier = nil
_
|, _ |. __ _
| (/,||_) (/,
TODO / Ideas / Etc.
Ideas:
del [varname/procname]
Will remove the symbol and value from the symbol table
Will remove the symbol and value from the symbol table,
not sure if this is useful but it feels like it adds
completeness
// XXX note sure about these array ops
-> [varname]
@ -47,6 +60,7 @@ length [varname]
Will put the length of the var on TOS if is it a string
or an array
// Not yet sure how to do this
ref [ident/symbol]
Put the symbol on TOS, to reference something that would
otherwise be executed immediately (if, while, proc, etc)
@ -76,7 +90,7 @@ Syntactic sugar:
Builtins on radar:
- `+`, `-`, `*`, `/`, `%`
- `%`, `not`
- `open`
- Opens a connection or file and returns a handle
- `close`
@ -87,6 +101,12 @@ Builtins on radar:
- Read from the given handle to the stack
- `append`
- Appends TOS to a string, file, array, or open connection
Need some way to respond to args given at runtime... may need
an array for that. Or maybe not? Maybe an `arg` word can be
defined so that: `0 INT arg myarg` would let someone pass an
arg: `felise myfile.fe --myarg=24`, with the arg defaulting to
0... does something like that work?
*/
func prompt(l *ln.State, cont bool) string {
@ -161,7 +181,7 @@ func lexParseInterpret(s string, en *env) {
}
}
// TODO add file input
func main() {
verFlag := flag.Bool("v", false, "Print version information and exit")
flag.BoolVar(&debug, "debug", false, "Turns on debug mode")

View File

@ -1,3 +1,15 @@
/*
Copyright (C) 2023 Brian Evans (aka sloum). All rights reserved.
This source code is available under the terms of the ffsl, or,
Floodgap Free Software License. A copy of the license has been
provided as the file 'LICENSE' in the same folder as this source
code file. If for some reason it is not present, you can find the
terms of version 1 of the FFSL at the following URL:
https://www.floodgap.com/software/ffsl/license.html
*/
package main
import (

View File

@ -1,3 +1,15 @@
/*
Copyright (C) 2023 Brian Evans (aka sloum). All rights reserved.
This source code is available under the terms of the ffsl, or,
Floodgap Free Software License. A copy of the license has been
provided as the file 'LICENSE' in the same folder as this source
code file. If for some reason it is not present, you can find the
terms of version 1 of the FFSL at the following URL:
https://www.floodgap.com/software/ffsl/license.html
*/
package main
import (

View File

@ -1,3 +1,15 @@
/*
Copyright (C) 2023 Brian Evans (aka sloum). All rights reserved.
This source code is available under the terms of the ffsl, or,
Floodgap Free Software License. A copy of the license has been
provided as the file 'LICENSE' in the same folder as this source
code file. If for some reason it is not present, you can find the
terms of version 1 of the FFSL at the following URL:
https://www.floodgap.com/software/ffsl/license.html
*/
package main
import (