Bluish Coder

Programming Languages, Martials Arts and Computers. The Weblog of Chris Double.


2007-11-28

Embedded Grammars in Factor

I'm working on a new parser combinator library for Factor based on Parsing Expression Grammars and Packrat parsers. This is based on what I learnt from writing a packrat parser in Javascript.

It's progressing quite well and already fixes some problems in the existing parser combinator library I wrote. The main issue with that one is it's not tail recursive and some combinations of parsers can run out of call stack.

The new library is in the 'peg' vocabulary. I haven't yet implemented the packrat side of things though so it is slow on large grammars and inputs.

I've also done a proof of concept of something I've been meaning to do for awhile. That is writing a parser for EBNF (or similar) that produces Factor code in the form of parser combinators to implement the described grammar. The code for this is in the 'peg.ebnf' vocabulary. It allows you to embed an EBNF-like language and have Factor words generated for each rule:

<EBNF
digit  = '1' | '2' | '3' | '4' .
number = digit { digit } .
expr   = number {('+' | '-' ) number } .
EBNF>

This example would create three Factor words. 'digit', 'number' and 'expr'. These words return parsers that can be used as normal:

"123" number parse
"1" digit parse
"1+243+342" expr parse

The EBNF accepted allows for choice, zero or more repetition, optional (exactly 0 or 1), and grouping. The generated AST is pretty ugly so by default it works best as a syntax checker. You can modify the generated AST with action productions:

<EBNF
digit  = '1' | '2' | '3' | '4' => convert-to-digit .
number = digit { digit }       => convert-to-number .
expr   = number '+' number     => convert-to-expr .
EBNF>

An action is a factor word after the '=>'. The word receives the AST produced from the rule on the stack and it can replace that with a new value that will be used in the AST. So 'convert-to-expr' above might produce a tuple holding the expression values (by default, a sequence of terms in the rule are stored in a vector):

TUPLE: ast-expr lhs operator rhs ;
C: <ast-expr> ast-expr
: convert-to-expr ( old -- new )
  first3 <ast-expr> ;

The generated code is currently pretty ugly, mainly due to it being a quick proof of concept. I'll try doing a few grammars and tidy it up, changing the interface if needed, as I go along.

As an experiment I did a grammar for the PL/0 programming language. It's in 'peg.pl0'. The grammar from the wikipedia article is:

program = block "." .

block = [ "const" ident "=" number {"," ident "=" number} ";"]
        [ "var" ident {"," ident} ";"]
        { "procedure" ident ";" block ";" } statement .

statement = [ ident ":=" expression | "call" ident |
            "begin" statement {";" statement } "end" |
            "if" condition "then" statement |
            "while" condition "do" statement ].

condition = "odd" expression |
            expression ("="|"#"|"<"|"<="|">"|">=") expression .

expression = [ "+"|"-"] term { ("+"|"-") term}.

term = factor {("*"|"/") factor}.

factor = ident | number | "(" expression ")".</pre>The Factor grammar is very similar:<pre>: ident ( -- parser )
  CHAR: a CHAR: z range 
  CHAR: A CHAR: Z range 2array choice repeat1 
  [ >string ] action ;

: number ( -- parser )
  CHAR: 0 CHAR: 9 range repeat1 [ string>number ] action ;

<EBNF
program = block '.' .
block = [ 'const' ident '=' number { ',' ident '=' number } ';' ]
        [ 'var' ident { ',' ident } ';' ]
        { 'procedure' ident ';' [ block ';' ] } statement .
statement = [ ident ':=' expression | 'call' ident |
              'begin' statement {';' statement } 'end' |
              'if' condition 'then' statement |
              'while' condition 'do' statement ] .
condition = 'odd' expression |
            expression ('=' | '#' | '<=' | '<' | '>=' | '>') expression .
expression = ['+' | '-'] term {('+' | '-') term } .
term = factor {('*' | '/') factor } .
factor = ident | number | '(' expression ')'
EBNF>

This grammar as defined works and can parse PL/0 programs. I'll extend this as I improve the EBNF routines, adding actions, etc to generated a decent AST.

Tags: factor 

2007-11-21

Writing Web Applications in Factor

There was a request on #concatenative for information on how to write web applications in Factor. I went through a few steps on how to get started. I'm repeating it here for others that might be interested.

There are a number of different ways of writing web applications in Factor but for this approach I'm using the furnace framework.

The first step is to start the web server. This lives in the vocab 'http.server':

USE: http.server
[ 8888 httpd ] in-thread

This will start an instance of the server on port 8888 in another thread, to allow us to continue to enter commands in the listener.

By default web applications are accessed on the URL path /responder/name, where 'name' is the name of the web application.

Accessing the web application path runs an 'action'. An action produces HTML output which gets sent back to the client browser. A web application has a default 'action' that gets run (the equivalent of an index.html), and can have other actions that are specified in the URL. Some examples:

