Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT: initial format module #5069

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion compiler.r
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ red: context [

standard-modules: [
;-- Name ------ Entry file -------------- OS availability -----
View %modules/view/view.red [Windows macOS Linux]
View %modules/view/view.red [Windows macOS Linux]
Format %modules/format/module.red [Windows macOS Linux Syllable FreeBSD OpenBSD] ;-- same list as for L10N
]

func-constructors: [
Expand Down
119 changes: 119 additions & 0 deletions modules/format/charmaps.red
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
Red [
Title: "Char maps for format L10N"
Description: "Provide L10N of symbols (mainly digits) across formatting module"
Author: @hiiamboris
Rights: "Copyright (C) 2021-2022 Red Foundation. All rights reserved."
License: {
Distributed under the Boost Software License, Version 1.0.
See https://github.com/red/red/blob/master/BSL-License.txt
}
Notes: {
Char map reflects how each symbol is translated in a given locale.
Date/time uses only digits translation,
Number formatter - also superscript, separators and special float names.

`testing` charmap is only used by number formatter,
mainly to distinguish financial from normal digits.
}
]

; #include %locale.red
; #include %../common/new-each.red

context [
digit-list: "0123456789"
superscript: "0⁰1¹2²3³4⁴5⁵6⁶7⁷8⁸9⁹+⁺-⁻(⁽)⁾" ;-- hardcoded
; subscript: "0₀1₁2₂3₃4₄5₅6₆7₇8₈9₉+₊-₋(₍)₎" ;-- not used yet

;@@ temporary helper until we have a proper one in runtime
map-each: function ['word series code /eval] [
collect [
looper: either integer? series ['repeat]['foreach]
system/words/:looper (word) series [
keep either eval [reduce do code][do code]
]
]
]

;; prepare charmap for 'testing' locale
build-test-charmap: function [] [
ct: #() ;-- for testing: chars are fixed and differ from prototypes
ct/superscript: to map! map-each/eval [x y] superscript [[x y]]
ct/finance: to map! map-each/eval x digit-list [ ;-- fin digits should differ from default ones
[x pick "𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡" x - #"0" + 1] ;-- so we can distinguish them in tests
]
put ct/finance #"$" func [size] [
pick ["¥" "CN¥" "CNY" "Chinese yuan"] size ;-- names should differ is the only requirement
]
ct/finance/(#"."): #","
ct/finance/(#" "): #"."
ct/default: to map! map-each/eval x digit-list [
[x pick "0123456789" x - #"0" + 1]
]
foreach [x y] "..EEx× '++--(())%%‰‰" [ct/default/:x: y]
; ct/default/(#"#"): "" ;-- empty for hash, used to remove `#`s
ct/default/nan: "NaN"
ct/default/inf: "INF"
ct
]

formatting/build-charmap: function [
"Fill char-map with data from system/locale"
/for lc-name [word!] "Default: system/locale/locale"
][
if lc-name = 'testing [
return formatting/charmaps/testing: build-test-charmap
]

lc-name: any [
lc-name
system/locale/locale
do make error! rejoin ["Unsupported locale "lc-name]
]
; any [lc-name ERROR "Unsupported locale (lc-name)"]
unless lc-name = 'testing [system/locale/tools/expand-locale lc-name]
loc: system/locale/list/:lc-name
#assert [loc]

cm: make map! []
cm/superscript: to map! map-each/eval [x y] superscript [[x y]]

sys: loc/numbers/system
dig: loc/numbers/:sys/digits
fin: loc/numbers/:sys/fin-digits
#assert [dig]
#assert [fin]
cm/finance: to map! map-each/eval i 10 [[#"0" + i - 1 pick fin i]]

sym: loc/numbers/:sys/symbols
cm/default: def: to map! map-each/eval i 10 [[#"0" + i - 1 pick dig i]]
extend def reduce [
; #"." sym/decimal
; #"E" sym/exponential
; #" " sym/group
; #"-" sym/minus
; #"+" sym/plus
; #"%" sym/percent
; #"‰" sym/permille
; #"x" sym/superscripting-exponent
; #"#" "" ;-- used to remove absent digits
'nan sym/nan
'inf sym/infinity
]
formatting/charmaps/:lc-name: cm
]

;; called by format functions when they need this data
formatting/update-charmap: function [
"Fill char-map with data for chosen locale only if it's empty"
/for lc-name [word!] "Default: system/locale/locale"
][
loc: any [lc-name system/locale/locale]
#assert [loc]
any [
formatting/charmaps/:loc ;-- this works for testing locale as well
formatting/build-charmap/for loc
]
]

]
75 changes: 75 additions & 0 deletions modules/format/form-logic.red
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
Red [
Title: "Form-logic function"
Description: https://github.com/hiiamboris/red-formatting/discussions/12
Author: [@hiiamboris @greggirwin]
Rights: "Copyright (C) 2022 Red Foundation. All rights reserved."
License: {
Distributed under the Boost Software License, Version 1.0.
See https://github.com/red/red/blob/master/BSL-License.txt
}
]


formatting/logic-formats: #(
true-false ["True" "False"]
on-off ["On" "Off"]
yes-no ["Yes" "No"]
YN ["Y" "N"]
)

;@@ this deserves to be global, but first we will need to design it
;@@ right now it's just an adhoc helper
;@@ TODO: allow warnings when no translation is found
formatting/translate: function [
"Return translation of a string"
string [string!] "Returned as is if no translation is found"
/in locale [word! none!] "Locale to translate into"
][
locale: system/locale/tools/expand-locale locale
any [
select system/locale/list/:locale/strings string
string
]
]

formatting/form-logic: form-logic: function [
"Format a logic value as a string"
value [logic!]
fmt [word! string! block!] {One of [true-false on-off yes-no YN] or custom ["True" "False"] format}
/in locale [word! none!] "Locale to express value in"
][
fmts: formatting/logic-formats
if word? fmt [ ;-- Named formats
fmt: any [
select fmts fmt
do make error! rejoin ["Unknown named format: " fmt]
]
]
if 2 <> length? fmt [
do make error! rejoin ["Format " mold fmt " must contain 2 values"]
]
formatting/translate/in form pick fmt value locale ;-- form is used here to support custom values
]

#assert [
fmt-en: func [val fmt] [formatting/form-logic/in val fmt 'en_US]
fmt-ru: func [val fmt] [formatting/form-logic/in val fmt 'ru_RU]

"True" = fmt-en yes 'true-false
"False" = fmt-en no 'true-false
"Yes" = fmt-en yes 'yes-no
"No" = fmt-en no 'yes-no
"On" = fmt-en yes 'on-off
"Off" = fmt-en no 'on-off
"Y" = fmt-en yes 'YN
"N" = fmt-en no 'YN
"1" = fmt-en yes "10"
"0" = fmt-en no "10"
"RIGHT" = fmt-en yes ["RIGHT" "WRONG"]
"WRONG" = fmt-en no ["RIGHT" "WRONG"]

"Вкл." = fmt-ru yes 'on-off
"Выкл." = fmt-ru no 'on-off
"Да" = fmt-ru yes 'YN
"Нет" = fmt-ru no 'YN
]
Loading