EXU: add dimensional powers and area volume units
This commit is contained in:
@@ -18,6 +18,12 @@ const definitions: UnitDefinition[] = [
|
||||
{ id: 'm', symbol: 'm', dimension: 'length', factor: 1000 },
|
||||
{ id: 'in', symbol: 'in', dimension: 'length', factor: 25.4 },
|
||||
{ id: 'ft', symbol: 'ft', dimension: 'length', factor: 304.8 },
|
||||
{ id: 'mm2', symbol: 'mm2', dimension: 'area', factor: 1 },
|
||||
{ id: 'cm2', symbol: 'cm2', dimension: 'area', factor: 100 },
|
||||
{ id: 'm2', symbol: 'm2', dimension: 'area', factor: 1_000_000 },
|
||||
{ id: 'mm3', symbol: 'mm3', dimension: 'volume', factor: 1 },
|
||||
{ id: 'cm3', symbol: 'cm3', dimension: 'volume', factor: 1_000 },
|
||||
{ id: 'm3', symbol: 'm3', dimension: 'volume', factor: 1_000_000_000 },
|
||||
{ id: 'deg', symbol: 'deg', dimension: 'angle', factor: 1 },
|
||||
{ id: 'rad', symbol: 'rad', dimension: 'angle', factor: 180 / Math.PI },
|
||||
{ id: '%', symbol: '%', dimension: 'percent', factor: 1 },
|
||||
@@ -63,7 +69,7 @@ const tokenize = (source: string): Token[] => {
|
||||
while (index < source.length) {
|
||||
const character = source[index]
|
||||
if (/\s/.test(character)) { index += 1; continue }
|
||||
if ('+-*/(),'.includes(character)) { tokens.push({ kind: 'operator', text: character, position: index }); index += 1; continue }
|
||||
if ('+-*/^(),'.includes(character)) { tokens.push({ kind: 'operator', text: character, position: index }); index += 1; continue }
|
||||
if (character === '%') { tokens.push({ kind: 'identifier', text: character, position: index }); index += 1; continue }
|
||||
const number = source.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/)
|
||||
if (number) { tokens.push({ kind: 'number', text: number[0], position: index }); index += number[0].length; continue }
|
||||
@@ -95,6 +101,15 @@ const divide = (left: Quantity, right: Quantity): Quantity => {
|
||||
throw new TypeError(`Cannot divide ${left.dimension} by ${right.dimension}.`)
|
||||
}
|
||||
|
||||
const power = (base: Quantity, exponent: Quantity): Quantity => {
|
||||
if (exponent.dimension !== 'dimensionless' || !Number.isInteger(exponent.value)) throw new TypeError('Exponent must be a dimensionless integer.')
|
||||
if (base.dimension === 'dimensionless') return { value: Math.pow(base.value, exponent.value), dimension: 'dimensionless' }
|
||||
if (exponent.value === 1) return { ...base }
|
||||
if (exponent.value === 2 && base.dimension === 'length') return { value: base.value ** 2, dimension: 'area' }
|
||||
if (exponent.value === 3 && base.dimension === 'length') return { value: base.value ** 3, dimension: 'volume' }
|
||||
throw new TypeError(`Cannot raise ${base.dimension} to the power ${exponent.value}.`)
|
||||
}
|
||||
|
||||
const evaluateFunction = (name: string, args: Quantity[]): Quantity => {
|
||||
const normalized = name.toLowerCase()
|
||||
if (normalized === 'abs' || normalized === 'round') {
|
||||
@@ -170,7 +185,13 @@ class QuantityParser {
|
||||
private parseUnary(): Quantity {
|
||||
if (this.match('+')) return this.parseUnary()
|
||||
if (this.match('-')) { const value = this.parseUnary(); return { ...value, value: -value.value } }
|
||||
return this.parsePrimary()
|
||||
return this.parsePower()
|
||||
}
|
||||
|
||||
private parsePower(): Quantity {
|
||||
const base = this.parsePrimary()
|
||||
if (!this.match('^')) return base
|
||||
return power(base, this.parseUnary())
|
||||
}
|
||||
|
||||
private parsePrimary(): Quantity {
|
||||
|
||||
Reference in New Issue
Block a user