Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Renamed the grammar to match the language´s name and modified README.

  • Loading branch information...
commit 11041ade7e60ff2f76465a1044f40786e3a5d405 1 parent 0874855
thoefer2 thoefer2 authored

Showing 4 changed files with 567 additions and 9 deletions. Show diff stats Hide diff stats

  1. +4 4 Makefile
  2. +98 0 Mocca.g
  3. +451 0 MoccaWalker.g
  4. +14 5 README.md
8 Makefile
... ... @@ -1,12 +1,12 @@
1 1 parser:
2   - export CLASSPATH=/Users/tom/tom/FHR/MA/code/bin/antlr-3.3-complete.jar:$$CLASSPATH && java org.antlr.Tool Ex1Test.g Ex1Walker.g
  2 + export CLASSPATH=/Users/tom/tom/FHR/MA/code/bin/antlr-3.3-complete.jar:$$CLASSPATH && java org.antlr.Tool Mocca.g MoccaWalker.g
3 3
4 4 minify:
5 5 java -jar tools/compiler-latest/compiler.jar \
6 6 --js=`pwd`/antlr3-all.js \
7   - --js=`pwd`/Ex1TestLexer.js \
8   - --js=`pwd`/Ex1TestParser.js\
9   - --js=`pwd`/Ex1Walker.js\
  7 + --js=`pwd`/MoccaLexer.js \
  8 + --js=`pwd`/MoccaParser.js\
  9 + --js=`pwd`/MoccaWalker.js\
10 10 --js=`pwd`/jquery-1.8.3.min.js\
11 11 --js=`pwd`/main.js \
12 12 --js_output_file=`pwd`/mocca-lang.js
98 Mocca.g
... ... @@ -0,0 +1,98 @@
  1 +grammar Mocca;
  2 +
  3 +options {
  4 + language=JavaScript;
  5 + output=AST;
  6 +}
  7 +
  8 +tokens {
  9 + FN_CALL;
  10 + FN_DEF;
  11 + FN_DEF_INLINE;
  12 + EX;
  13 +}
  14 +
  15 +@members {
  16 +
  17 +}
  18 +
  19 +prog: exprStmt* EOF -> exprStmt*
  20 + ;
  21 +
  22 +exprStmt: ID ':' expr ';' -> ^(':' ID expr)
  23 + | name=ID ':' '{' (arg+=ID)? (',' arg+=ID)* '=>' body+=exprStmt* '}' ';' -> ^(FN_DEF $name $arg* $body*)
  24 + | name=ID ':' '=>' inline=exprStmt -> ^(FN_DEF_INLINE $name $inline)
  25 + | expr ';' -> ^(EX expr)
  26 + ;
  27 +
  28 +expr: (exprAnd -> exprAnd)
  29 + (
  30 + ('||' e=exprAnd -> ^('||' $expr $e))
  31 + )*
  32 + ;
  33 +
  34 +exprAnd: (exprCompare -> exprCompare)
  35 + (
  36 + ('&&' e=exprCompare -> ^('&&' $exprAnd $e))
  37 + )*
  38 + ;
  39 +
  40 +exprCompare: (exprAdd -> exprAdd)
  41 + (
  42 + ('==' e=exprAdd -> ^('==' $exprCompare $e))
  43 + |
  44 + ('!=' e=exprAdd -> ^('!=' $exprCompare $e))
  45 + |
  46 + ('>' e=exprAdd -> ^('>' $exprCompare $e))
  47 + |
  48 + ('<' e=exprAdd -> ^('<' $exprCompare $e))
  49 + |
  50 + ('>=' e=exprAdd -> ^('>=' $exprCompare $e))
  51 + |
  52 + ('<=' e=exprAdd -> ^('<=' $exprCompare $e))
  53 + )?
  54 + ;
  55 +
  56 +exprAdd: (exprMult -> exprMult)
  57 + (
  58 + ('+' e=exprMult -> ^('+' $exprAdd $e))
  59 + |
  60 + ('-' e=exprMult -> ^('-' $exprAdd $e))
  61 + )*
  62 + ;
  63 +
  64 +exprMult:(unary -> unary)
  65 + (
  66 + ('*' e=unary -> ^('*' $exprMult $e))
  67 + |
  68 + ('/' e=unary -> ^('/' $exprMult $e))
  69 + )*
  70 + ;
  71 +
  72 +unary: atom
  73 + | '!' unary -> ^('!' unary) // points again to unary as one can chain multiple negations like !!!true;
  74 + ;
  75 +
  76 +// matching for BOOLEAN in atom means one can syntactically use TRUE in an arithmetic expression which
  77 +// doesn´t make much sense. Cases like this need to be catched in the semantic analysis phase.
  78 +atom: INTEGER
  79 + | ID
  80 + | BOOLEAN
  81 + | '(' expr ')' -> expr
  82 + | fn
  83 + | 'if' cond=expr ':' body=exprStmt -> ^('if' $cond $body)
  84 + ;
  85 +
  86 +fn: ID '(' e+=expr? (',' e+=expr)* ')' -> ^(FN_CALL ID $e*)
  87 +// | (ID '(' expr? (',' expr)* ')' '{')=>ID '(' x+=expr? (',' x+=expr)* ')' '{' body=exprStmt '}' -> ^(FN_CALL_LAZY ID $x* $body)
  88 + ;
  89 +
  90 +BOOLEAN: ('T'|'F');
  91 +ID: CHAR (CHAR|'0'..'9'|'.')*;
  92 +INTEGER: '0'..'9'+;
  93 +STRING: '"' (.)* '"';
  94 +WS: (' '|'\t'|'\r'|'\n') { this.skip(); };
  95 +
  96 +fragment CHAR: 'a'..'z'|'A'..'Z';
  97 +
  98 +
