default

it's time to zoom out and look at what we've done so far.

we made a class that lets us define new terms. we can define a new term by OVERRIDING methods.

this is a MAXIMALIST approach. we are getting all of the INFORMATION we might need. EVERYTHING can be OVERRIDDEN.

HOWEVER, most of the time, you don't need to OVERRIDE EVERYTHING. because we can add SENSIBLE defaults to every single method.


the defaults you choose depend on how your head works. these are the defaults that i expect.

terms bite off nothing by default.

class Term {
	//...
	bite(text) {
		return ""
	}
}

check gets inferred from bite.

class Term {
	//...
	check(text) {
		return this.bite(text) !== null
	}
}

tail also gets inferred from bite.

class Term {
	//...
	tail(text) {
		const bite = this.bite(text)
		if (bite === null) {
			return null
		}
		return text.slice(bite.length)
	}
}

travel defaults to the bite or nothing.

class Term {
	//...
	travel(text) {
		return this.bite(text) ?? ""
	}
}

eat defaults to the bite and tail.

class Term {
	//...
	eat(text) {
		return [this.bite(text), this.tail(text)]
	}
}

terms emit their bite by default.

class Term {
	//...
	emit(text) {
		return this.bite(text)
	}
}

terms are anonymous by default.

class Term {
	//...
	name() {
		return "anonymous term"
	}
}

terms preview the rest of the text by default.

class Term {
	//...
	preview(text) {
		return text
	}
}

error uses check and name and preview.

class Term {
	//...
	error(text) {
		if (this.check(text)) {
			return null
		}
		return `Expected ${this.name()} but found '${text.preview(text)}'!`
	}
}

translate uses emit and error.

class Term {
	//...
	translate(text) {
		const result = this.emit(text)
		if (result === null) {
			throw new Error(this.error(text))
		}
		return result
	}
}

with all of these defaults, the terms we make end up being quite a bit smaller.

this is our string term.

class StringTerm extends Term {
	constructor(string) {
		super()
		this.string = string
	}

	name() {
		return `"${this.string}"`
	}

	bite(text) {
		if (text.startsWith(this.string)) {
			return this.string
		}
		return null
	}
}

this is our "or" term.

class OrTerm extends Term {
	constructor(term1, term2) {
		super()
		this.term1 = term1
		this.term2 = term2
	}

	name() {
		return `${this.term1.name()} or ${this.term2.name()}`
	}

	bite(text) {
		const bite1 = this.term1.bite(text)
		if (bite1 !== null) return bite1

		const bite2 = this.term2.bite(text)
		if (bite2 !== null) return bite2
		
		return null
	}

}

try it for yourself.

TRANSLATED:



back to the dream