Skip to content

Instantly share code, notes, and snippets.

@inariksit
Last active Feb 20, 2021
Embed
What would you like to do?
Small example of a formal language in GF
-- Abstract syntax for a fragment of a proramming language.
-- Only class definition, to keep the example small.
abstract MyOOP = {
flags startcat = Class ;
cat
Class ; -- class ClassName : { [Field] }
Field ; -- field_name : BuiltinType
[Field]{0} ;
BuiltinType ;
fun
ClassDef : String -> [Field] -> Class ;
MkField : String -> BuiltinType -> Field ;
BoolType, StringType : BuiltinType ;
}
-- Concrete syntax for a programming language.
-- Example: class Business = { is_legal : Boolean } ;
concrete MyOOPCnc of MyOOP = {
lincat
[Field] = {s : Str ; isEmpty : IsEmpty} ;
param
IsEmpty = Empty | NonEmpty ;
lin
-- : String -> [Field] -> Class ;
ClassDef name fields = {
s = "class" ++ name.s ++
case fields.isEmpty of {
Empty => fields.s ;
NonEmpty => "= {" ++ fields.s ++ "}"
} ++ ";"
} ;
-- : String -> BuiltinType -> Field ;
MkField name type = {s = name.s ++ ":" ++ type.s} ;
-- These funs are automatically generated from cat [Field]{0} ;
-- : [Field]
BaseField = {s = [] ; isEmpty = Empty} ;
-- : Field -> [Field] -> [Field]
ConsField f fs =
let sep : Str = case fs.isEmpty of {
Empty => [] ;
NonEmpty => ";" } ;
in {s = f.s ++ sep ++ fs.s ; isEmpty = NonEmpty} ;
-- : BuiltinType ;
BoolType = {s = "Boolean"} ;
StringType = {s = "String"} ;
}
-- English concrete syntax.
-- Example: Business is a class with a Boolean field is_legal
concrete MyOOPEng of MyOOP = open SyntaxEng, (P=ParadigmsEng), SymbolicEng in {
lincat
Class = S ;
Field = LinField ; -- name : NP ; type : LinBuiltin
[Field] = {
s : ListNP ;
size : Size ;
firstField : LinField ; -- If only 1 field, use compact description
} ;
BuiltinType = LinBuiltin ; -- Two ways to express type:
-- AP: Boolean (field foo)
-- Adv: (field foo) of type Boolean
param
Size = Zero | One | Many ; -- Size of list
-----------
-- Class --
-----------
lin
-- : String -> [Field] -> Class ;
ClassDef name fields =
let classname : NP = symb name ;
with_fields : Adv = case fields.size of {
Zero => mkAdv with_Prep (mkNP noPl_Det field_N) ; -- with no fields
One => withField fields.firstField ; -- with a <type> field <name>
Many => withField fields.s -- with fields <name1 of type1, …>
} ;
description : NP = mkNP a_Det (mkCN class_N with_fields)
in mkS (mkCl classname description) ;
oper
withField = overload {
withField : LinField -> Adv = \fld -> -- (class) with a Boolean field is_legal
let boolean_AP : AP = fld.type.ap ;
is_legal_NP : NP = fld.name
in mkAdv with_Prep (mkNP a_Det
(mkCN boolean_AP
(mkCN field_N is_legal_NP))) ;
withField : [NP] -> Adv = \fields -> -- (class) with fields <is_legal of type Boolean, …>
mkAdv with_Prep (mkNP aPl_Det
(mkCN field_N
(mkNP and_Conj fields)))
} ;
-- Lexicon
class_N : N = P.mkN "class" ;
field_N : N = P.mkN "field" ;
noPl_Det : Det = mkDet no_Quant pluralNum ;
------------
-- Fields --
------------
lin
-- : String -> BuiltinType -> Field ;
MkField name type = {
name = symb name ;
type = type
} ;
-- : [Field]
BaseField = dummyListField ;
-- : Field -> [Field] -> [Field]
ConsField f fs =
case fs.size of {
Zero => fs ** { -- s is still just a dummy list, not used in ClassDef!
firstField = f ; -- put f into firstField
size = One -- increase size
} ;
One => fs ** {
s = mkListField f fs.firstField ; -- construct first actual list from f and fs.firstField
size = Many
} ;
Many => fs ** {s = mkListField f fs.s} -- add the new field to the existing list
} ;
oper
LinField : Type = {
name : NP ;
type : LinBuiltin
} ;
ofType : LinField -> NP = \fld -> mkNP fld.name fld.type.adv ;
mkListField = overload {
mkListField : LinField -> LinField -> ListNP = \f1,f2 ->
mkListNP (ofType f1) (ofType f2) ;
mkListField : LinField -> ListNP -> ListNP = \f,fs ->
mkListNP (ofType f) fs
} ;
-- Even empty lists must contain some ListNP, some NP etc.
-- None of these are ever used in ClassDef.
-- The important info is size = Zero. That tells ClassDef to output "a class with no fields".
dummyListField = {
s = mkListNP nothing_NP nothing_NP ;
firstField = {
name = nothing_NP ;
type = {ap = mkAP (P.mkA "empty") ; adv = P.mkAdv "nothing"}
} ;
size = Zero
} ;
--------------------
-- Builtin types --
--------------------
lin
BoolType = mkType "Boolean" ;
StringType = mkType "String" ;
oper
LinBuiltin : Type = {ap : AP ; adv : Adv} ;
mkType : Str -> LinBuiltin = \str -> {
ap = mkAP (P.mkA str) ;
adv = mkAdv (P.mkPrep "of type") (mkNP (P.mkPN str))
} ;
-- To make BuiltinTypes print out nicely in the GF shell, we use a linref.
-- See explanation in https://inariksit.github.io/gf/2018/08/28/gf-gotchas.html#linref
linref
BuiltinType = \bt -> (mkUtt bt.ap).s ;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment