Open TodePond opened 1 year ago
literals and built-ins done
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')
Actually scoping won't work without some sort of memoisation / referencing. gonna look at that now
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:
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...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...
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!?
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]
})
Ok that's done now! That's enough for today
Probably good to do skip next, and the other operators just to getem done
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
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.
Literals
Scope
Operators
Built-In
Automatic Properties
Manual Properties
Performance
State