Brio Lang Examples
This page contains a few code examples to demonstrate what Brio Lang can do!
Send an SMS via Twilio
# Example: send a text message via Twilio
class Twilio{
method init(){
# set common instance variables
@baseUrl = "https://api.twilio.com/2010-04-01/Accounts"
@accountSid = getEnv("AccountSid")
@authToken = getEnv("AuthToken")
@from_ = getEnv("From")
}
method sendText(number, message){
# invoke the Twilio API to send a sms text
let url = @baseUrl + "/" + @accountSid + "/Messages.json"
let data = "From=" + @from_ + "&Body=" + message + "&To=" + number
httpPost(url, @accountSid, @authToken, data);
}
}
method main(){
# instantiate Twilio class and invoke method
let client = new Twilio()
client.sendText("+16501112222", "Hello world!")
print("message was sent!")
}
Web Application
Brio Lang can also be used to write server-side applications using the FastCGI protocol.
method main(environ){
let headers = "Content-Type: text/html\r\n\r\n";
let index = open("index.html", "r") # <h1>Hello world</h1>
let body = index.read();
return headers + body;
};
$ spawn-fcgi -p 8000 -n -- ./bin/brio -fcgi ./path/to/server.brio
After running spawn-fcgi
, you may now issue requests to your HTTP server http://localhost
which will pass the request to your Brio Lang application. Please note that your HTTP server must be configured appropriately, for example NGINX would have the following definition:
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
location / {
fastcgi_pass 127.0.0.1:8000;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
}
}
}
In the future when a built-in Socket
abstraction is introduced, an HTTP server can be run directly within Brio Lang without requiring a separate component.
Reversing a String
method reverse(value){
let newString = ""
each (let c : value){
newString = c + newString
}
return newString
}
method main(){
let x = reverse("Brio Lang")
print(x)
}
$ brio my_app.brio
gnaL oirB
JSON Parser
###
The type values of a JSON token.
###
class TokenType {
let EOF = 0 # special token for end of file
let L_BRACE = 1 # {
let R_BRACE = 2 # }
let L_BRACKET = 3 # [
let R_BRACKET = 4 # ]
let COLON = 5 # :
let COMMA = 6 # ,
let LITERAL_STR = 7 # "foo"
let LITERAL_INT = 8 # 50
let LITERAL_DECIMAL = 9 # 3.14
let LITERAL_BOOL = 10 # true
}
###
Class to represent a JSON token.
###
class Token {
method init(type, text){
@type = type
@text = text
}
}
###
Special exception for JSON parsing errors.
###
class JsonException {}
###
Lexer that takes a string input and outputs an array of tokens.
###
class JsonLexer {
method init(data){
@data = data
@index = 0
@character = data[0]
}
method nextToken(){
# retrieve the next token from input
while (@character != "EOF"){
if (@character == " "){
@whitespace()
}elseif (@character == "{"){
@consume()
return new Token(TokenType.L_BRACE, "{")
}elseif (@character == "}"){
@consume()
return new Token(TokenType.R_BRACE, "}")
}elseif (@character == "["){
@consume()
return new Token(TokenType.L_BRACKET, "[")
}elseif (@character == "]"){
@consume()
return new Token(TokenType.R_BRACKET, "]")
}elseif (@character == ":"){
@consume()
return new Token(TokenType.COLON, ":")
}elseif (@character == ","){
@consume()
return new Token(TokenType.COMMA, ",")
}elseif (@character == '"'){
return @str()
}elseif (@isNumber()){
return @number()
}else{
return @keyword()
}
}
return new Token(TokenType.EOF, "")
}
method consume(){
# advance the index by one and fetch the next character
@index += 1
if (@index >= @data.size()){
@character = "EOF"
}else{
@character = @data[@index]
}
}
method whitespace(){
# match whitespace rules
while (@character == " "){
@consume()
}
}
method keyword(){
# match keyword rules
let value = ""
while (@character.isalpha()){
value += @character
@consume()
}
if (@isBool(value)){
return new Token(TokenType.LITERAL_BOOL, value)
}else{
raise JsonException("Unexpected Keyword")
}
}
method str(){
# match string rules
@consume()
let value = ""
while (@character != '"'){
value += @character
@consume()
}
@consume()
return new Token(TokenType.LITERAL_STR, value)
}
method isBool(value){
# determine if string is a boolean value
if (value == "true"){
return true
}elseif (value == "false"){
return true
}
return false
}
method isNumber(){
# determine if current character is a number
if (@character.isdigit()){
return true
}
return false
}
method isDecimal(value){
# determine if provided value is a decimal
each (let c : value){
if (c == "."){
return true
}
}
return false
}
method number(){
# match number rules
let value = ""
while(@isNumber() or @character == "."){
value += @character
@consume()
}
if (@isDecimal(value)){
return new Token(TokenType.LITERAL_DECIMAL, value)
}
return new Token(TokenType.LITERAL_INT, value)
}
}
###
Parser that takes tokens from the lexer and produces a Brio object.
###
class JsonParser {
method init(lexer){
@lexer = lexer
@p = 0
@tokens = []
}
method lt(i){
# fetch lookahead token at specified index
return @tokens[@p + i - 1]
}
method la(i){
# fetch lookahead token type at specified index
let token = @lt(i)
return token.type
}
method consume(){
# advance the token index by one
@p += 1
}
method match(token_type){
# consume token if it is the expected type
if (@la(1) == token_type){
@consume()
}else {
raise JsonException("Unexpected Token")
}
}
method parse(){
# fetch tokens from the lexer
let valid = true
while (valid){
let token = @lexer.nextToken()
if (token.type == TokenType.EOF){
valid = false
}else{
@tokens.push(token)
}
}
# parse JSON object or array
if (@la(1) == TokenType.L_BRACE){
return @object()
}elseif (@la(1) == TokenType.L_BRACKET){
return @list()
}else{
raise JsonException("Invalid JSON")
}
}
method object(){
# match object rules
let result = {}
@match(TokenType.L_BRACE)
while(@la(1) != TokenType.R_BRACE){
let key = @id()
@match(TokenType.COLON)
let value = @term()
result[key] = value
if (@la(1) == TokenType.COMMA){
@match(TokenType.COMMA)
}
}
@match(TokenType.R_BRACE)
return result
}
method list(){
# match list rules
let result = []
@match(TokenType.L_BRACKET)
while(@la(1) != TokenType.R_BRACKET){
let value = @term()
result.push(value)
if (@la(1) == TokenType.COMMA){
@match(TokenType.COMMA)
}
}
@match(TokenType.R_BRACKET)
return result
}
method id(){
# match id rules
if (@la(1) == TokenType.LITERAL_STR){
return @str()
}elseif (@la(1) == TokenType.LITERAL_DECIMAL){
return @dec()
}elseif (@la(1) == TokenType.LITERAL_INT){
return @int()
}
raise JsonException("Invalid JSON")
}
method term(){
# match term rules
if (@la(1) == TokenType.L_BRACE){
return @object()
}elseif (@la(1) == TokenType.L_BRACKET){
return @list()
}elseif (@la(1) == TokenType.LITERAL_BOOL){
return @bool()
}else{
return @id()
}
}
method str(){
# match str rules
let token = @lt(1)
@match(TokenType.LITERAL_STR)
return token.text
}
method int(){
# match int rules
let token = @lt(1)
@match(TokenType.LITERAL_INT)
return integer(token.text)
}
method dec(){
# match decimal rules
let token = @lt(1)
@match(TokenType.LITERAL_DECIMAL)
return decimal(token.text)
}
method bool(){
# match boolean rules
let token = @lt(1)
@match(TokenType.LITERAL_BOOL)
if (token.text == "true"){
return true
}else{
return false
}
}
}
###
Parses a string into a JSON object (dictionary or array).
###
method parse(text){
let lexer = new JsonLexer(text)
let parser = new JsonParser(lexer)
return parser.parse()
}
###
Stringifies a JSON object (dictionary or array).
###
method dump(data){
return string(data)
}