TodePond / MotherTode

language language
MIT License
21 stars 1 forks source link

`term.js` #26

Open TodePond opened 1 year ago

TodePond commented 1 year ago

Literals

Scope

Operators

Built-In

Automatic Properties

Manual Properties

Performance

State

TodePond commented 1 year ago

literals and built-ins done

TodePond commented 1 year ago

all properties done

state management done

next, I need to have basic scoping to be able to test things like left-recursion (needed to test 'except')

TodePond commented 1 year ago

Actually scoping won't work without some sort of memoisation / referencing. gonna look at that now

TodePond commented 1 year ago

I need to be able to do this kind of comparison (and have it return true):

Term.string("hello") === Term.string("hello")

But it returns false because the Term.string function creates a new object each time.

There are a couple of possibilities:

  1. The Term.string function could return a cached term. But... the user might not want that. And it could be unpredictable, and hard to debug and... making terms would become more complicated and... etc...
  2. We could use some sort of wrapper to return a cached term:
Term.memo("string", ["hello"]) === Term.memo("string", ["hello"])

But what about more complicated arguments:

Term.memo("maybe", [Term.memo("string", "hello")])

Would we have to memo every term like that???

But what about functions and stuff!?

Term.memo("emit", [() => "ribbit"])

It feels really dodgy serialising functions like that. Scrap that idea then...

  1. To compare terms, you need to actively store them?
Term.set("hello", Term.string("hello"))

Term.get("hello") === Term.get("hello")

This is starting to really seem like scoping after all... Maybe I should just go for it!?

TodePond commented 1 year ago

The thing I don't like about this is... it introduces state into things :( Is there some way of doing this without state???

Term.declare = (names, values) => { ... }
Term.declare = ((add) => {
    const number = Term.or([add, literal])
    const literal = Term.regExp(/[0-9]+/)
    const _add = Term.list([
        Term.except(number, [add]),
        Term.string("+"),
        number,
    ])
    return [_add]
})
TodePond commented 1 year ago

Ok that's done now! That's enough for today

TodePond commented 1 year ago

Probably good to do skip next, and the other operators just to getem done

TodePond commented 1 year ago

Had some thoughts about the 'declare' syntax.

Firstly, either way, it would be good to change it's return + args to an object instead of an array. I already had trouble getting the terms in the right order yesterday, so that should be a sign that the array concept sucks.

Also, it feels like it's basically powerful enough to be used as 'scope' as well. You could declare declares inside declares to make deeper scopes.

The only thing it's missing (I think) is the concept of 'global' and 'self' (with global just being a top-level self).

So I think 'scope' could just be a wrapper around 'declare'? That would do some special stuff for self/global? The resultant term could just route the 'self' term onto itself.

const number = Term.scope( ... )

number.test("3")
number.test("3+2")

number.self === number
number.global === number

number.literal.test("3")
number.add.test("3+2")

number.add.global === number
TodePond commented 1 year ago

Refactored and split up 'declare' into 'hoist' and 'reference'.

and added a new method to terms: 'travel', which gets the longest possible snippet that is consistent with the term.

Useful for error messages, and deciding what the best error message to show is.

Also added a 'name' property to terms, which is a safer + shorter alternative to toString. Also useful for when you log a term to the console.

I realised that composite terms don't check 'check' so I've unticked that.