Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove atoms table #5

Merged
merged 3 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 7 additions & 36 deletions engine/atom.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,12 @@ import (
"io"
"regexp"
"strings"
"sync"
"unicode/utf8"
)

var (
quotedAtomEscapePattern = regexp.MustCompile(`[[:cntrl:]]|\\|'`)
)

var (
atomTable = struct {
sync.RWMutex
names []string
atoms map[string]Atom
}{
atoms: map[string]Atom{},
}
)

// Well-known atoms.
var (
atomEmpty = NewAtom("")
Expand Down Expand Up @@ -208,27 +196,15 @@ var (
)

// Atom is a prolog atom.
type Atom uint64
type Atom string

// NewAtom interns the given string and returns an Atom.
func NewAtom(name string) Atom {
// A one-char atom is just a rune.
if r, n := utf8.DecodeLastRuneInString(name); r != utf8.RuneError && n == len(name) {
return Atom(r)
}

atomTable.Lock()
defer atomTable.Unlock()

a, ok := atomTable.atoms[name]
if ok {
return a
}
return Atom(name)
}

a = Atom(len(atomTable.names) + (utf8.MaxRune + 1))
atomTable.atoms[name] = a
atomTable.names = append(atomTable.names, name)
return a
func NewAtomRune(v rune) Atom {
return Atom(v)
}

// WriteTerm outputs the Atom to an io.Writer.
Expand All @@ -237,7 +213,7 @@ func (a Atom) WriteTerm(w io.Writer, opts *WriteOptions, _ *Env) error {
openClose := (opts.left != (operator{}) || opts.right != (operator{})) && opts.getOps().defined(a)

if openClose {
if opts.left.name != 0 && opts.left.specifier.class() == operatorClassPrefix {
if opts.left.name != "" && opts.left.specifier.class() == operatorClassPrefix {
_, _ = ew.Write([]byte(" "))
}
_, _ = ew.Write([]byte("("))
Expand Down Expand Up @@ -289,12 +265,7 @@ func (a Atom) Compare(t Term, env *Env) int {
}

func (a Atom) String() string {
if a <= utf8.MaxRune {
return string(rune(a))
}
atomTable.RLock()
defer atomTable.RUnlock()
return atomTable.names[a-(utf8.MaxRune+1)]
return string(a)
}

// Apply returns a Compound which Functor is the Atom and args are the arguments. If the arguments are empty,
Expand Down
17 changes: 8 additions & 9 deletions engine/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -1621,7 +1621,7 @@ func CharCode(vm *VM, char, code Term, k Cont, env *Env) *Promise {
return Error(representationError(flagCharacterCode, env))
}

return Unify(vm, ch, Atom(r), k, env)
return Unify(vm, ch, NewAtomRune(r), k, env)
default:
return Error(typeError(validTypeInteger, code, env))
}
Expand Down Expand Up @@ -1685,12 +1685,11 @@ func PutChar(vm *VM, streamOrAlias, char Term, k Cont, env *Env) *Promise {
case Variable:
return Error(InstantiationError(env))
case Atom:
if c > utf8.MaxRune {
r, n := utf8.DecodeLastRuneInString(c.String())
if r == utf8.RuneError || n != len(c.String()) {
return Error(typeError(validTypeCharacter, c, env))
}

r := rune(c)

switch _, err := s.WriteRune(r); {
case errors.Is(err, errWrongIOMode):
return Error(permissionError(operationOutput, permissionTypeStream, streamOrAlias, env))
Expand Down Expand Up @@ -1861,7 +1860,7 @@ func GetChar(vm *VM, streamOrAlias, char Term, k Cont, env *Env) *Promise {
return Error(representationError(flagCharacter, env))
}

return Unify(vm, char, Atom(r), k, env)
return Unify(vm, char, NewAtomRune(r), k, env)
case io.EOF:
return Unify(vm, char, atomEndOfFile, k, env)
case errWrongIOMode:
Expand Down Expand Up @@ -1941,7 +1940,7 @@ func PeekChar(vm *VM, streamOrAlias, char Term, k Cont, env *Env) *Promise {
return Error(representationError(flagCharacter, env))
}

return Unify(vm, char, Atom(r), k, env)
return Unify(vm, char, NewAtomRune(r), k, env)
case io.EOF:
return Unify(vm, char, atomEndOfFile, k, env)
case errWrongIOMode:
Expand Down Expand Up @@ -2335,7 +2334,7 @@ func numberCharsWrite(vm *VM, num, chars Term, k Cont, env *Env) *Promise {

cs := make([]Term, len(rs))
for i, r := range rs {
cs[i] = Atom(r)
cs[i] = NewAtomRune(r)
}
return Unify(vm, chars, List(cs...), k, env)
}
Expand Down Expand Up @@ -2584,7 +2583,7 @@ func CurrentCharConversion(vm *VM, inChar, outChar Term, k Cont, env *Env) *Prom
if c1, ok := env.Resolve(inChar).(Atom); ok {
r := []rune(c1.String())
if r, ok := vm.charConversions[r[0]]; ok {
return Unify(vm, outChar, Atom(r), k, env)
return Unify(vm, outChar, NewAtomRune(r), k, env)
}
return Unify(vm, outChar, c1, k, env)
}
Expand All @@ -2599,7 +2598,7 @@ func CurrentCharConversion(vm *VM, inChar, outChar Term, k Cont, env *Env) *Prom
}

ks[i] = func(context.Context) *Promise {
return Unify(vm, pattern, tuple(Atom(r), Atom(cr)), k, env)
return Unify(vm, pattern, tuple(NewAtomRune(r), NewAtomRune(cr)), k, env)
}
}
return Delay(ks...)
Expand Down
6 changes: 3 additions & 3 deletions engine/compound.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func writeCompoundOpInfix(w io.Writer, c Compound, opts *WriteOptions, env *Env,
(opts.right != operator{} && r >= opts.right.priority)

if openClose {
if opts.left.name != 0 && opts.left.specifier.class() == operatorClassPrefix {
if opts.left.name != "" && opts.left.specifier.class() == operatorClassPrefix {
_, _ = fmt.Fprint(&ew, " ")
}
_, _ = fmt.Fprint(&ew, "(")
Expand Down Expand Up @@ -494,7 +494,7 @@ func pair(k, v Term) Term {
}

func tuple(args ...Term) Term {
return Atom(0).Apply(args...)
return Atom("").Apply(args...)
}

type charList string
Expand Down Expand Up @@ -524,7 +524,7 @@ func (c charList) Arg(n int) Term {
var t Term
switch n {
case 0:
t = Atom(r)
t = NewAtomRune(r)
case 1:
if i == len(c) {
t = atomEmptyList
Expand Down
28 changes: 14 additions & 14 deletions engine/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -463,21 +463,21 @@ func (p *Parser) op(maxPriority Integer) (Atom, error) {
if p.current().kind == tokenCloseList {
p.backup()
}
return 0, errNoOp
return "", errNoOp
case atomEmptyBlock:
p.backup()
if p.current().kind == tokenCloseCurly {
p.backup()
}
return 0, errNoOp
return "", errNoOp
default:
return a, nil
}
}

t, err := p.next()
if err != nil {
return 0, err
return "", err
}
switch t.kind {
case tokenComma:
Expand All @@ -489,7 +489,7 @@ func (p *Parser) op(maxPriority Integer) (Atom, error) {
}

p.backup()
return 0, errExpectation
return "", errExpectation
}

func (p *Parser) term0(maxPriority Integer) (Term, error) {
Expand Down Expand Up @@ -571,7 +571,7 @@ func (p *Parser) term0Atom(maxPriority Integer) (Term, error) {
return nil, errExpectation
}

if p.placeholder != 0 && t == p.placeholder {
if p.placeholder != "" && t == p.placeholder {
if len(p.args) == 0 {
return nil, errPlaceholder
}
Expand Down Expand Up @@ -616,53 +616,53 @@ func (p *Parser) atom() (Atom, error) {

t, err := p.next()
if err != nil {
return 0, err
return "", err
}
switch t.kind {
case tokenOpenList:
t, err := p.next()
if err != nil {
return 0, err
return "", err
}
switch t.kind {
case tokenCloseList:
return atomEmptyList, nil
default:
p.backup()
p.backup()
return 0, errExpectation
return "", errExpectation
}
case tokenOpenCurly:
t, err := p.next()
if err != nil {
return 0, err
return "", err
}
switch t.kind {
case tokenCloseCurly:
return atomEmptyBlock, nil
default:
p.backup()
p.backup()
return 0, errExpectation
return "", errExpectation
}
case tokenDoubleQuotedList:
switch p.doubleQuotes {
case doubleQuotesAtom:
return NewAtom(unDoubleQuote(t.val)), nil
default:
p.backup()
return 0, errExpectation
return "", errExpectation
}
default:
p.backup()
return 0, errExpectation
return "", errExpectation
}
}

func (p *Parser) name() (Atom, error) {
t, err := p.next()
if err != nil {
return 0, err
return "", err
}
switch t.kind {
case tokenLetterDigit, tokenGraphic, tokenSemicolon, tokenCut:
Expand All @@ -671,7 +671,7 @@ func (p *Parser) name() (Atom, error) {
return NewAtom(unquote(t.val)), nil
default:
p.backup()
return 0, errExpectation
return "", errExpectation
}
}

Expand Down
4 changes: 2 additions & 2 deletions engine/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ func (s *Stream) properties() []Term {
ps = append(ps, atomOutput)
}

if s.alias != 0 {
if s.alias != "" {
ps = append(ps, atomAlias.Apply(s.alias))
}

Expand Down Expand Up @@ -510,7 +510,7 @@ type streams struct {
}

func (ss *streams) add(s *Stream) {
if s.alias != 0 {
if s.alias != "" {
if ss.aliases == nil {
ss.aliases = map[Atom]*Stream{}
}
Expand Down
4 changes: 2 additions & 2 deletions engine/term_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ func TestCompareAtomic(t *testing.T) {
{a: &y{}, t: NewVariable(), o: 1},
{a: &y{}, t: NewFloatFromInt64(0), o: 1},
{a: &y{}, t: Integer(0), o: 1},
{a: &y{}, t: Atom(0), o: 1},
{a: &y{}, t: Atom(""), o: 1},
{a: &y{}, t: &x{}, o: 1},
{a: &y{val: 1}, t: &y{val: 0}, cmp: cmp, o: 1},
{a: &y{val: 0}, t: &y{val: 0}, cmp: cmp, o: 0},
{a: &y{val: 0}, t: &y{val: 1}, cmp: cmp, o: -1},
{a: &y{}, t: &z{}, o: -1},
{a: &y{}, t: Atom(0).Apply(Integer(0)), o: -1},
{a: &y{}, t: Atom("").Apply(Integer(0)), o: -1},
}

for _, tt := range tests {
Expand Down
22 changes: 22 additions & 0 deletions engine/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,25 @@ func TestProcedureIndicator_Apply(t *testing.T) {
assert.Nil(t, c)
})
}

func TestVM_ResetEnv(t *testing.T) {
var vm VM
varCounter = 10
varContext = NewVariable()
rootContext = NewAtom("non-root")
rootEnv = &Env{
binding: binding{
key: newEnvKey(varContext),
value: NewAtom("non-root"),
},
}

t.Run("Reset environment", func(t *testing.T) {
vm.ResetEnv()

assert.Equal(t, int64(1), varCounter) // 1 because NewVariable() is called in ResetEnv()
assert.Equal(t, "root", rootContext.String())
assert.Equal(t, newEnvKey(varContext), rootEnv.binding.key)
assert.Equal(t, NewAtom("root"), rootEnv.binding.value)
})
}
Loading