Lots of progress.
This commit is contained in:
parent
a239c6f632
commit
1202139b89
14
globals.go
14
globals.go
|
@ -1,29 +1,25 @@
|
|||
package main
|
||||
|
||||
const (
|
||||
stackErrFmt string = "ERROR | Line %d\n %s\n"
|
||||
STACK_SIZE int = 256
|
||||
|
||||
// Begin type enums
|
||||
INT int = iota
|
||||
INT_a
|
||||
FLOAT
|
||||
FLOAT_a
|
||||
STRING
|
||||
STRING_a
|
||||
BOOL
|
||||
BOOL_a
|
||||
// CHAR
|
||||
// BYTE
|
||||
SYMBOL
|
||||
KEYWORD
|
||||
TYPE
|
||||
IDENT
|
||||
|
||||
PROC
|
||||
PROC_a
|
||||
WHILE
|
||||
IF
|
||||
|
||||
END
|
||||
DOCSTRING
|
||||
MAXTYPE
|
||||
MAXTYPE // Just here to get a number range
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
9
go.mod
9
go.mod
|
@ -1,3 +1,10 @@
|
|||
module git.rawtext.club/sloum/nimf2
|
||||
module git.rawtext.club/sloum/felise
|
||||
|
||||
go 1.20
|
||||
|
||||
require github.com/peterh/liner v1.2.2
|
||||
|
||||
require (
|
||||
github.com/mattn/go-runewidth v0.0.3 // indirect
|
||||
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 // indirect
|
||||
)
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=
|
||||
github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI=
|
||||
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 h1:kwrAHlwJ0DUBZwQ238v+Uod/3eZ8B2K5rYsUHBQvzmI=
|
||||
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
112
helpers.go
112
helpers.go
|
@ -10,39 +10,29 @@ import (
|
|||
func kindToString(k int) string {
|
||||
switch k {
|
||||
case INT:
|
||||
return "int"
|
||||
case INT_a:
|
||||
return "[]int"
|
||||
return "INT"
|
||||
case FLOAT:
|
||||
return "float"
|
||||
case FLOAT_a:
|
||||
return "[]float"
|
||||
return "FLOAT"
|
||||
case STRING:
|
||||
return "string"
|
||||
case STRING_a:
|
||||
return "[]string"
|
||||
return "STRING"
|
||||
case BOOL:
|
||||
return "bool"
|
||||
case BOOL_a:
|
||||
return "[]bool"
|
||||
// case CHAR:
|
||||
// return "char"
|
||||
// case BYTE:
|
||||
// return "byte"
|
||||
return "BOOL"
|
||||
case SYMBOL:
|
||||
return "symbol"
|
||||
return "SYMBOL"
|
||||
case KEYWORD:
|
||||
return "keyword"
|
||||
case IDENT:
|
||||
return "ident"
|
||||
return "KEYWORD"
|
||||
// case IDENT:
|
||||
// return "ident"
|
||||
case TYPE:
|
||||
return "type"
|
||||
return "TYPE"
|
||||
case PROC:
|
||||
return "proc"
|
||||
case PROC_a:
|
||||
return "[]proc"
|
||||
return "PROC"
|
||||
case WHILE:
|
||||
return "WHILE"
|
||||
case IF:
|
||||
return "IF"
|
||||
case DOCSTRING:
|
||||
return "docstring"
|
||||
return "DOCSTRING"
|
||||
case END:
|
||||
return "."
|
||||
default:
|
||||
|
@ -51,21 +41,46 @@ func kindToString(k int) string {
|
|||
}
|
||||
|
||||
// Returns the kind, initial value, and error
|
||||
func sigilToKind(v string) (int, value, error) {
|
||||
if len(v) < 2 {
|
||||
return -1, nil, fmt.Errorf("Invalid identifier (hint: sigil + at least one alpha-numeric char)")
|
||||
}
|
||||
switch v[0] {
|
||||
case '$':
|
||||
return STRING, "", nil
|
||||
case '+':
|
||||
return INT, 0, nil
|
||||
case '%':
|
||||
return FLOAT, 0.0, nil
|
||||
case '?':
|
||||
return BOOL, false, nil
|
||||
// func sigilToKind(v string) (int, value, error) {
|
||||
// if len(v) < 2 {
|
||||
// return -1, nil, fmt.Errorf("Invalid identifier (hint: sigil + at least one alpha-numeric char)")
|
||||
// }
|
||||
// switch v[0] {
|
||||
// case '$':
|
||||
// return STRING, "", nil
|
||||
// case '+':
|
||||
// return INT, 0, nil
|
||||
// case '%':
|
||||
// return FLOAT, 0.0, nil
|
||||
// case '?':
|
||||
// return BOOL, false, nil
|
||||
// default:
|
||||
// return 0, nil, fmt.Errorf("Unsupported sigil: %q", v[0])
|
||||
// }
|
||||
// }
|
||||
|
||||
func typeInit(kind int) value {
|
||||
switch kind {
|
||||
case INT:
|
||||
return 0
|
||||
case FLOAT:
|
||||
return 0.0
|
||||
case BOOL:
|
||||
return false
|
||||
case STRING:
|
||||
return ""
|
||||
case TYPE:
|
||||
return TYPE
|
||||
case WHILE:
|
||||
return while{}
|
||||
case IF:
|
||||
return condIf{}
|
||||
case PROC:
|
||||
return proc{}
|
||||
case DOCSTRING:
|
||||
return ""
|
||||
default:
|
||||
return 0, nil, fmt.Errorf("Unsupported sigil: %q", v[0])
|
||||
panic("Unknown type given to typeInit")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +113,9 @@ func isKeyword(s string) bool {
|
|||
switch s {
|
||||
case "var", "set", "+", "-", "*", "/",
|
||||
"length", "ref", "return", "print", "println", "dup",
|
||||
"drop", "over", "swap", "cast", "type":
|
||||
"drop", "over", "swap", "cast", "type", "while", "dowhile",
|
||||
"if", "else", "&&", "||", ">", "<", "=", "stackdump",
|
||||
"clearstack":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
@ -135,6 +152,21 @@ func toString(t token, codeRepresentation bool) string {
|
|||
}
|
||||
}
|
||||
|
||||
func toBoolRaw(t token) bool {
|
||||
switch t.kind {
|
||||
case BOOL:
|
||||
return t.val.(bool)
|
||||
case INT:
|
||||
return !(t.val.(int) == 0)
|
||||
case STRING:
|
||||
return !(t.val.(string) == "")
|
||||
case FLOAT:
|
||||
return !(t.val.(float64) == 0.0)
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func toBool(t token) token {
|
||||
switch t.kind {
|
||||
case BOOL:
|
||||
|
|
77
interpret.go
77
interpret.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
@ -13,29 +14,89 @@ func interpret(tokens []token, en *env) error {
|
|||
ReplacedSymbol:
|
||||
switch t.kind {
|
||||
case FLOAT, INT, STRING, BOOL, TYPE:
|
||||
globalStack.Push(t)
|
||||
err := globalStack.Push(t)
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, t.line, err.Error())
|
||||
}
|
||||
case PROC:
|
||||
en.Add(t.val.(proc).name, t)
|
||||
case IF:
|
||||
v, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, t.line, err.Error())
|
||||
}
|
||||
if toBool(v).val.(bool) {
|
||||
err = interpret(t.val.(condIf).truthy, en)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if t.val.(condIf).hasFalseBranch {
|
||||
err = interpret(t.val.(condIf).falsy, en)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case WHILE:
|
||||
if !t.val.(while).doWhile {
|
||||
v, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, t.line, err.Error())
|
||||
}
|
||||
cond := toBool(v).val.(bool)
|
||||
if cond {
|
||||
for {
|
||||
err = interpret(t.val.(while).body, NewEnv(en))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v, err = globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, t.line, err.Error())
|
||||
}
|
||||
if !toBool(v).val.(bool) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = interpret(t.val.(while).body, NewEnv(en))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
v, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, t.line, err.Error())
|
||||
}
|
||||
if !toBool(v).val.(bool) {
|
||||
break
|
||||
}
|
||||
err = interpret(t.val.(while).body, NewEnv(en))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
case SYMBOL:
|
||||
foundIn, err := en.Find(t.val.(string))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ERROR | Line %d\n | Could not find symbol `%s`\n", t.line, t.val.(string))
|
||||
return fmt.Errorf("ERROR │ Line %d\n │ Could not find symbol `%s`\n", t.line, t.val.(string))
|
||||
}
|
||||
t = foundIn.vars[t.val.(string)]
|
||||
if t.kind == PROC {
|
||||
err = interpret(t.val.(proc).body, NewEnv(en))
|
||||
if err != nil {
|
||||
// TODO there is opportunity here to create a
|
||||
// stack trace of sorts by combining the
|
||||
// error with the current proc name/line
|
||||
return err
|
||||
// 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))
|
||||
}
|
||||
} else {
|
||||
goto ReplacedSymbol
|
||||
}
|
||||
case KEYWORD:
|
||||
// Handle early exit from a proc
|
||||
if t.val.(string) == "return" && en.parent != nil {
|
||||
if (t.val.(string) == "return" && en.parent != nil) || t.val.(string) == "break" {
|
||||
return nil
|
||||
}
|
||||
err := callKeyword(t, r, en)
|
||||
|
@ -43,7 +104,7 @@ ReplacedSymbol:
|
|||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("ERROR | Line %d\n | Unknown type encountered: %s\n", t.line, kindToString(t.kind))
|
||||
return fmt.Errorf("ERROR │ Line %d\n │ Unknown type encountered: %s\n", t.line, kindToString(t.kind))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
267
keywords.go
267
keywords.go
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
@ -31,18 +32,38 @@ func callKeyword(kw token, r *tokenReader, en *env) error {
|
|||
case "over":
|
||||
return libOver()
|
||||
case "drop":
|
||||
return libDrop()
|
||||
return libDrop(kw.line)
|
||||
case "cast":
|
||||
return libConvertType(kw.line)
|
||||
case "&&":
|
||||
return libLogicalAnd(kw.line)
|
||||
case "||":
|
||||
return libLogicalOr(kw.line)
|
||||
case "=":
|
||||
return libEqual(kw.line)
|
||||
case ">":
|
||||
return libGreaterThan(kw.line)
|
||||
case "<":
|
||||
return libLessThan(kw.line)
|
||||
case "stackdump":
|
||||
return libStackDump()
|
||||
case "clearstack":
|
||||
return libClearStack()
|
||||
default:
|
||||
return fmt.Errorf("Unknown keyword: %s", kw.val.(string))
|
||||
return fmt.Errorf("ERROR | Line %d\n Unknown keyword: `%s`\n", kw.val.(string))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func libAdd(line int) error {
|
||||
v2 := globalStack.Pop()
|
||||
v1 := globalStack.Pop()
|
||||
v2, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if v1.kind == STRING && v2.kind == STRING {
|
||||
globalStack.Push(token{STRING, v1.val.(string) + v2.val.(string), line})
|
||||
} else if v1.kind == INT && v2.kind == INT {
|
||||
|
@ -51,15 +72,21 @@ func libAdd(line int) error {
|
|||
globalStack.Push(token{FLOAT, v1.val.(float64) + v2.val.(float64), line})
|
||||
} else {
|
||||
fmt.Println("STACK-DUMP: ", globalStack)
|
||||
return fmt.Errorf("ERROR | Line %d\n Cannot `-` %s and %s\n", line, kindToString(v1.kind), kindToString(v2.kind))
|
||||
return fmt.Errorf("ERROR | Line %d\n Cannot `-` %s and %s\n", line, kindToString(v1.kind), kindToString(v2.kind))
|
||||
}
|
||||
// TODO array operations, once arrays are added
|
||||
return nil
|
||||
}
|
||||
|
||||
func libSubtract(line int) error {
|
||||
v2 := globalStack.Pop()
|
||||
v1 := globalStack.Pop()
|
||||
v2, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if v1.kind == STRING && v2.kind == STRING {
|
||||
s := strings.Replace(v1.val.(string), v2.val.(string), "", -1)
|
||||
globalStack.Push(token{STRING, s, line})
|
||||
|
@ -68,15 +95,21 @@ func libSubtract(line int) error {
|
|||
} else if v1.kind == FLOAT && v2.kind == FLOAT {
|
||||
globalStack.Push(token{FLOAT, v1.val.(float64) - v2.val.(float64), line})
|
||||
} else {
|
||||
return fmt.Errorf("ERROR | Line %d\n Cannot `-` %s and %s\n", kindToString(v1.kind), kindToString(v2.kind))
|
||||
return fmt.Errorf("ERROR | Line %d\n Cannot `-` %s and %s\n", kindToString(v1.kind), kindToString(v2.kind))
|
||||
}
|
||||
// TODO array operations, once arrays are added
|
||||
return nil
|
||||
}
|
||||
|
||||
func libMultiply(line int) error {
|
||||
v2 := globalStack.Pop()
|
||||
v1 := globalStack.Pop()
|
||||
v2, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if v1.kind == STRING && v2.kind == INT {
|
||||
s := strings.Repeat(v1.val.(string), v2.val.(int))
|
||||
globalStack.Push(token{STRING, s, line})
|
||||
|
@ -85,62 +118,86 @@ func libMultiply(line int) error {
|
|||
} else if v1.kind == FLOAT && v2.kind == FLOAT {
|
||||
globalStack.Push(token{FLOAT, v1.val.(float64) * v2.val.(float64), line})
|
||||
} else {
|
||||
return fmt.Errorf("ERROR | Line %d\n Cannot `*` %s and %s\n", kindToString(v1.kind), kindToString(v2.kind))
|
||||
return fmt.Errorf("ERROR | Line %d\n Cannot `*` %s and %s\n", kindToString(v1.kind), kindToString(v2.kind))
|
||||
}
|
||||
// TODO array operations, once arrays are added
|
||||
return nil
|
||||
}
|
||||
|
||||
func libDivide(line int) error {
|
||||
v2 := globalStack.Pop()
|
||||
v1 := globalStack.Pop()
|
||||
v2, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if v1.kind == INT && v2.kind == INT {
|
||||
globalStack.Push(token{INT, v1.val.(int) / v2.val.(int), line})
|
||||
} else if v1.kind == FLOAT && v2.kind == FLOAT {
|
||||
globalStack.Push(token{FLOAT, v1.val.(float64) / v2.val.(float64), line})
|
||||
} else {
|
||||
return fmt.Errorf("ERROR | Line %d\n Cannot `/` %s and %s\n", kindToString(v1.kind), kindToString(v2.kind))
|
||||
return fmt.Errorf("ERROR | Line %d\n Cannot `/` %s and %s\n", kindToString(v1.kind), kindToString(v2.kind))
|
||||
}
|
||||
// TODO array operations, once arrays are added
|
||||
return nil
|
||||
}
|
||||
|
||||
func libVar(line int, r *tokenReader, en *env) error {
|
||||
typeToken, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if typeToken.kind != TYPE {
|
||||
return fmt.Errorf("ERROR | Line %d\n `var` expects a type on the top of the stack\n ", line)
|
||||
}
|
||||
newToken := token{}
|
||||
newToken.kind = typeToken.val.(int)
|
||||
newToken.line = line
|
||||
|
||||
t, err := r.Read()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ERROR | Line %d\n Unexpectedly reached EOF\n", line)
|
||||
return fmt.Errorf("ERROR | Line %d\n Unexpectedly reached EOF\n", line)
|
||||
}
|
||||
if t.kind != SYMBOL {
|
||||
return fmt.Errorf("ERROR | Line %d\n A non-symbol value was given as an identifier to var\n", line)
|
||||
return fmt.Errorf("ERROR | Line %d\n A non-symbol value was given as an identifier to var\n", line)
|
||||
}
|
||||
kind, val, err := sigilToKind(t.val.(string))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ERROR | Line %d\n %s\n", line, err.Error())
|
||||
}
|
||||
newToken := token{kind, val, line}
|
||||
newToken.val = typeInit(newToken.kind)
|
||||
en.Add(t.val.(string), newToken)
|
||||
fmt.Println(*en)
|
||||
return nil
|
||||
}
|
||||
|
||||
func libSet(line int, r *tokenReader, en *env) error {
|
||||
t, err := r.Read()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ERROR | Line %d\n Unexpectedly reached EOF\n", line)
|
||||
return fmt.Errorf("ERROR | Line %d\n Unexpectedly reached EOF\n", line)
|
||||
}
|
||||
if t.kind != SYMBOL {
|
||||
return fmt.Errorf("ERROR | Line %d\n A non-symbol value was given as an identifier to set\n", line)
|
||||
return fmt.Errorf("ERROR | Line %d\n A non-symbol value was given as an identifier to set\n", line)
|
||||
}
|
||||
e, err := en.Find(t.val.(string))
|
||||
if err != nil {
|
||||
return fmt.Errorf("ERROR | Line %d\n %s\n", t.line, err.Error())
|
||||
return fmt.Errorf("ERROR | Line %d\n %s\n", t.line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
current := e.vars[t.val.(string)]
|
||||
if v1.kind != current.kind {
|
||||
return fmt.Errorf("ERROR | Line %d\n Cannot assign %s to %s var\n", t.line, kindToString(v1.kind), kindToString(current.kind))
|
||||
}
|
||||
v1 := globalStack.Pop()
|
||||
e.vars[t.val.(string)] = v1
|
||||
return nil
|
||||
}
|
||||
|
||||
func libPrint(newline bool) error {
|
||||
v1 := globalStack.Pop()
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if newline {
|
||||
fmt.Println(toString(v1, false))
|
||||
} else {
|
||||
|
@ -155,33 +212,175 @@ func libDuplicate() error {
|
|||
}
|
||||
|
||||
func libSwap() error {
|
||||
v2 := globalStack.Pop()
|
||||
v1 := globalStack.Pop()
|
||||
v2, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
globalStack.Push(v2)
|
||||
globalStack.Push(v1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func libOver() error {
|
||||
v2 := globalStack.Pop()
|
||||
v1 := globalStack.Pop()
|
||||
v2, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
globalStack.Push(v1)
|
||||
globalStack.Push(v2)
|
||||
globalStack.Push(v1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func libDrop() error {
|
||||
globalStack.Pop()
|
||||
func libDrop(line int) error {
|
||||
_, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func libLogicalAnd(line int) error {
|
||||
v2, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if toBoolRaw(v1) && toBoolRaw(v2) {
|
||||
globalStack.Push(token{BOOL, true, line})
|
||||
} else {
|
||||
globalStack.Push(token{BOOL, false, line})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func libLogicalOr(line int) error {
|
||||
v2, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if toBoolRaw(v1) || toBoolRaw(v2) {
|
||||
globalStack.Push(token{BOOL, true, line})
|
||||
} else {
|
||||
globalStack.Push(token{BOOL, false, line})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func libEqual(line int) error {
|
||||
v2, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if v1.kind == v2.kind && v1.val == v2.val{
|
||||
globalStack.Push(token{BOOL, true, line})
|
||||
} else {
|
||||
globalStack.Push(token{BOOL, false, line})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func libGreaterThan(line int) error {
|
||||
v2, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if v1.kind == v2.kind {
|
||||
switch v1.kind {
|
||||
case STRING:
|
||||
globalStack.Push(token{BOOL, v1.val.(string) > v1.val.(string), line})
|
||||
case INT:
|
||||
globalStack.Push(token{BOOL, v1.val.(int) > v1.val.(int), line})
|
||||
case FLOAT:
|
||||
globalStack.Push(token{BOOL, v1.val.(float64) > v1.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})
|
||||
} else if (v1.kind == FLOAT && v2.kind == INT) {
|
||||
globalStack.Push(token{BOOL, v1.val.(float64) > float64(v1.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))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func libLessThan(line int) error {
|
||||
v2, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
v1, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if v1.kind == v2.kind {
|
||||
switch v1.kind {
|
||||
case STRING:
|
||||
globalStack.Push(token{BOOL, v1.val.(string) < v1.val.(string), line})
|
||||
case INT:
|
||||
globalStack.Push(token{BOOL, v1.val.(int) < v1.val.(int), line})
|
||||
case FLOAT:
|
||||
globalStack.Push(token{BOOL, v1.val.(float64) < v1.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})
|
||||
} else if (v1.kind == FLOAT && v2.kind == INT) {
|
||||
globalStack.Push(token{BOOL, v1.val.(float64) < float64(v1.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))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func libStackDump() error {
|
||||
fmt.Fprintf(os.Stderr, "\n%s\n", globalStack.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
func libClearStack() error {
|
||||
globalStack.sp = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func libConvertType(line int) error {
|
||||
typeToken := globalStack.Pop()
|
||||
typeToken, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
if typeToken.kind != TYPE {
|
||||
return fmt.Errorf("ERROR | Line %d\n `cast` expected a TYPE on top of stack\n", line)
|
||||
}
|
||||
fromToken := globalStack.Pop()
|
||||
fromToken, err := globalStack.Pop()
|
||||
if err != nil {
|
||||
return fmt.Errorf(stackErrFmt, line, err.Error())
|
||||
}
|
||||
toKind, fromKind := typeToken.val.(int), fromToken.kind
|
||||
|
||||
if fromKind == toKind {
|
||||
|
|
100
lexer.go
100
lexer.go
|
@ -1,99 +1,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type token struct {
|
||||
kind int
|
||||
val value
|
||||
line int
|
||||
}
|
||||
|
||||
type tokenReader struct {
|
||||
tokens []token
|
||||
p int
|
||||
}
|
||||
|
||||
func (t *tokenReader) Read() (token, error) {
|
||||
t.p++
|
||||
if t.p - 1 >= len(t.tokens) {
|
||||
t.p--
|
||||
return token{}, fmt.Errorf("Token overflow")
|
||||
}
|
||||
return t.tokens[t.p-1], nil
|
||||
}
|
||||
|
||||
func (t *tokenReader) Unread() (error) {
|
||||
t.p--
|
||||
if t.p < 0 {
|
||||
return fmt.Errorf("Token underflow")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t tokenReader) Current() int {
|
||||
return t.p
|
||||
}
|
||||
|
||||
func (t *tokenReader) Jump(i int) {
|
||||
if i < 0 || i > len(t.tokens) {
|
||||
panic("Token jump error")
|
||||
}
|
||||
t.p = i
|
||||
}
|
||||
|
||||
func NewTokenReader(tokens []token) *tokenReader {
|
||||
return &tokenReader{tokens, 0}
|
||||
}
|
||||
|
||||
|
||||
func (t token) String(litRep bool) string {
|
||||
var repr string
|
||||
switch t.kind {
|
||||
case INT:
|
||||
repr = strconv.Itoa(t.val.(int))
|
||||
case INT_a:
|
||||
repr = fmt.Sprintf("%v", t.val.([]int))
|
||||
case FLOAT:
|
||||
repr = fmt.Sprintf("%f", t.val.(float64))
|
||||
case FLOAT_a:
|
||||
repr = fmt.Sprintf("%v", t.val.([]float64))
|
||||
case BOOL:
|
||||
if t.val.(bool) {
|
||||
repr = "true"
|
||||
} else {
|
||||
repr = "false"
|
||||
}
|
||||
case BOOL_a:
|
||||
repr = fmt.Sprintf("%v", t.val.([]bool))
|
||||
case STRING:
|
||||
if litRep {
|
||||
repr = fmt.Sprintf(`"%s"`, t.val.(string))
|
||||
} else {
|
||||
repr = fmt.Sprintf("%s", t.val.(string))
|
||||
}
|
||||
case STRING_a:
|
||||
repr = fmt.Sprintf("%v", t.val.([]string))
|
||||
case KEYWORD, IDENT:
|
||||
repr = fmt.Sprintf("%s", t.val.(string))
|
||||
case PROC:
|
||||
repr = t.val.(proc).String()
|
||||
default:
|
||||
panic("Issue while stringifying token")
|
||||
}
|
||||
|
||||
if debug {
|
||||
return fmt.Sprintf("TOKEN %-10s Ln: %-3d %-10s\n", kindToString(t.kind), t.line, repr)
|
||||
} else {
|
||||
return repr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func lex(f string ) []token {
|
||||
var reader *strings.Reader
|
||||
data := f
|
||||
|
@ -237,6 +149,18 @@ func eatWordNumber(r *strings.Reader, currentLine *int) token {
|
|||
*currentLine,
|
||||
}
|
||||
|
||||
if tokenString == "false" {
|
||||
out.val = false
|
||||
out.kind = BOOL
|
||||
return out
|
||||
}
|
||||
|
||||
if tokenString == "true" {
|
||||
out.val = true
|
||||
out.kind = BOOL
|
||||
return out
|
||||
}
|
||||
|
||||
// Check for hex string
|
||||
if strings.HasPrefix(tokenString, "0x") {
|
||||
i, err := strconv.ParseInt(tokenString[2:], 16, 0)
|
||||
|
|
151
main.go
151
main.go
|
@ -1,47 +1,32 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
// "fmt"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
ln "github.com/peterh/liner"
|
||||
)
|
||||
|
||||
const (
|
||||
version float64 = 0.1
|
||||
name string = "felise"
|
||||
)
|
||||
|
||||
var line *ln.State
|
||||
var initialTerm ln.ModeApplier = nil
|
||||
var linerTerm ln.ModeApplier = nil
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Variable sigil system:
|
||||
|
||||
- $myString (string)
|
||||
- +myInt (int)
|
||||
- %myFloat (float)
|
||||
- ?myBool (bool)
|
||||
- $:myString ([]string)
|
||||
- #:myInt ([]int)
|
||||
- %:myFloat ([]float)
|
||||
- ?:myBool ([]bool)
|
||||
- @myHandle (io-handle)
|
||||
- *$myString (ref to string)
|
||||
|
||||
---
|
||||
|
||||
Special forms:
|
||||
|
||||
var [varname]
|
||||
The above creates a var initialized to a value,
|
||||
it will be set to a type based on the sigil
|
||||
|
||||
proc [procname] | This is a docstring | [body...] .
|
||||
Procs should not start with a sigil value as they
|
||||
become words in the language. Procs get their
|
||||
own env during execution, similar to scheme. This
|
||||
allows for nested procs that only live in the given
|
||||
scope
|
||||
|
||||
set [varname/procname]
|
||||
The above will set the var to TOS (and pop off TOS),
|
||||
it will be typechecked and an error will be thrown
|
||||
if it is not the correct type
|
||||
Ideas:
|
||||
|
||||
del [varname/procname]
|
||||
Will remove the symbol and value from the symbol table
|
||||
|
||||
// XXX note sure about these array ops
|
||||
-> [varname]
|
||||
Will append a value to the given array
|
||||
|
||||
|
@ -54,14 +39,8 @@ length [varname]
|
|||
or an array
|
||||
|
||||
ref [ident/symbol]
|
||||
Put the symbol on TOS
|
||||
|
||||
|
||||
stack >|
|
||||
ref $mystring
|
||||
stack >$mystring|
|
||||
var
|
||||
|
||||
Put the symbol on TOS, to reference something that would
|
||||
otherwise be executed immediately (if, while, proc, etc)
|
||||
|
||||
---
|
||||
|
||||
|
@ -69,7 +48,7 @@ Stack ops:
|
|||
|
||||
When the interpreter encounters a symbol it will add it
|
||||
to the stack if it is a value type. Otherwise, it will
|
||||
produce a call to the proc.
|
||||
produce a call to the proc, if, or while
|
||||
|
||||
---
|
||||
|
||||
|
@ -86,38 +65,98 @@ Syntactic sugar:
|
|||
|
||||
---
|
||||
|
||||
Builtins:
|
||||
Builtins on radar:
|
||||
|
||||
- `+`, `-`, `*`, `/`, `%`
|
||||
- `open`
|
||||
- Opens a connection or file and returns a handle
|
||||
- `close`
|
||||
- Closes a connection or file
|
||||
- `write`
|
||||
- writes to a given handle
|
||||
- `read`, `readline`, `readchar`, `readbyte`
|
||||
- Read from the given handle to the stack
|
||||
- `append`
|
||||
- Appends TOS to a string, file, array
|
||||
- Appends TOS to a string, file, array, or open connection
|
||||
*/
|
||||
|
||||
func lexParseInterpret(s string) {
|
||||
func prompt(l *ln.State, cont bool) string {
|
||||
p := "> "
|
||||
|
||||
if cont {
|
||||
p = "+ "
|
||||
}
|
||||
val, err := l.Prompt(p)
|
||||
if err == ln.ErrPromptAborted {
|
||||
return ""
|
||||
}
|
||||
l.AppendHistory(val)
|
||||
return val
|
||||
}
|
||||
|
||||
|
||||
func repl() {
|
||||
fmt.Printf("%s version %.2f\n(`bye` or `exit` to quit)\n\n", name, version)
|
||||
initialTerm, _ = ln.TerminalMode()
|
||||
line = ln.NewLiner()
|
||||
linerTerm, _ = ln.TerminalMode()
|
||||
defer line.Close()
|
||||
|
||||
var text strings.Builder
|
||||
var cont bool
|
||||
baseEnv := NewEnv(nil)
|
||||
|
||||
for {
|
||||
if linerTerm != nil {
|
||||
linerTerm.ApplyMode()
|
||||
}
|
||||
in := prompt(line, cont)
|
||||
if initialTerm != nil {
|
||||
initialTerm.ApplyMode()
|
||||
}
|
||||
if s := strings.TrimSpace(in); s == "" {
|
||||
continue
|
||||
} else if s == "bye" || s == "exit" {
|
||||
break
|
||||
}
|
||||
if strings.HasSuffix(in, "\\") {
|
||||
cont = true
|
||||
text.WriteString(in[:len(in)-1])
|
||||
text.WriteRune('\n')
|
||||
} else {
|
||||
cont = false
|
||||
text.WriteString(in)
|
||||
}
|
||||
if !cont {
|
||||
lexParseInterpret(text.String(), baseEnv)
|
||||
text.Reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func lexParseInterpret(s string, en *env) {
|
||||
lexedTokens := lex(s)
|
||||
r := NewTokenReader(lexedTokens)
|
||||
parsedTokens := parse(r)
|
||||
err := interpret(parsedTokens, NewEnv(nil))
|
||||
err := interpret(parsedTokens, en)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
|
||||
globalStack.sp = 0
|
||||
} else {
|
||||
fmt.Println("\n\033[0mOk.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
codeString := `
|
||||
proc square
|
||||
| takes an INT from the stack and squares it, putting the result back on the stack |
|
||||
dup *
|
||||
.
|
||||
verFlag := flag.Bool("v", false, "Print version information and exit")
|
||||
flag.BoolVar(&debug, "debug", false, "Turns on debug mode")
|
||||
flag.Parse()
|
||||
|
||||
3
|
||||
5 swap square
|
||||
println`
|
||||
if *verFlag {
|
||||
fmt.Printf("%.2f\n", version)
|
||||
return
|
||||
}
|
||||
|
||||
lexParseInterpret(codeString)
|
||||
repl()
|
||||
}
|
||||
|
|
60
parser.go
60
parser.go
|
@ -5,6 +5,9 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
var ifDepth int
|
||||
var whileDepth int
|
||||
|
||||
func parse(r *tokenReader) []token {
|
||||
out := make([]token, 0, len(r.tokens))
|
||||
for {
|
||||
|
@ -15,21 +18,38 @@ func parse(r *tokenReader) []token {
|
|||
switch t.kind {
|
||||
case END:
|
||||
return out
|
||||
case SYMBOL:
|
||||
case KEYWORD:
|
||||
if t.val.(string) == "proc" {
|
||||
out = append(out, eatProcedure(r, t.line))
|
||||
} else if t.val.(string) == "break" {
|
||||
if whileDepth <= 0 {
|
||||
panic(fmt.Sprintf("`break` found outside of `while`/`dowhile` ... `.` construct on line %d", t.line))
|
||||
}
|
||||
out = append(out, t)
|
||||
} else if t.val.(string) == "else" {
|
||||
if ifDepth <= 0 {
|
||||
panic(fmt.Sprintf("`else` found outside of `if` .. `.` construct on line %d", t.line))
|
||||
}
|
||||
out = append(out, t)
|
||||
return out
|
||||
} else if t.val.(string) == "if" {
|
||||
out = append(out, eatIf(r, t.line))
|
||||
} else if t.val.(string) == "while" {
|
||||
out = append(out, eatWhile(r, t.line, false))
|
||||
} else if t.val.(string) == "dowhile" {
|
||||
out = append(out, eatWhile(r, t.line, true))
|
||||
} else {
|
||||
out = append(out, t)
|
||||
}
|
||||
case INT, FLOAT, STRING, BOOL, KEYWORD, TYPE:
|
||||
case INT, FLOAT, STRING, BOOL, TYPE, SYMBOL:
|
||||
out = append(out, t)
|
||||
case DOCSTRING:
|
||||
if debug {
|
||||
fmt.Fprintf(os.Stderr, "WARN | Line %d\n | docstring found outside of proc body\n", t.line)
|
||||
fmt.Fprintf(os.Stderr, "WARN │ Line %d\n │ docstring found outside of proc body\n", t.line)
|
||||
}
|
||||
default:
|
||||
if debug {
|
||||
fmt.Fprintf(os.Stderr, "WARN | Line %d\n | Unimplemented type found: %s\n", kindToString(t.kind))
|
||||
fmt.Fprintf(os.Stderr, "WARN │ Line %d\n │ Unimplemented type found: %s\n", kindToString(t.kind))
|
||||
}
|
||||
out = append(out, t)
|
||||
}
|
||||
|
@ -37,9 +57,39 @@ func parse(r *tokenReader) []token {
|
|||
return out
|
||||
}
|
||||
|
||||
func eatIf(r *tokenReader, line int) token {
|
||||
ifDepth++
|
||||
i := condIf{}
|
||||
i.hasFalseBranch = false
|
||||
truthy := parse(r)
|
||||
if len(truthy) != 0 {
|
||||
last := truthy[len(truthy)-1]
|
||||
if last.kind == KEYWORD && last.val.(string) == "else" {
|
||||
i.hasFalseBranch = true
|
||||
truthy = truthy[:len(truthy)-1]
|
||||
i.falsy = parse(r)
|
||||
last := i.falsy[len(truthy)-1]
|
||||
if last.kind == KEYWORD && last.val.(string) == "else" {
|
||||
panic(fmt.Sprintf("Extra call to `else` found on line %d", last.line))
|
||||
}
|
||||
}
|
||||
}
|
||||
i.truthy = truthy
|
||||
ifDepth--
|
||||
return token{IF, i, line}
|
||||
}
|
||||
|
||||
func eatWhile(r *tokenReader, line int, doWhile bool) token {
|
||||
whileDepth++
|
||||
w := while{}
|
||||
w.doWhile = doWhile
|
||||
w.body = parse(r)
|
||||
whileDepth--
|
||||
return token{WHILE, w, line}
|
||||
}
|
||||
|
||||
func eatProcedure(r *tokenReader, line int) token {
|
||||
p := proc{}
|
||||
p.kind = PROC
|
||||
name, err := r.Read()
|
||||
if name.kind != SYMBOL || err != nil {
|
||||
panic(fmt.Sprintf("proc expected a name on line %d", name.line))
|
||||
|
|
34
stack.go
34
stack.go
|
@ -1,24 +1,31 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type stack struct {
|
||||
s []token
|
||||
sp int
|
||||
}
|
||||
|
||||
func (p *stack) Push(t token) {
|
||||
func (p *stack) Push(t token) error {
|
||||
p.s[p.sp] = t
|
||||
p.sp++
|
||||
if p.sp >= len(p.s) {
|
||||
panic("Stack overflow")
|
||||
return errors.New("Stack overflow")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *stack) Pop() token {
|
||||
func (p *stack) Pop() (token, error) {
|
||||
p.sp--
|
||||
if p.sp < 0 {
|
||||
panic("Stack underflow")
|
||||
return token{}, errors.New("Stack underflow")
|
||||
}
|
||||
return p.s[p.sp]
|
||||
return p.s[p.sp], nil
|
||||
}
|
||||
|
||||
func (p stack) Peek() token {
|
||||
|
@ -32,6 +39,23 @@ func (p stack) Depth() int {
|
|||
return p.sp
|
||||
}
|
||||
|
||||
func (p stack) String() string {
|
||||
var b strings.Builder
|
||||
b.WriteString(`
|
||||
============
|
||||
STACK DUMP
|
||||
============
|
||||
|
||||
TOS->BOS:
|
||||
`)
|
||||
for i := p.sp-1; i >= 0; i-- {
|
||||
b.WriteString(fmt.Sprintf("| %s ", kindToString(p.s[i].kind)))
|
||||
}
|
||||
b.WriteString("|\n")
|
||||
return b.String()
|
||||
|
||||
}
|
||||
|
||||
func NewStack() stack {
|
||||
return stack{s: make([]token, STACK_SIZE, STACK_SIZE)}
|
||||
}
|
||||
|
|
141
types.go
141
types.go
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -11,6 +12,52 @@ import (
|
|||
*/
|
||||
type value interface{}
|
||||
|
||||
/*
|
||||
conditionals are implemented here as
|
||||
a special form handled by the interpreter
|
||||
and using the type system to provide
|
||||
that feature
|
||||
*/
|
||||
type condIf struct {
|
||||
truthy []token
|
||||
falsy []token
|
||||
hasFalseBranch bool
|
||||
}
|
||||
|
||||
func (ci condIf) String() string {
|
||||
var b strings.Builder
|
||||
b.WriteString("if ")
|
||||
for i := range ci.truthy {
|
||||
b.WriteString(toString(ci.truthy[i], true))
|
||||
b.WriteRune(' ')
|
||||
}
|
||||
if len(ci.falsy) > 0 {
|
||||
b.WriteString("then ")
|
||||
for i := range ci.falsy {
|
||||
b.WriteString(toString(ci.falsy[i], true))
|
||||
b.WriteRune(' ')
|
||||
}
|
||||
}
|
||||
b.WriteRune('.')
|
||||
return b.String()
|
||||
}
|
||||
|
||||
type while struct {
|
||||
doWhile bool // When true, functions as `do ... while` rather than `while ... do`
|
||||
body []token
|
||||
}
|
||||
|
||||
func (w while) String() string {
|
||||
var b strings.Builder
|
||||
b.WriteString("while ")
|
||||
for i := range w.body {
|
||||
b.WriteString(toString(w.body[i], true))
|
||||
b.WriteRune(' ')
|
||||
}
|
||||
b.WriteRune('.')
|
||||
return b.String()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
proc represents a procedure
|
||||
|
@ -21,7 +68,6 @@ type proc struct {
|
|||
body []token
|
||||
name string
|
||||
doc string
|
||||
kind int
|
||||
}
|
||||
|
||||
func (p proc) String() string {
|
||||
|
@ -30,7 +76,98 @@ func (p proc) String() string {
|
|||
b.WriteString(toString(p.body[i], true))
|
||||
b.WriteRune(' ')
|
||||
}
|
||||
return fmt.Sprintf("proc %s |%s| %s .", p.name, p.doc, b.String())
|
||||
if p.doc != "" {
|
||||
return fmt.Sprintf("proc %s\n |%s|\n %s\n.", p.name, p.doc, b.String())
|
||||
}
|
||||
return fmt.Sprintf("proc %s\n %s\n.", p.name, b.String())
|
||||
}
|
||||
|
||||
/*
|
||||
token is the basic unit that the interpreter
|
||||
understands
|
||||
*/
|
||||
type token struct {
|
||||
kind int
|
||||
val value
|
||||
line int
|
||||
}
|
||||
|
||||
func (t token) String(litRep bool) string {
|
||||
var repr string
|
||||
switch t.kind {
|
||||
case INT:
|
||||
repr = strconv.Itoa(t.val.(int))
|
||||
case FLOAT:
|
||||
repr = fmt.Sprintf("%f", t.val.(float64))
|
||||
case BOOL:
|
||||
if t.val.(bool) {
|
||||
repr = "true"
|
||||
} else {
|
||||
repr = "false"
|
||||
}
|
||||
case STRING:
|
||||
if litRep {
|
||||
repr = fmt.Sprintf(`"%s"`, t.val.(string))
|
||||
} else {
|
||||
repr = fmt.Sprintf("%s", t.val.(string))
|
||||
}
|
||||
case IF:
|
||||
repr = t.val.(condIf).String()
|
||||
case WHILE:
|
||||
repr = t.val.(while).String()
|
||||
case KEYWORD:
|
||||
repr = fmt.Sprintf("%s", t.val.(string))
|
||||
case PROC:
|
||||
repr = t.val.(proc).String()
|
||||
default:
|
||||
panic("Issue while stringifying token")
|
||||
}
|
||||
|
||||
if debug {
|
||||
return fmt.Sprintf("TOKEN %-10s Ln: %-3d %-10s\n", kindToString(t.kind), t.line, repr)
|
||||
} else {
|
||||
return repr
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
tokenReader creates a readable token stream
|
||||
that can be moved in both directions
|
||||
*/
|
||||
type tokenReader struct {
|
||||
tokens []token
|
||||
p int
|
||||
}
|
||||
|
||||
func (t *tokenReader) Read() (token, error) {
|
||||
t.p++
|
||||
if t.p - 1 >= len(t.tokens) {
|
||||
t.p--
|
||||
return token{}, fmt.Errorf("Token overflow")
|
||||
}
|
||||
return t.tokens[t.p-1], nil
|
||||
}
|
||||
|
||||
func (t *tokenReader) Unread() (error) {
|
||||
t.p--
|
||||
if t.p < 0 {
|
||||
return fmt.Errorf("Token underflow")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t tokenReader) Current() int {
|
||||
return t.p
|
||||
}
|
||||
|
||||
func (t *tokenReader) Jump(i int) {
|
||||
if i < 0 || i > len(t.tokens) {
|
||||
panic("Token jump error")
|
||||
}
|
||||
t.p = i
|
||||
}
|
||||
|
||||
func NewTokenReader(tokens []token) *tokenReader {
|
||||
return &tokenReader{tokens, 0}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue