Merge pull request #657 from quantified-uncertainty/simple-units
Support "k", "M", "G", "T" and other units
This commit is contained in:
		
						commit
						493c406b99
					
				| 
						 | 
				
			
			@ -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))}")
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -55,16 +30,3 @@ let durationDispatch = (call: EV.functionCall, _: DistributionOperation.env): op
 | 
			
		|||
  | _ => 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
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user