-
Notifications
You must be signed in to change notification settings - Fork 0
Module definition language definition
Fairly basic for now, and a lot of it is just a bit of a braindump. I'll write a proper language reference once I know better what things are going to look like.
The idea is, as far as possible, to make the language declarative: say "what" not "how". I think that this should work even for the more complex examples involving choice and repetition. (This is thinking just of surveys for the moment...)
Use the same "optional layout" approach as Haskell so that layout
converts to { ... ; ... ; ... } on lexing. I'll work out the
detailed rules for this as part of the grammar.
(I eventually want some sort of macro/syntax definition facility, so this might have be revisited when I implement that.)
-
In general, identifiers are composed of a period-separated sequence of identifier components, each of which begins with a letter and is composed of letters, digits,
_(underscore) and-(hyphen). Examples:oP.Examples.MoodSurvey,Standard.MontrealCogntiveAssessment,ClinicA.Admin.PatientIntake. -
Some identifiers are reserved as keywords.
-
Both keywords and user-defined identifiers are case-sensitive: all keywords are lower case.
-
Numeric literals:
123,1.4,-0.5E-6,25%: all numeric values are just treated as "numbers", i.e. there's no semantic split between integer and floating point values -- if a float is used in a context where an integer is needed, we'll just round. -
String literals:
"abc","Hello, \"Bob\"!"(Escape sequnces are\"for double quote and\[0-9]+for Unicode -- need to think about this a little). -
Boolean literals:
true,yes;false,no(all options equivalent when boolean value required). -
Vector/array literals:
[ 1, 2, 3 ],[ true, false, false ](heterogeneous). Equivalent to[ 0 => 1, 1 => 2, 2 => 3 ], i.e. treated the same as maps/records. -
Map/record literals:
[ a: 1.2, b = "abc", "11XC5" => false, 2: "x" ](keys may be identifiers, strings or numeric, separator can be=,:or=>).
The usual kind of thing:
-
Numeric operators:
+,-,*,/,^,floor,ceil,abs; -
Comparison operators:
==,/=,<=,>=,==@i,/=@i,<=@i,>=@i(the@ithings are case folding comparisons -- just experimenting with possible syntax for these); -
Boolean operators:
and,or,not,any,all; -
Function applications:
f(1, 2),myFunc(x, "abc"), etc.
-
General language features:
-
Functions: parameter passing using call-by-value or call-by-reference? Allow both, but require call-by-reference to be explicitly flagged? (Initial approach: functions all pure call-by-value only, mostly just to have them in place to play with and think about later.)
-
Macros: could be useful (see below in Questions), but need to think about the details -- some sort of backquote-like templating scheme?
-
Modules:
- What can go in a module?
- What happens when a module is activated? (Distinguished "Main" component?)
-
Enumerations: basically just a way to map between identifiers and some numeric or textual representation.
-
-
Application-specific features:
-
Components:
SurveyPage(a collection of questions),Question, function definitions, etc.; -
Assets: image, video, audio;
-
Translations:
- Base language to other languages;
- Templating/functional approach for pluralisation and word order issues.
-
Help/description text: probably using some sort of message catalogue approach -- this plays nicely with translation;
-
Module metadata: simple key/value block (like a COBOL
DESCRIPTION SECTION.(!)).
-
What about mechanisms for users to define, e.g. new question types?
I'd thought of having a macro-like Specialisation construction where
you would be able to define a question/component type that provides a
subset of the functionality of an existing one (as a really simple
example, you could define a YesNoQuestion as a specialisation of a
more general ChoiceQuestion), but that's quite restrictive.
To define general components though, you need to be able to represent the HTML and JavaScript templates that are generated when the component is rendered, which is getting way too detailed for most users.
-
Modules live in namespaces, e.g.
base,openPsych,openPsych.Tests,ClinicA, etc. -
Within each namespace, module names can be considered hierarchically, e.g.
SubstanceAbuse.MoodSurvey, but this is not necessary. -
Naming:
<namespace>:<module>gives namespace explicitly, e.g.ClinicA:Utils.Logo,openPsych:Tests.StroopTest; without namespace, a module name references the first matching module in a namespace search path. (Or a better way to think of it is perhaps a bit like Plan9's union directories, where you basically overlay all the modules defined in all the namespaces so you end up with what looks like a single module hierarchy, but you can easily override things as you want. For example, there might be abase:Styles.Fontsmodule that defines font names to be used for rendering components. If you define aClinicA:Styles.Fontsmodule and use theClinicAnamespace, then the definitions in the latter module will override the defaults, whatever they might be. -
A namespace is essentially a record containing modules; a module is essentially a record containing definitions of various kinds.
-
Defining modules:
-- Define the namespace for all further definitions in the file:
namespace openPsych
-- Define a module:
module Simple.MoodSurvey
...
- Setting namespace "search path":
use namespace ClinicA as A
use namespace openPsych
or
use namespace ClinicA as A, openPsych as oP
-
Namespace
baseavailable by default. -
Namespaces searched in order given in
use namespacedeclarations (or stacked in a union in that order, so the lastuse namespacegives the "topmost" namespace that you see before any of the others). -
Aliasing with
use namespace ... as .... -
Parameterisation over namespaces:
module Simple.MoodSurvey(styleNamespace)
use namespace styleNamespace
...
which can then be used as
use namespace ClinicA
Main = Simple.MoodSurvey(ClinicA)
or something like that...