diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 008f7fa..77982fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,8 @@ on: - 'tests/**' - 'unchained.nimble' - '.github/workflows/ci.yml' + branches: + - 'master' pull_request: paths: - 'examples/**' diff --git a/changelog.org b/changelog.org index 486cebc..45ccf9a 100644 --- a/changelog.org +++ b/changelog.org @@ -1,3 +1,6 @@ +* v0.4.7 +- support ~range[T]~ types. Previously caused error when determining + associated "unit" * v0.4.6 - fix parsing of single runes, i.e. when doing ~defUnit(Ω•m⁻¹)~ the parser would try to parse the Ω as a single ~char~ and not a unicode rune diff --git a/src/unchained/macro_utils.nim b/src/unchained/macro_utils.nim index 9af6162..a1489f2 100644 --- a/src/unchained/macro_utils.nim +++ b/src/unchained/macro_utils.nim @@ -75,6 +75,21 @@ proc resolveTypeFromGenericInst(n: NimNode): NimNode = # simply leave as is result = n +proc resolveFromRange*(n: NimNode): NimNode = + ## Resolves the underlying type of a `range[T]`. + ## + ## In the end we call `getType.repr` on the literal (or constant) that defines + ## the lower range: + ## BracketExpr + ## Sym "range" + ## Infix + ## Ident ".." + ## FloatLit 0.0 + ## FloatLit 1.0 + let typ = n.getTypeInst + doAssert typ[0].repr == "range", "ntyRange is not a range type? " & $typ.treerepr + result = typ[1][1].getType + proc getUnitTypeImpl*(n: NimNode): NimNode = case n.typeKind of ntyAlias: result = n.resolveTypeFromAlias() @@ -82,6 +97,7 @@ proc getUnitTypeImpl*(n: NimNode): NimNode = of ntyTypeDesc: result = n.resolveTypeFromTypeDesc() of ntyGenericInst: result = n.resolveTypeFromGenericInst() of ntyFloat32, ntyFloat64, ntyFloat: result = n + of ntyRange: result = n.resolveFromRange() of ntyUserTypeClass: ## NOTE: Attempting to resolve a type from such an implicit generic doesn't ## work properly. See tests/tResolveImplicitQuantity.nim diff --git a/src/unchained/parse_units.nim b/src/unchained/parse_units.nim index b059fdf..c286a0c 100644 --- a/src/unchained/parse_units.nim +++ b/src/unchained/parse_units.nim @@ -193,13 +193,16 @@ proc tryLookupUnitType*(tab: UnitTable, n: NimNode): Option[UnitProduct] = ## In that case, we can just return it instead of parsing it again. ## This is especially useful for predefined aliases like `N = Newton = KiloGram•Meter•Second⁻²` ## as we otherwise fully resolve it to the long format. + proc inSupportedBaseType(s: string): bool = + result = s in ["FloatType", "float32", "float", "float64", "int", "int64", "UnitLess"] + proc fromTab(tab: UnitTable, nStr: string): Option[UnitProduct] = - if tab.isUserDefined(nStr): + if inSupportedBaseType(nStr): + result = some(initUnitProduct()) + elif tab.isUserDefined(nStr): result = some(tab.getUserDefined(nStr)) elif nStr in tab: result = some(tab[nStr].toUnitInstance(assignPrefix = false).toUnitProduct()) - elif nStr in ["FloatType", "float32", "float", "float64", "int", "int64", "UnitLess"]: - result = some(initUnitProduct()) case n.kind of nnkIdent: @@ -210,14 +213,33 @@ proc tryLookupUnitType*(tab: UnitTable, n: NimNode): Option[UnitProduct] = let nTyp = n.getTypeInst var nStr: string case nTyp.kind - of nnkBracketExpr: nStr = nTyp[1].strVal + of nnkBracketExpr: + case nTyp.len + of 1: nStr = nTyp[1].strVal + of 2: # might be a range type, e.g.: + # BracketExpr + # Sym "range" + # Infix + # Ident ".." + # FloatLit 0.0 + # FloatLit 1.0 + # or a `nnkBracketExpr[typedesc, foo]` + if nTyp[0].kind in [nnkSym, nnkIdent] and nTyp[0].strVal == "range": + # check if range is in supported types + let rangeTyp = nTyp[1][1].getType + nStr = rangeTyp.repr + elif nTyp[0].kind in [nnkSym, nnkIdent] and nTyp[0].strVal.normalize == "typedesc": + doAssert nTyp[1].kind in [nnkSym, nnkIdent], "Typedesc argument is not a symbol: " & $nTyp.treerepr + nStr = nTyp[1].strVal + else: + error("Unexpected type: " & $nTyp.treerepr & " for input: " & $n.treerepr) + else: + error("Unexpected type: " & $nTyp.treerepr & " for input: " & $n.treerepr) of nnkDistinctTy: nStr = nTyp[1].strVal of nnkSym: nStr = nTyp.strVal else: error("Invalid node for type : " & nTyp.repr) - if nStr in ["FloatType", "float32", "float", "float64", "int", "int64", "UnitLess"]: - result = some(initUnitProduct()) - else: - result = fromTab(tab, nStr) + + result = fromTab(tab, nStr) of nnkTypeOfExpr: result = tab.tryLookupUnitType(n[0]) else: diff --git a/tests/tunchained.nim b/tests/tunchained.nim index a41b1f5..d842e75 100644 --- a/tests/tunchained.nim +++ b/tests/tunchained.nim @@ -1057,6 +1057,18 @@ suite "Unchained - Bug issues": let x = 5.Ω•m⁻¹ check typeof(x) is Ω•m⁻¹ + test "Range types work": + block IntRange: + let x: range[0 .. 10] = 5 + let y = 123.kg + check typeof(x * y) is kg + check typeof(y / x) is kg + block FloatRange: + let x: range[0.0 .. 1.0] = 0.5 + let y = 123.kg + check typeof(x * y) is kg + check typeof(y / x) is kg + suite "Utils": test "Power w/ static integer exponents for floats": let x = 5 diff --git a/unchained.nimble b/unchained.nimble index df83493..ca1688c 100644 --- a/unchained.nimble +++ b/unchained.nimble @@ -16,7 +16,7 @@ task installTestDeps, "Install dependencies to run tests in CI": task test, "Runs the test": exec "nim c -r tests/tunchained.nim" exec "nim c -r tests/tresolveAlias.nim" - when not defined(windows): # For some reason in CI in this repo lapack isn't found using msys2 (works in ggplotnim) + when defined(linux): # For some reason in CI in this repo lapack isn't found using msys2 (works in ggplotnim) exec "nim c -r -d:nolapack examples/bethe_bloch.nim" exec "nim c -r examples/custom_unit_system.nim"