451 MoccaWalker.g
... ... @@ -0,0 +1,451 @@
  1 +tree grammar MoccaWalker;
  2 +
  3 +options {
  4 + language=JavaScript;
  5 + tokenVocab=Mocca;
  6 + ASTLabelType=CommonTree;
  7 +}
  8 +
  9 +@members {
  10 +
  11 + L = {};
  12 +
  13 + L.Scope = function(name, enclosingScope) {
  14 + this.name = name;
  15 + this.enclosingScope = enclosingScope;
  16 + this.symbols = {};
  17 + };
  18 +
  19 + L.Scope.prototype.getScopeName = function() {
  20 + return this.name;
  21 + };
  22 + L.Scope.prototype.getEnclosingScope = function() {
  23 + return this.enclosingScope;
  24 + };
  25 + L.Scope.prototype.define = function(symbol) {
  26 + this.symbols[symbol.id] = symbol;
  27 + };
  28 + L.Scope.prototype.resolve = function(id) {
  29 + var lookup = this.symbols[id];
  30 + if(lookup !== undefined)
  31 + return lookup;
  32 + else if(this.getEnclosingScope() !== undefined)
  33 + return this.getEnclosingScope().resolve(id);
  34 + else
  35 + return undefined;
  36 + };
  37 +
  38 +
  39 +
  40 + L.Root = function() {
  41 + this.nodes = [];
  42 + this.interpret = function() {
  43 + this.nodes.forEach(function(node) {
  44 + node.interpret();
  45 + });
  46 + }
  47 + };
  48 +
  49 + L.Assignment = function(scope, id, node) {
  50 + this.scope = scope;
  51 + this.id = id;
  52 + this.node = node;
  53 + this.interpret = function() {
  54 + var value = this.node.interpret();
  55 + var sym = new L.SymVar(this.id, value);
  56 + this.scope.define(sym);
  57 + return value;
  58 + };
  59 + };
  60 +
  61 + L.Add = function(node1, node2) {
  62 + this.node1 = node1;
  63 + this.node2 = node2;
  64 + this.interpret = function() {
  65 + var a = node1.interpret();
  66 + var b = node2.interpret();
  67 + if(a.isInt() && b.isInt()) {
  68 + var result = new L.Value(a.toInt() + b.toInt(), L.T.INT);
  69 + return result;
  70 + }
  71 + throw new Error("<Add: Numeric operands expected. State: a="+a+", b="+b+">");
  72 + };
  73 + };
  74 +
  75 + L.Sub = function(node1, node2) {
  76 + this.node1 = node1;
  77 + this.node2 = node2;
  78 + this.interpret = function() {
  79 + var a = node1.interpret();
  80 + var b = node2.interpret();
  81 + if(a.isInt() && b.isInt()) {
  82 + var result = new L.Value(a.toInt() - b.toInt(), L.T.INT);
  83 + return result;
  84 + }
  85 + throw new Error("<Sub: Numeric operands expected. State: a="+a+", b="+b+">");
  86 + };
  87 + };
  88 +
  89 + L.Mult = function(node1, node2) {
  90 + this.node1 = node1;
  91 + this.node2 = node2;
  92 + this.interpret = function() {
  93 + var a = node1.interpret();
  94 + var b = node2.interpret();
  95 + if(a.isInt() && b.isInt()) {
  96 + var result = new L.Value(a.toInt() * b.toInt(), L.T.INT);
  97 + return result;
  98 + }
  99 + throw new Error("<Mult: Numeric operands expected. State: a="+a+", b="+b+">");
  100 + };
  101 + };
  102 +
  103 + L.Divide = function(node1, node2) {
  104 + this.node1 = node1;
  105 + this.node2 = node2;
  106 + this.interpret = function() {
  107 + var a = node1.interpret();
  108 + var b = node2.interpret();
  109 + if(a.isInt() && b.isInt()) {
  110 + var result = new L.Value(a.toInt() / b.toInt(), L.T.INT);
  111 + return result;
  112 + }
  113 + throw new Error("<Divide: Numeric operands expected. State: a="+a+", b="+b+">");
  114 + };
  115 + };
  116 +
  117 + L.Eq = function(node1, node2) {
  118 + this.node1 = node1;
  119 + this.node2 = node2;
  120 + this.interpret = function() {
  121 + var a = node1.interpret();
  122 + var b = node2.interpret();
  123 + if(a.isInt() && b.isInt()) {
  124 + var result = new L.Value(a.toInt() === b.toInt(), L.T.BOOLEAN);
  125 + return result;
  126 + } else if(a.isBoolean() && b.isBoolean()) {
  127 + var result = new L.Value(a.toBoolean() === b.toBoolean(), L.T.BOOLEAN);
  128 + return result;
  129 + }
  130 + throw new Error("<Eq: State: a="+a+", b="+b+">");
  131 + };
  132 + };
  133 +
  134 + L.Neq = function(node1, node2) {
  135 + this.node1 = node1;
  136 + this.node2 = node2;
  137 + this.interpret = function() {
  138 + var a = node1.interpret();
  139 + var b = node2.interpret();
  140 + if(a.isInt() && b.isInt()) {
  141 + var result = new L.Value(a.toInt() !== b.toInt(), L.T.BOOLEAN);
  142 + return result;
  143 + } else if(a.isBoolean() && b.isBoolean()) {
  144 + var result = new L.Value(a.toBoolean() !== b.toBoolean(), L.T.BOOLEAN);
  145 + return result;
  146 + }
  147 + throw new Error("<Neq: State: a="+a+", b="+b+">");
  148 + };
  149 + };
  150 +
  151 + L.Gt = function(node1, node2) {
  152 + this.node1 = node1;
  153 + this.node2 = node2;
  154 + this.interpret = function() {
  155 + var a = node1.interpret();
  156 + var b = node2.interpret();
  157 + if(a.isInt() && b.isInt()) {
  158 + var result = new L.Value(a.toInt() > b.toInt(), L.T.BOOLEAN);
  159 + return result;
  160 + }
  161 + throw new Error("<Gt: State: a="+a+", b="+b+">");
  162 + };
  163 + };
  164 +
  165 + L.Lt = function(node1, node2) {
  166 + this.node1 = node1;
  167 + this.node2 = node2;
  168 + this.interpret = function() {
  169 + var a = node1.interpret();
  170 + var b = node2.interpret();
  171 + if(a.isInt() && b.isInt()) {
  172 + var result = new L.Value(a.toInt() < b.toInt(), L.T.BOOLEAN);
  173 + return result;
  174 + }
  175 + throw new Error("<Lt: State: a="+a+", b="+b+">");
  176 + };
  177 + };
  178 +
  179 + L.Gte = function(node1, node2) {
  180 + this.node1 = node1;
  181 + this.node2 = node2;
  182 + this.interpret = function() {
  183 + var a = node1.interpret();
  184 + var b = node2.interpret();
  185 + if(a.isInt() && b.isInt()) {
  186 + var result = new L.Value(a.toInt() >= b.toInt(), L.T.BOOLEAN);
  187 + return result;
  188 + }
  189 + throw new Error("<Gte: State: a="+a+", b="+b+">");
  190 + };
  191 + };
  192 +
  193 + L.Lte = function(node1, node2) {
  194 + this.node1 = node1;
  195 + this.node2 = node2;
  196 + this.interpret = function() {
  197 + var a = node1.interpret();
  198 + var b = node2.interpret();
  199 + if(a.isInt() && b.isInt()) {
  200 + var result = new L.Value(a.toInt() <= b.toInt(), L.T.BOOLEAN);
  201 + return result;
  202 + }
  203 + throw new Error("<Lte: State: a="+a+", b="+b+">");
  204 + };
  205 + };
  206 +
  207 + L.And = function(node1, node2) {
  208 + this.node1 = node1;
  209 + this.node2 = node2;
  210 + this.interpret = function() {
  211 + var a = node1.interpret();
  212 + var b = node2.interpret();
  213 + if(a.isBoolean() && b.isBoolean()) {
  214 + var result = new L.Value(a.toBoolean() && b.toBoolean(), L.T.BOOLEAN);
  215 + return result;
  216 + }
  217 + throw new Error("<And: State: a="+a+", b="+b+">");
  218 + };
  219 + };
  220 +
  221 + L.Or = function(node1, node2) {
  222 + this.node1 = node1;
  223 + this.node2 = node2;
  224 + this.interpret = function() {
  225 + var a = node1.interpret();
  226 + var b = node2.interpret();
  227 + if(a.isBoolean() && b.isBoolean()) {
  228 + var result = new L.Value(a.toBoolean() || b.toBoolean(), L.T.BOOLEAN);
  229 + return result;
  230 + }
  231 + throw new Error("<Or: State: a="+a+", b="+b+">");
  232 + };
  233 + };
  234 +
  235 + L.Negate = function(node1) {
  236 + this.node1 = node1;
  237 + this.interpret = function() {
  238 + var a = node1.interpret();
  239 + if(a.isBoolean()) {
  240 + var result = new L.Value(!(a.toBoolean()), L.T.BOOLEAN);
  241 + return result;
  242 + }
  243 + throw new Error("<Negate: State: a="+a+">");
  244 + };
  245 + };
  246 +
  247 + L.Literal = function(value) {
  248 + this.value = value;
  249 + this.interpret = function() {
  250 + return this.value;
  251 + };
  252 + };
  253 +
  254 + L.Lookup = function(scope, id) {
  255 + this.scope = scope;
  256 + this.id = id;
  257 + this.interpret = function() {
  258 + var symbol = this.scope.resolve(this.id);
  259 + if(symbol !== undefined) {
  260 + return symbol.value;
  261 + }
  262 + throw new Error("<Lookup: invalid ID, "+this.id+">");
  263 + };
  264 + };
  265 +
  266 + L.FunctionDef = function(scope, id, localScope) {
  267 + this.scope = scope;
  268 + this.id = id;
  269 + this.args = [];
  270 + this.body = [];
  271 + this.localScope = localScope;
  272 + this.interpret = function() {
  273 + var that = this;
  274 + var sym = new L.SymVar(this.id, new L.Value({
  275 + args: this.args,
  276 + invoke: function(actualArgs) {
  277 + for(var i = 0; i < actualArgs.length; i++) {
  278 + that.localScope.define(new L.SymVar(that.args[i], actualArgs[i]));
  279 + }
  280 + var last;
  281 + for(i = 0; i < that.body.length; i++) {
  282 + var stmt = that.body[i];
  283 + last = stmt.interpret();
  284 + }
  285 + return last;
  286 + }
  287 + }, L.T.FN));
  288 + this.scope.define(sym);
  289 + return sym.value;
  290 + };
  291 + };
  292 +
  293 + L.FunctionCall = function(scope, id) {
  294 + this.scope = scope;
  295 + this.id = id;
  296 + this.args = [];
  297 + this.addArgument = function(arg) {
  298 + this.args.push(arg);
  299 + };
  300 + this.interpret = function(){
  301 + var argumentsValues = [];
  302 + this.args.forEach(function(arg) {
  303 + argumentsValues.push(arg.interpret());
  304 + });
  305 + var sym = this.scope.resolve(id);
  306 + if(sym !== undefined && sym.value.isFunction()) {
  307 + var fn = sym.value.toFunction();
  308 + if(this.args.length === fn.args.length) {
  309 + return fn.invoke(argumentsValues);
  310 + } else {
  311 + throw new Error("<FunctionCall: length of formal arguments != actual arguments>");
  312 + }
  313 + } else {
  314 + throw new Error("<FunctionCall: fn "+this.id+" is undefined or not a function>");
  315 + }
  316 + };
  317 + };
  318 +
  319 + L.T = { INT: 1, FN: 2, BOOLEAN: 3 };
  320 + L.Value = function(data, type) {
  321 + this.data = data;
  322 + this.type = type;
  323 + this.toInt = function() {
  324 + if(this.type === L.T.INT)
  325 + return parseInt(this.data);
  326 + throw new Error("Value: INT expected");
  327 + };
  328 + this.isInt = function() {
  329 + return this.type === L.T.INT;
  330 + };
  331 + this.toBoolean = function() {
  332 + if(this.type === L.T.BOOLEAN) {
  333 + return this.data;
  334 + }
  335 + };
  336 + this.isBoolean = function() {
  337 + return this.type === L.T.BOOLEAN;
  338 + };
  339 + this.toString = function() {
  340 + if(this.isBoolean())
  341 + return (this.toBoolean() ? "T" : "F") + " ("+this.typeString()+")";
  342 + return this.data+ " ("+this.typeString()+")";
  343 + };
  344 + this.isFunction = function() {
  345 + return this.type === L.T.FN;
  346 + };
  347 + this.toFunction = function(){
  348 + if(this.type === L.T.FN)
  349 + return this.data;
  350 + };
  351 + this.typeString = function() {
  352 + for(var prop in L.T) {
  353 + if(L.T[prop] === this.type)
  354 + return prop;
  355 + }
  356 + };
  357 + };
  358 +
  359 + L.SymVar = function(id, value) {
  360 + this.id = id;
  361 + this.value = value;
  362 + };
  363 +
  364 + L.GlobalScope = function(){
  365 +
  366 + L.Scope.call(this, "global", undefined);
  367 + this.define(new L.SymVar("puts", new L.Value({
  368 + args: [1],
  369 + invoke: function(args) {
  370 + var arg = args[0];
  371 + console.debug(arg.toString());
  372 + }
  373 + }, L.T.FN)
  374 + ));
  375 + this.define(new L.SymVar("assert", new L.Value({
  376 + args: [1],
  377 + invoke: function(args) {
  378 + var arg = args[0];
  379 + if(!arg.toBoolean() === true)
  380 + throw new Error("AssertionError");
  381 + }
  382 + }, L.T.FN)
  383 + ));
  384 +
  385 + };
  386 + L.GlobalScope.prototype = L.Scope.prototype;
  387 + this.currentScope = new L.GlobalScope();
  388 + this.oldScope;
  389 + this.pushScope = function() {
  390 + this.oldScope = this.currentScope;
  391 + this.currentScope = new L.Scope("block", this.oldScope);
  392 + return this.currentScope;
  393 + };
  394 + this.popScope = function() {
  395 + this.currentScope = this.currentScope.getEnclosingScope();
  396 + };
  397 +}
  398 +
  399 +
  400 +prog returns [node]
  401 +@init { var root = new L.Root(); }
  402 +@after { $node = root; }
  403 + : (e=exprStmt {root.nodes.push($e.node);})*
  404 + ;
  405 +
  406 +exprStmt returns [node]
  407 + : ^(':' ID expr) { $node = new L.Assignment(this.currentScope, $ID.text, $expr.node); }
  408 + | ^(FN_DEF name=ID {var node = new L.FunctionDef(this.currentScope, $name.text, this.pushScope()); } (arg=ID {node.args.push($arg.text);} )* (e=exprStmt { node.body.push($e.node); })* ) {
  409 + $node = node;
  410 + this.popScope();
  411 + }
  412 + | ^(EX expr) { $node = $expr.node; }
  413 + | ^(FN_DEF_INLINE name=ID {var node = new L.FunctionDef(this.currentScope, $name.text, this.pushScope()); } (e=exprStmt { node.body.push($e.node); }) ) {
  414 + $node = node;
  415 + this.popScope();
  416 + }
  417 + ;
  418 +
  419 +expr returns [node]
  420 + : ^('+' a=expr b=expr) { $node = new L.Add($a.node, $b.node); }
  421 + | ^('-' a=expr b=expr) { $node = new L.Sub($a.node, $b.node); }
  422 + | ^('*' a=expr b=expr) { $node = new L.Mult($a.node, $b.node); }
  423 + | ^('/' a=expr b=expr) { $node = new L.Divide($a.node, $b.node); }
  424 + | ^('==' a=expr b=expr) { $node = new L.Eq($a.node, $b.node); }
  425 + | ^('!=' a=expr b=expr) { $node = new L.Neq($a.node, $b.node); }
  426 + | ^('>' a=expr b=expr) { $node = new L.Gt($a.node, $b.node); }
  427 + | ^('<' a=expr b=expr) { $node = new L.Lt($a.node, $b.node); }
  428 + | ^('&&' a=expr b=expr) { $node = new L.And($a.node, $b.node); }
  429 + | ^('||' a=expr b=expr) { $node = new L.Or($a.node, $b.node); }
  430 + | ^('>=' a=expr b=expr) { $node = new L.Gte($a.node, $b.node); }
  431 + | ^('<=' a=expr b=expr) { $node = new L.Lte($a.node, $b.node); }
  432 + | ^('!' a=expr) { $node = new L.Negate($a.node); }
  433 + | ^(FN_CALL ID {var node = new L.FunctionCall(this.currentScope, $ID.text);} (e=expr {node.addArgument($e.node);})*) { $node = node; }
  434 + | i=INTEGER {
  435 + var valueInt = new L.Value($i.text, L.T.INT);
  436 + $node = new L.Literal(valueInt);
  437 + }
  438 + | i=ID {
  439 + var node = new L.Lookup(this.currentScope, $i.text);
  440 + $node = node;
  441 + }
  442 + | i=BOOLEAN {
  443 + var node = new L.Literal(new L.Value($i.text === "T", L.T.BOOLEAN));
  444 + $node = node;
  445 + }
  446 + ;
  447 +
  448 +
  449 +
  450 +
  451 +
