diff --git a/env/env.go b/env/env.go index a7706d45..8dfb01e7 100755 --- a/env/env.go +++ b/env/env.go @@ -243,8 +243,30 @@ func (e *RyeCtx) Get2(word int) (Object, bool, *RyeCtx) { } func (e *RyeCtx) Set(word int, val Object) Object { - e.state[word] = val - return val + if _, exists := e.state[word]; exists { + return NewError("Can't set already set word, try using modword") + } else { + e.state[word] = val + return val + } +} + +func (e *RyeCtx) Unset(word int) Object { + if _, exists := e.state[word]; !exists { + return NewError("Can't unset non-existing word in this context") + } else { + delete(e.state, word) + return NewInteger(1) + } +} + +func (e *RyeCtx) Mod(word int, val Object) Object { + if _, exists := e.state[word]; exists { + e.state[word] = val + return val + } else { + return NewError("Can't mod an unset word, try using setword") + } } type ProgramState struct { diff --git a/env/idxs.go b/env/idxs.go index 71bacacb..04b58e69 100755 --- a/env/idxs.go +++ b/env/idxs.go @@ -48,6 +48,10 @@ var NativeTypes = [...]string{ // Todo change to BuiltinTypes "SpreadsheetRowType", "Decimal", "Vector", + "OpCPath", + "PipeCPath", + "Modword", + "LModword", } func (e *Idxs) IndexWord(w string) int { diff --git a/env/object.go b/env/object.go index 9c3bfb12..1a85c40f 100644 --- a/env/object.go +++ b/env/object.go @@ -48,6 +48,10 @@ const ( SpreadsheetRowType Type = 33 DecimalType Type = 34 VectorType Type = 35 + OpCPathType Type = 36 + PipeCPathType Type = 37 + ModwordType Type = 38 + LModwordType Type = 39 ) // after adding new type here, also add string to idxs.go @@ -601,6 +605,96 @@ func (i LSetword) Dump(e Idxs) string { return ":" + e.GetWord(i.Index) } +// +// MODWORD +// + +type Modword struct { + Index int +} + +func NewModword(index int) *Modword { + nat := Modword{index} + return &nat +} + +func (i Modword) Type() Type { + return ModwordType +} + +func (i Modword) Inspect(e Idxs) string { + return "[Modword: " + e.GetWord(i.Index) + "]" +} + +func (b Modword) Print(e Idxs) string { + return e.GetWord(b.Index) + ":" +} + +func (i Modword) Trace(msg string) { + fmt.Print(msg + "(Modword): ") + fmt.Println(i.Index) +} + +func (i Modword) GetKind() int { + return int(ModwordType) +} + +func (i Modword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Index == o.(Modword).Index +} + +func (i Modword) Dump(e Idxs) string { + return e.GetWord(i.Index) + ":" +} + +// +// LMODWORD +// + +type LModword struct { + Index int +} + +func NewLModword(index int) *LModword { + nat := LModword{index} + return &nat +} + +func (i LModword) Type() Type { + return LModwordType +} + +func (i LModword) Inspect(e Idxs) string { + return "[LModword: " + e.GetWord(i.Index) + "]" +} + +func (b LModword) Print(e Idxs) string { + return ":" + e.GetWord(b.Index) +} + +func (i LModword) Trace(msg string) { + fmt.Print(msg + "(lModword): ") + fmt.Println(i.Index) +} + +func (i LModword) GetKind() int { + return int(LModwordType) +} + +func (i LModword) Equal(o Object) bool { + if i.Type() != o.Type() { + return false + } + return i.Index == o.(LModword).Index +} + +func (i LModword) Dump(e Idxs) string { + return ":" + e.GetWord(i.Index) +} + // // OPWORD // @@ -1448,6 +1542,7 @@ func (i Argword) Dump(e Idxs) string { // type CPath struct { + Mode int // 0 Cpath, 1 OpCpath , 2 PipeCPath Cnt int Word1 Word Word2 Word @@ -1491,15 +1586,17 @@ func (i CPath) GetKind() int { return int(CPathType) } -func NewCPath2(w1 Word, w2 Word) *CPath { +func NewCPath2(mode int, w1 Word, w2 Word) *CPath { var cp CPath + cp.Mode = mode cp.Cnt = 2 cp.Word1 = w1 cp.Word2 = w2 return &cp } -func NewCPath3(w1 Word, w2 Word, w3 Word) *CPath { +func NewCPath3(mode int, w1 Word, w2 Word, w3 Word) *CPath { var cp CPath + cp.Mode = mode cp.Cnt = 3 cp.Word1 = w1 cp.Word2 = w2 diff --git a/evaldo/builtins.go b/evaldo/builtins.go index e1d64f5b..727cddf9 100644 --- a/evaldo/builtins.go +++ b/evaldo/builtins.go @@ -722,6 +722,20 @@ var builtins = map[string]*env.Builtin{ }, }, + "unset!": { // *** + Argsn: 1, + Doc: "Unset a word in current context", + Pure: false, + Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { + switch word := arg0.(type) { + case env.Word: + return ps.Ctx.Unset(word.Index) + default: + return MakeArgError(ps, 1, []env.Type{env.WordType}, "set") + } + }, + }, + "get_": { // *** find a name or decide on order of naming with generic words clashes with Argsn: 1, Doc: "Returns value of the word in context", diff --git a/evaldo/evaldo.go b/evaldo/evaldo.go index b90ba709..5781881d 100644 --- a/evaldo/evaldo.go +++ b/evaldo/evaldo.go @@ -251,6 +251,32 @@ func MaybeEvalOpwordOnRight(nextObj env.Object, ps *env.ProgramState, limited bo ps.Ser.Next() ps = EvalWord(ps, opword.ToWord(), ps.Res, false, opword.Force > 0) return MaybeEvalOpwordOnRight(ps.Ser.Peek(), ps, limited) + case env.CPath: + if opword.Mode == 1 { + ps.Ser.Next() + ps = EvalWord(ps, opword, ps.Res, false, false) // WWWWWWWWWWWWWWWWWWWWWWWWWWWW error interface converions + // when calling cpath + return MaybeEvalOpwordOnRight(ps.Ser.Peek(), ps, limited) + } else if opword.Mode == 2 { + if limited { + return ps + } + ps.Ser.Next() + ps = EvalWord(ps, opword, ps.Res, false, false) // TODO .. check opword force + if ps.ReturnFlag { + return ps //... not sure if we need this + } + // checkFlagsBi() + /*if ps.FailureFlag { // uncommented 202008017 + ps.FailureFlag = false + ps.ErrorFlag = true + ps.ReturnFlag = true + return ps + }*/ + return MaybeEvalOpwordOnRight(ps.Ser.Peek(), ps, limited) + } else { + ps.SkipFlag = false + } case env.Pipeword: if limited { return ps @@ -274,7 +300,25 @@ func MaybeEvalOpwordOnRight(nextObj env.Object, ps *env.ProgramState, limited bo } //ProcOpword(nextObj, es) idx := opword.Index - ps.Ctx.Set(idx, ps.Res) + ps.Res = ps.Ctx.Set(idx, ps.Res) + if ps.Res.Type() == env.ErrorType { + ps.ErrorFlag = true + return ps + } + ps.Ser.Next() + ps.SkipFlag = false + return MaybeEvalOpwordOnRight(ps.Ser.Peek(), ps, limited) + case env.LModword: + if limited { + return ps + } + //ProcOpword(nextObj, es) + idx := opword.Index + ps.Res = ps.Ctx.Mod(idx, ps.Res) + if ps.Res.Type() == env.ErrorType { + ps.ErrorFlag = true + return ps + } ps.Ser.Next() ps.SkipFlag = false return MaybeEvalOpwordOnRight(ps.Ser.Peek(), ps, limited) @@ -329,6 +373,8 @@ func EvalExpressionConcrete(ps *env.ProgramState) *env.ProgramState { return EvalGenword(ps, object.(env.Genword), nil, false) case env.SetwordType: return EvalSetword(ps, object.(env.Setword)) + case env.ModwordType: + return EvalModword(ps, object.(env.Modword)) case env.GetwordType: return EvalGetword(ps, object.(env.Getword), nil, false) case env.CommaType: @@ -360,7 +406,6 @@ func findWordValue(ps *env.ProgramState, word1 env.Object) (bool, env.Object, *e case env.Opword: object, found := ps.Ctx.Get(word.Index) return found, object, nil - case env.CPath: currCtx := ps.Ctx i := 1 @@ -421,8 +466,9 @@ func EvalWord(ps *env.ProgramState, word env.Object, leftVal env.Object, toLeft } } // fmt.Println(kind) - if leftVal != nil && ps.Ctx.Kind.Index != -1 { // don't use generic words if context kind is -1 --- TODO temporary solution to isolates, think about it more - object, found = ps.Gen.Get(kind, word.(env.Word).Index) + rword, ok := word.(env.Word) + if ok && leftVal != nil && ps.Ctx.Kind.Index != -1 { // don't use generic words if context kind is -1 --- TODO temporary solution to isolates, think about it more + object, found = ps.Gen.Get(kind, rword.Index) } } if found { @@ -495,7 +541,22 @@ func EvalSetword(ps *env.ProgramState, word env.Setword) *env.ProgramState { // es1 := EvalExpression(es) ps1, _ := EvalExpressionInj(ps, nil, false) idx := word.Index - ps1.Ctx.Set(idx, ps1.Res) + ps1.Res = ps1.Ctx.Set(idx, ps1.Res) + if ps1.Res.Type() == env.ErrorType { + ps1.ErrorFlag = true + } + return ps1 +} + +// evaluates expression to the right and sets the result of it to a word in current context +func EvalModword(ps *env.ProgramState, word env.Modword) *env.ProgramState { + // es1 := EvalExpression(es) + ps1, _ := EvalExpressionInj(ps, nil, false) + idx := word.Index + ps1.Res = ps1.Ctx.Mod(idx, ps1.Res) + if ps1.Res.Type() == env.ErrorType { + ps1.ErrorFlag = true + } return ps1 } diff --git a/loader/loader.go b/loader/loader.go index b1a5479e..f7bb122e 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -266,11 +266,32 @@ func parseFpath(v *Values, d Any) (Any, error) { func parseCPath(v *Values, d Any) (Any, error) { switch len(v.Vs) { case 2: - return *env.NewCPath2(v.Vs[0].(env.Word), v.Vs[1].(env.Word)), nil + return *env.NewCPath2(0, v.Vs[0].(env.Word), v.Vs[1].(env.Word)), nil case 3: - return *env.NewCPath3(v.Vs[0].(env.Word), v.Vs[1].(env.Word), v.Vs[2].(env.Word)), nil + return *env.NewCPath3(0, v.Vs[0].(env.Word), v.Vs[1].(env.Word), v.Vs[2].(env.Word)), nil default: - return *env.NewCPath3(v.Vs[0].(env.Word), v.Vs[1].(env.Word), v.Vs[2].(env.Word)), nil + return *env.NewCPath3(0, v.Vs[0].(env.Word), v.Vs[1].(env.Word), v.Vs[2].(env.Word)), nil + } +} + +func parseOpCPath(v *Values, d Any) (Any, error) { + switch len(v.Vs) { + case 2: + return *env.NewCPath2(1, v.Vs[0].(env.Word), v.Vs[1].(env.Word)), nil + case 3: + return *env.NewCPath3(1, v.Vs[0].(env.Word), v.Vs[1].(env.Word), v.Vs[2].(env.Word)), nil + default: + return *env.NewCPath3(1, v.Vs[0].(env.Word), v.Vs[1].(env.Word), v.Vs[2].(env.Word)), nil + } +} +func parsePipeCPath(v *Values, d Any) (Any, error) { + switch len(v.Vs) { + case 2: + return *env.NewCPath2(2, v.Vs[0].(env.Word), v.Vs[1].(env.Word)), nil + case 3: + return *env.NewCPath3(2, v.Vs[0].(env.Word), v.Vs[1].(env.Word), v.Vs[2].(env.Word)), nil + default: + return *env.NewCPath3(2, v.Vs[0].(env.Word), v.Vs[1].(env.Word), v.Vs[2].(env.Word)), nil } } @@ -305,6 +326,20 @@ func parseLSetword(v *Values, d Any) (Any, error) { return *env.NewLSetword(idx), nil } +func parseModword(v *Values, d Any) (Any, error) { + //fmt.Println("SETWORD:" + v.Token()) + word := v.Token() + idx := wordIndex.IndexWord(word[:len(word)-2]) + return *env.NewModword(idx), nil +} + +func parseLModword(v *Values, d Any) (Any, error) { + //fmt.Println("SETWORD:" + v.Token()) + word := v.Token() + idx := wordIndex.IndexWord(word[2:]) + return *env.NewLModword(idx), nil +} + func parseOpword(v *Values, d Any) (Any, error) { //fmt.Println("OPWORD:" + v.Token()) word := v.Token() @@ -404,12 +439,14 @@ func newParser() *Parser { // TODO -- add string eaddress path url time BLOCK <- "{" SPACES SERIES* "}" BBLOCK <- "[" SPACES SERIES* "]" GROUP <- "(" SPACES SERIES* ")" - SERIES <- (GROUP / COMMENT / URI / EMAIL / STRING / DECIMAL / NUMBER / COMMA / SETWORD / LSETWORD / ONECHARPIPE / PIPEWORD / EXWORD / XWORD / OPWORD / TAGWORD / CPATH / FPATH / KINDWORD / GENWORD / GETWORD / WORD / VOID / BLOCK / GROUP / BBLOCK / ARGBLOCK ) SPACES + SERIES <- (GROUP / COMMENT / URI / EMAIL / STRING / DECIMAL / NUMBER / COMMA / MODWORD / SETWORD / LMODWORD / LSETWORD / ONECHARPIPE / PIPECPATH / PIPEWORD / EXWORD / XWORD / OPCPATH / OPWORD / TAGWORD / CPATH / FPATH / KINDWORD / GENWORD / GETWORD / WORD / VOID / BLOCK / GROUP / BBLOCK / ARGBLOCK ) SPACES ARGBLOCK <- "{" WORD ":" WORD "}" WORD <- LETTER LETTERORNUM* / NORMOPWORDS GENWORD <- "~" UCLETTER LCLETTERORNUM* SETWORD <- LETTER LETTERORNUM* ":" + MODWORD <- LETTER LETTERORNUM* "::" LSETWORD <- ":" LETTER LETTERORNUM* + LMODWORD <- "::" LETTER LETTERORNUM* GETWORD <- "?" LETTER LETTERORNUM* PIPEWORD <- "\\" LETTER LETTERORNUM* / "|" LETTER LETTERORNUM* / PIPEARROWS / "|" NORMOPWORDS ONECHARPIPE <- "|" ONECHARWORDS @@ -425,6 +462,8 @@ func newParser() *Parser { // TODO -- add string eaddress path url time EMAILPART <- < ([a-zA-Z0-9._]+) > FPATH <- "%" URIPATH* CPATH <- WORD ( "/" WORD )+ + OPCPATH <- "." WORD ( "/" WORD )+ + PIPECPATH <- "\\" WORD ( "/" WORD )+ / "|" WORD ( "/" WORD )+ ONECHARWORDS <- < [<>*+-=/] > NORMOPWORDS <- < ("_"[<>*+-=/]) > PIPEARROWS <- ">>" / "~>" / "->" @@ -462,6 +501,8 @@ func newParser() *Parser { // TODO -- add string eaddress path url time g["VOID"].Action = parseVoid g["SETWORD"].Action = parseSetword g["LSETWORD"].Action = parseLSetword + g["MODWORD"].Action = parseModword + g["LMODWORD"].Action = parseLModword g["OPWORD"].Action = parseOpword g["PIPEWORD"].Action = parsePipeword g["ONECHARPIPE"].Action = parseOnecharpipe @@ -478,6 +519,8 @@ func newParser() *Parser { // TODO -- add string eaddress path url time g["URI"].Action = parseUri g["FPATH"].Action = parseFpath g["CPATH"].Action = parseCPath + g["OPCPATH"].Action = parseOpCPath + g["PIPECPATH"].Action = parsePipeCPath g["COMMENT"].Action = parseComment /* g["SERIES"].Action = func(v *Values, d Any) (Any, error) { return v, nil diff --git a/main.go b/main.go index 08f33e31..b9fb1565 100644 --- a/main.go +++ b/main.go @@ -571,11 +571,11 @@ func main_cgi_file(file string, sig bool) { } func main_rye_repl(_ io.Reader, _ io.Writer, subc bool, here bool) { - input := " 123 " // "name: \"Rye\" version: \"0.011 alpha\"" + input := " " // "name: \"Rye\" version: \"0.011 alpha\"" // userHomeDir, _ := os.UserHomeDir() // profile_path := filepath.Join(userHomeDir, ".rye-profile") - fmt.Println("Welcome to Rye console. Use ls for current or lsp or lsp\\ \"prin\" to list parent context.") + fmt.Println("Welcome to Rye console. Use ls to list current or lsp and lsp\\ \"prin\" to list parent contexts.") //if _, err := os.Stat(profile_path); err == nil { //content, err := os.ReadFile(profile_path)