Merge pull request #657 from quantified-uncertainty/simple-units

Support "k", "M", "G", "T" and other units
This commit is contained in:
Ozzie Gooen 2022-06-06 15:51:55 -07:00 committed by GitHub
commit 493c406b99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 99 additions and 64 deletions

View File

@ -232,6 +232,7 @@ describe("Peggy parse", () => {
})
describe("unit", () => {
testParse("1m", "{(::fromUnit_m 1)}")
testParse("1M", "{(::fromUnit_M 1)}")
testParse("1m+2cm", "{(::add (::fromUnit_m 1) (::fromUnit_cm 2))}")
})
})

View File

@ -264,6 +264,9 @@ basicLiteral
identifier 'identifier'
= ([_a-z]+[_a-z0-9]i*) {return nodeIdentifier(text())}
unitIdentifier 'identifier'
= ([_a-zA-Z]+[_a-z0-9]i*) {return nodeIdentifier(text())}
dollarIdentifier '$identifier'
= ([\$_a-z]+[\$_a-z0-9]i*) {return nodeIdentifier(text())}
@ -271,7 +274,7 @@ string 'string'
= characters:("'" @([^'])* "'") {return nodeString(characters.join(''))}
/ characters:('"' @([^"])* '"') {return nodeString(characters.join(''))}
number = number:(float / integer) unit:identifier?
number = number:(float / integer) unit:unitIdentifier?
{
if (unit === null)
{ return number }

View File

@ -0,0 +1,27 @@
module EV = ReducerInterface_ExpressionValue
type expressionValue = EV.expressionValue
let dispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch call {
| ("toString", [EvDate(t)]) => EV.EvString(DateTime.Date.toString(t))->Ok->Some
| ("makeDateFromYear", [EvNumber(year)]) =>
switch DateTime.Date.makeFromYear(year) {
| Ok(t) => EV.EvDate(t)->Ok->Some
| Error(e) => Reducer_ErrorValue.RETodo(e)->Error->Some
}
| ("dateFromNumber", [EvNumber(f)]) => EV.EvDate(DateTime.Date.fromFloat(f))->Ok->Some
| ("toNumber", [EvDate(f)]) => EV.EvNumber(DateTime.Date.toFloat(f))->Ok->Some
| ("subtract", [EvDate(d1), EvDate(d2)]) =>
switch DateTime.Date.subtract(d1, d2) {
| Ok(d) => EV.EvTimeDuration(d)->Ok
| Error(e) => Error(RETodo(e))
}->Some
| ("subtract", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.subtractDuration(d1, d2))->Ok->Some
| ("add", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.addDuration(d1, d2))->Ok->Some
| _ => None
}
}

View File

@ -1,32 +1,7 @@
module EV = ReducerInterface_ExpressionValue
type expressionValue = EV.expressionValue
let dateDispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch call {
| ("toString", [EvDate(t)]) => EV.EvString(DateTime.Date.toString(t))->Ok->Some
| ("makeDateFromYear", [EvNumber(year)]) =>
switch DateTime.Date.makeFromYear(year) {
| Ok(t) => EV.EvDate(t)->Ok->Some
| Error(e) => Reducer_ErrorValue.RETodo(e)->Error->Some
}
| ("dateFromNumber", [EvNumber(f)]) => EV.EvDate(DateTime.Date.fromFloat(f))->Ok->Some
| ("toNumber", [EvDate(f)]) => EV.EvNumber(DateTime.Date.toFloat(f))->Ok->Some
| ("subtract", [EvDate(d1), EvDate(d2)]) =>
switch DateTime.Date.subtract(d1, d2) {
| Ok(d) => EV.EvTimeDuration(d)->Ok
| Error(e) => Error(RETodo(e))
}->Some
| ("subtract", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.subtractDuration(d1, d2))->Ok->Some
| ("add", [EvDate(d1), EvTimeDuration(d2)]) =>
EV.EvDate(DateTime.Date.addDuration(d1, d2))->Ok->Some
| _ => None
}
}
let durationDispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
let dispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch call {
@ -54,17 +29,4 @@ let durationDispatch = (call: EV.functionCall, _: DistributionOperation.env): op
EV.EvTimeDuration(DateTime.Duration.divide(d1, d2))->Ok->Some
| _ => None
}
}
let dispatch = (call: EV.functionCall, env: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch dateDispatch(call, env) {
| Some(r) => Some(r)
| None =>
switch durationDispatch(call, env) {
| Some(r) => Some(r)
| None => None
}
}
}
}

View File

@ -14,15 +14,27 @@ type expressionValue = ExpressionValue.expressionValue
Map external calls of Reducer
*/
// I expect that it's important to build this first, so it doesn't get recalculated for each tryRegistry() call.
let registry = FunctionRegistry_Library.registry
let tryRegistry = ((fnName, args): ExpressionValue.functionCall, env) => {
FunctionRegistry_Core.Registry.matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
)
}
let dispatch = (call: ExpressionValue.functionCall, environment, chain): result<
expressionValue,
'e,
> =>
switch ReducerInterface_GenericDistribution.dispatch(call, environment) {
| Some(r) => r
| None =>
ReducerInterface_DateTime.dispatch(call, environment) |> E.O.default(chain(call, environment))
}
> => {
E.A.O.firstSomeFn([
() => ReducerInterface_GenericDistribution.dispatch(call, environment),
() => ReducerInterface_Date.dispatch(call, environment),
() => ReducerInterface_Duration.dispatch(call, environment),
() => ReducerInterface_Number.dispatch(call, environment),
() => tryRegistry(call, environment),
])->E.O2.default(chain(call, environment))
}
/*
If your dispatch is too big you can divide it into smaller dispatches and pass the call so that it gets called finally.

View File

@ -350,20 +350,5 @@ let genericOutputToReducerValue = (o: DistributionOperation.outputType): result<
| GenDistError(err) => Error(REDistributionError(err))
}
// I expect that it's important to build this first, so it doesn't get recalculated for each tryRegistry() call.
let registry = FunctionRegistry_Library.registry
let tryRegistry = ((fnName, args): ExpressionValue.functionCall, env) => {
FunctionRegistry_Core.Registry.matchAndRun(~registry, ~fnName, ~args, ~env)->E.O2.fmap(
E.R2.errMap(_, s => Reducer_ErrorValue.RETodo(s)),
)
}
let dispatch = (call: ExpressionValue.functionCall, environment) => {
let regularDispatch =
dispatchToGenericOutput(call, environment)->E.O2.fmap(genericOutputToReducerValue)
switch regularDispatch {
| Some(x) => Some(x)
| None => tryRegistry(call, environment)
}
}
let dispatch = (call: ExpressionValue.functionCall, environment) =>
dispatchToGenericOutput(call, environment)->E.O2.fmap(genericOutputToReducerValue)

View File

@ -0,0 +1,45 @@
module EV = ReducerInterface_ExpressionValue
type expressionValue = EV.expressionValue
module ScientificUnit = {
let nameToMultiplier = str =>
switch str {
| "n" => Some(1E-9)
| "m" => Some(1E-3)
| "k" => Some(1E3)
| "M" => Some(1E6)
| "B" => Some(1E9)
| "G" => Some(1E9)
| "T" => Some(1E12)
| "P" => Some(1E15)
| _ => None
}
let getMultiplier = (r: string) => {
let match = Js.String2.match_(r, %re(`/fromUnit_([_a-zA-Z]*)/`))
switch match {
| Some([_, unit]) => nameToMultiplier(unit)
| _ => None
}
}
}
let dispatch = (call: EV.functionCall, _: DistributionOperation.env): option<
result<expressionValue, QuriSquiggleLang.Reducer_ErrorValue.errorValue>,
> => {
switch call {
| (
("fromUnit_n"
| "fromUnit_m"
| "fromUnit_k"
| "fromUnit_M"
| "fromUnit_B"
| "fromUnit_G"
| "fromUnit_T"
| "fromUnit_P") as op,
[EvNumber(f)],
) =>
op->ScientificUnit.getMultiplier->E.O2.fmap(multiplier => EV.EvNumber(f *. multiplier)->Ok)
| _ => None
}
}