19 README.md
Source Rendered
... ... @@ -1,19 +1,28 @@
1 1 The Mocca Programming Language
2 2 ==============================
3 3
4   -Actually to early for a precise description, but try it nonetheless:
  4 +Actually it´s much to early for a more precise description, but I try it nonetheless:
5 5
  6 +* dynamically typed
  7 +* interpreted within JavaScript, no compilation.
6 8 * int, boolean and function data types
7 9 * expression evaluation respective correct precedence and associativity
8 10 * variable definitions and usage in expressions
9 11 * lexical scoping
10   -* functions behave like closures
  12 +* functions capture their definition context (aka "closures")
11 13 * a function call returns the last statement per default
12 14 * a few semantic runtime checks
13 15
14   -No flow control structures atm.
15   -
16   -See index.html for a few code samples. Basically it´s quite easy to get started: include mocca-lang.js and type your code in text/mocca script blocks.
  16 +Mocca is experimental at the moment but there are some things on my mind
  17 +I want to evaluate:
17 18
  19 +* use native JavaScript and JavaScript libraris from within Mocca
  20 +* direct integration of "standard libraries" in the language. That means
  21 + something like "jQuery as language construct"
  22 +* extending Mocca with library functions that are indistinguishable from
  23 + builtin keywords
  24 +* programs as data (aka "metaprogramming")
  25 +* code generation for JavaScript (and another language?!)
18 26
  27 +See index.html for a few code samples.
19 28

0 comments on commit 11041ad

Please sign in to comment.
Something went wrong with that request. Please try again.