This is an old revision of the document!
Parse the ALF language source file and generate a JSON abstract syntax tree (AST).
The homework will take one or two parameters from the command line. The first paremeter is the filename with the Alf script, the second one the output file. If the output file is missing, the output is the same as the script file with the extension ”.json”.
node main.js script.alf script.alf.json
Here are some tips for solving the homework:
Try running small scripts to test every feature.
If you use sublime text, you may use this syntax highlighting file to write Alf language.
You may read here how to add the file.
All the nodes in the AST have the following format:
{ id:"node_id" }
The list of the node ids is:
If you have any questions related to the homework, please ask them by posting an issue on the github repository with the title format [calculator] <your question title>. You will need a github account for that.
If you want to receive an email when issues are posted or when there are new messages, got to the github repository and click Watch.
The Alf language is a typed language defined as follows:
a:=10 b:=20l s:=55
Values are
7
7.5
"a"
"this is a text"
false
none
Comments are a part of the source file that is ignored. They are enclosed between ”{” and ”}” and are not imbricated.
{this is a text to describe the script} define v:=55
An identifier can be any value that starts with a letter, $ or _ and may have also numbers in its name.
identifier $identifier _identifier
{ id:"identifier", title:"identifier_name" }
A value is any integer number, float number, logic or string.
3534 45634.423 false "s" "string"
{ id:"value", type:"value_type", // integer, real, character, logic, string value:actual_value }
define variable_name [:variable_type] [:= value or expression], ...
define a:integer := 3 define a := 3 define a1:integer:=3, a2:string := "text", a3:int
{ id:"var", variables:[ // array of variables { type:"data type", title: "varable name" }, ] }
class class_name [extends super_class] property property_name:data_type [:= initial_value] ... ... message message_name[:data_type] parameter1[:data_type][:=default_value], parameter1[:data_type][:=default_value], parameter3[:data_type][:=default_value], ... begin // ... message source end end;
{ id:"class", title:"class_name", parent:"super_class_name", properties:[ // array of properties { type:"property data type", title: "property name", value: "property value" // if it exists }, ], messages: [ // array of messages { type:"message return data type" title:"message_name", parameters:[ // array of parameters { type:"data type", title: "parameter name" }, ], statements:[ // array of statements (even if it is only one) ... ] } }
object_name|property
{ id:"property", object: { expression for object }, title: { expression for property } }
list_name:elements_data_type[integer_number to integer_number] <note> [] are actual brackets and are mandatory </note>
{ id:"list", title: array_name, element_type:elements_data_type, from: from_integer_number, to: to_integer_number }
array_name#index
{ id:"element_of_list", list:{ expression for list }, index: { expression for index } }
Operator | Precedence |
---|---|
if | Low |
= != | |
> >= < ⇐ | |
and or xor | |
not | |
+ - | |
* / mod | High |
2+5 2*4+5 variable+5 x>y x=y 2+(x+y)*(5+6)
{ id:"exp", op:"operator", left:left_operand, right:right_operand }
{ id:"exp", op:"negative", value:50 }
variable := expression;
x := 1000 s := "text" + "s" y := [module f] l#i := 900 q.e := "this is a text"
{ id:"set", to: { ... id, property, element_of_list }, from: { ... exp } }
Messages are functions and are usually defined in classes. If a message is defined outside a class, it belongs to the module object.
message message_name:return_type parameter1:[parameter1_type][:=value], ... = expression
message message_name:return_type parameter1:[parameter1_type][:=value], ... begin ... end
message f1 begin end;
message sum:int a:int, b:int = is a+b
message power:int a:int, n:int begin define p:int define i:int if n = 0 then begin power:=1 end for i in [1,n] p:=p * a return p end
{ id:"message", title:"message_name", parameters:[ // array of parameters { type:"data type", id: "parameter name" }, ], return_type:"type of the value that the message returns", statements:[ // array of statements (even if it is only one) ... ] }
This is the statements that sets the return value of the message.
return x+y
{ id:"return", value: { ... } }
The is a call for a function
[object message_name parameter_name1:value1, parameter_name2:value, ...]
[object message_name value1, value2, ...]
[module write text:"Text"] s := [sum n1:3 n2:5] [module getdir]
{ id:"dispatch", message:"message_name", object:"destination_object_name", parameters:{ // dictionary of parameter name or index and value "parameter1": parameter_value, "2":parameter value, ... } }
if expression ... end
if expression ... else ... end
{ id:"if", exp:expression, then: [ // list of statements ] }
{ id:"if", exp:expression, then: [ // list of statements ], else: [ // list of statements ] }
There are three types of loops: while, repeat and for.
while expression ... end
{ id:"while", exp :expression, statements: [ // array of statements ] }
repeat ... when expression
{ id:"repeat", exp :expression, statements: [ // array of statements ] }
for variable_name in expression ... end
or
for variable_name in (number to number) ... end
for variable_name in (number downto number) ... end
{ id:"for", variable:variable_name, exp:{ expression ... } statements: [ // array of statements ] }
or
{ id:"for", variable:variable_name, list:{ low: { expression }, high: { expression }, direction: "to" or "downto" }, statements: [ // array of statements ] }
If the source file has any error, you will have to output a JSON with the error. There are two kinds of errors:
For any lexical error, the output will be:
{ error:"lexical", line: line_number, text: "text that is not recognised" }
For any syntactical error, the output will be:
{ error:"syntax", line: line_number, token: "token that the parser saw", // token name will not be checked by checker expected: [list of expected tokens] // token name will not be checked by checker }
For an additional 0.5p, implement the following:
Add preprocessing to the source. You have
Registers an identifier to a value.
#register identifier value
The identifier will be replaced in the program with the value (textually).
Verifies if an identifier is registered and has a value and adds the source code up to else or endif. Else is optional.
#register platform ... ... #if platform = windows p := "Windows" {this source is ignored if platform is not windows} #else p := "Linux" {this source code is ignored if platform is not linux} end
Unregister an identifier from a value.
#register N value ... #unregister N {N is undefined}
The homework will be tested automatically using a set of public and private tests.
You can download the public tests from the GitHub repository.
To run the tests, download the contents of the repository in the folder with the homework. Enter the verify folder and run ./run_all.sh.
cd verify ./run_all.sh
You will need bash for that. You can use either Linux or Windows Linux Subsystem.
wget https://nodejs.org/dist/v6.10.0/node-v6.10.0-linux-x64.tar.xz tar xvfJ node-v6.10.0-linux-x64.tar.xz cd node-v6.10.0-linux-x64 sudo cp -R * /usr
When uploading the homework, we might have some private tests that it needs to pass. vmchecker will run them.
The homework needs to be uploaded to vmchecker. Login with your moodle user name, select the Automates et Langages Formelles (FILS) course and upload the homework archive.
The readme file has the following format:
Your full name Group An explanation how you wrote your homework, what did you use, what are the main ideas.
To upload your homework, please follow the following:
DO NO include node_modules.
When the archive is uploaded, vmchecker will run:
unzip archive.zip homework cd homework npm install echo '{ "node":true, "loopfunc": true, "esnext":true }' > .jshintrc jshint *.js jison grammar.jison -o grammar.js