http://localhost:8888/responder/foo
Runs the default action for the 'foo' web application
http://localhost:8888/responder/foo/doit
Runs the 'doit' action
http://localhost:8888/responder/foo/hello?name=chris
Runs the 'hello' action giving the argument 'name' with the value 'chris'

The syntax for furnace URL's is therefore http://servername:port/responder/{webappname}/{action}?{arguments}

Furnace web application must exist under the 'webapps' vocabulary. So accessing /responder/foo will look for furnace details in the vocabulary 'webapps.foo'.

A furnace web application is registered with the http server using the 'web-app' word. It takes three arguments on the stack:

\ web-app effect-in . 
=> { "name" "default" "path" }

The 'name' is the vocabulary name of the web application with out the 'webapps.' prefix. 'default' is the name of the action that gets run when the web application URL is accessed. 'path' is the location of any template files the web application uses.

An action is a word that outputs data to be sent to the browser. It can be as simple as:

: doit ( -- ) serving-text "I am here" print ;

The word must be registered as an action:

\ doit { } define-action

Now accessing the URL for the web application with 'doit' at the end of the path will result in 'I am here' being sent to the browser. Note the 'serving-text' call. That outputs the headers for the mime type and the standard HTTP response. There is also a 'serving-html', or you could write the headers manually.

Actions can take arguments. These are placed on the stack for the word that is called:

: hello ( name -- ) serving-text "Hello " write print ;
\ hello { { "hello" } } define-action

So the complete code for the simplest of web applications is:

USE: http.server
[ 8888 httpd ] in-thread
IN: webapps.test
USE: furnace

: index serving-text "We're alive!" print ;
\ index { } define-action 

: hello ( name -- ) serving-text "Hello " write print ;
\ hello { { "name" } } define-action

"test" "index" "." web-app

Accessing http://localhost:8888/responder/test will run the 'index' action. This is what we passed as the 'default' parameter on the stack to the 'web-app' word. Accessing http://localhost:8888/responder/test/hello?name=chris will run the 'hello' action.

There is also the facility to have template files, very much like JSP. The 'path' parameter to 'web-app' defines the location of these. Inside your action word you can call 'render-template' to run the template and have it sent to the browser:

: runme ( -- ) f "page" "Title" render-template ;
\ runme { } define-action

This will load the 'page.furnace' file in the path given to 'web-app'. It should contain standard HTML with embedded Factor code inside <% and %> tags. It will be run and sent to the client. The 'f' passed in this example can be an instance of a tuple (an object) and the template can access the slots of that instance to display data, etc.

There is quite a bit more that can be done. There is a continuation based workflow system, validators for actions, etc. There is also much more that needs to be done. handling sessions, cookies, etc. Hopefully this post gives a quick introduction and allows you to get started.

Tags: factor 

2007-11-09

Opera has a new Video enabled build

Opera is making a call for video on the web, releasing an experimental build with video support modelled on the latest WHATWG specification.

Their post has some examples to try out and instructions on how to use the <video> element.

Their examples work quite well in the latest video enabled build of Firefox too. Thanks to help from Robert O'Callahan it now has support for the 'controls' attribute. There's no more need for JavaScript buttons to activate video. I've also implemented some of the events. This code is in the git repository.

Some very experimental binary builds if you want to try things out:

Be aware that these are builds from a random point in the Mozilla CVS tree, with the Video patch applied. I don't guarantee they'll work for much more than demonstrating video support and it's very likely to contain bugs. That said, I run these builds often.

Try out the demo's that Opera have done, or the one's on my test page.

It's very cool to see video support using patent free formats running on more than one browser, with simple HTML that can be embedded by anyone. Thanks Opera!

Tags: ogg 

2007-11-02

ECMAScript 4 Reference Implementation Updated

The reference implementation for ECMAScript 4 has been updated to M1 and is available for download. The source is available (written in SML), as well as binaries for Windows, OS X, and Linux.

Tags: javascript 

2007-11-01

Ikarus Scheme - native-code compiler for R6RS Scheme

Ikarus Scheme was announced on the comp.lang.scheme newsgroup. It's an open source R6RS Scheme system which does incremental native code compilation.

Ikarus is an optimizing compiler, so your Scheme code will run fast without the need to port hot spots to C "for performance". With an incremental compiler, you don't need a separate compilation step to make your program run fast. The best part is that the compiler itself is fast, capable of compiling thousands of lines of code per second.

More details at the Ikarus Scheme website.

Tags: scheme 


This site is accessable over tor as hidden service 6vp5u25g4izec5c37wv52skvecikld6kysvsivnl6sdg6q7wy25lixad.onion, or Freenet using key:
USK@1ORdIvjL2H1bZblJcP8hu2LjjKtVB-rVzp8mLty~5N4,8hL85otZBbq0geDsSKkBK4sKESL2SrNVecFZz9NxGVQ,AQACAAE/bluishcoder/-61/


Tags

Archives
Links