[Ometa] First pass at OMeta in OMeta2
Frank Shearar
frank.shearar at angband.za.org
Wed Jun 3 12:21:02 PDT 2009
I have implemented a first pass at an OMeta2 grammar of the legacy =
OMeta. (Yes, I am too lazy to convert by hand my several hundred rule =
Delphi grammar!)
Please find attached some code, with tests, that hopefully isn't =
unusably horrible.
I also extended OMeta2Compiler a bit. I added a new selector =
#compile:in:notifying:ifFail:using:. The last parameter of this selector =
is a reference to the kind of rules you'll be passing. In particular, we =
have
compile: origCode in: cls notifying: notify ifFail: failBlock
^ self compile: origCode in: cls notifying: notify ifFail: failBlock =
using: OMeta2RuleParser.
and
compileLegacy: origCode in: cls notifying: notify ifFail: failBlock
^ self compile: origCode in: cls notifying: notify ifFail: failBlock =
using: OMeta1RuleParser.
Lastly, OMeta2RuleParser and OMeta1RuleParser each have a class-side =
method called rootRule that returns the name of each "main" production: =
#production for OMeta1RuleParser, and #rule for OMeta2RuleParser.
The tests have one thing I don't like: they should really show that the =
OMeta1 rules produce the same parse tree as the OMeta2 rules by actually =
producing those trees in the test. So instead of, for instance,
testParseSymbol
self assertParses: ' #foo ' as: '#foo' using: #symbol.
self assertParses: '#foo123' as: '#foo123' using: #symbol.
self assertParses: '#''foo''' as: '#foo' using: #symbol.
the tests should really be saying something like
self assertParsesIdentically: ' #foo ' using: #symbol
where
assertParsesIdentically: aString and: a2String using: aRule
^ self assertEquals: (OMeta1RuleParser matchAll: aString with: =
aRule) collection: (OMeta2RuleParser matchAll: a2String with: aRule)
or something similar.
Like I say, it's a first pass. I've tested it against random rules in my =
Delphi grammar, but I've not attempted exhaustive testing. That's the =
next step.
frank
-------------- next part --------------
'From Squeak3.10.2 of ''5 June 2008'' [latest update: #7179] on 3 June 2009=
at 8:08:20 pm'!
Smalltalk renameClassNamed: #OMeta1RuleTranslator as: #OMeta1RuleParser!
OMeta2 subclass: #OMeta1RuleParser
instanceVariableNames: 'grammarClass locals'
classVariableNames: ''
poolDictionaries: ''
category: 'OMeta2'!
TestCase subclass: #TestOMeta1RuleParser
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'OMeta2-Tests'!
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 6/3/2009 15=
:19'!
application =3D
spaces $< name:prod argument*:args spaces $>
-> [OrderedCollection new add: #App; add: prod asSymbol; addAll: args; yo=
urself]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 6/3/2009 15=
:15'!
argument =3D
spaces hostLanguageTinyExpression! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/24/2009 1=
1:53'!
character =3D
spaces $$ char:x -> [{#App. #exactly. x storeString.}]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/24/2009 1=
1:53'!
expr =3D
expr4:x (token('|') expr4)*:xs -> [(OrderedCollection with: #Or) add: x; a=
ddAll: xs; yourself]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/25/2009 1=
2:08'!
expr1 =3D
application
| semanticAction
| semanticPredicate
| ( keyword('nil') | keyword('true') | keyword('false')
| number | string | character
| symbol ):x -> [{#App. #exactly. x}]
| token('"') (~$" char)*:cs $" -> [OrderedCollection new
add: #And;
addAll: (cs collect: [:c |
{#App. #exactly. c storeString}
]);
add: {#Action. (String withAll: cs) storeString};
yourself]
| token('(') expr:x token(')') -> [x]
| token('{') token('}') -> [{#Form. {#action. 'true'}}]
| token('{') expr:x token('}') -> [{#Form. x}]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/25/2009 1=
2:07'!
expr2 =3D
token('~') expr2:x -> [{#Not. x}]
| token('&') expr1:x -> [{#Lookahead. x}]
| expr1! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/25/2009 1=
2:09'!
expr3 =3D
expr2:r
( token('*') [{#Many. r}]:r
| token('+') [{#Many1. r}]:r
| empty )
( $: name:n [locals add: n asSymbol] [{#Set. n. r}]:r
| empty ) -> [r]
| token(':') name:n [locals add: n asSymbol] -> [{#Set. n. {#App. #an=
ything}}]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/25/2009 1=
2:05'!
expr4 =3D
expr3*:xs -> [OrderedCollection new add: #And; addAll: xs; yourself]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 6/3/2009 14=
:09'!
hostLanguageExpression =3D
foreign(O2SqueakRecognizer. #squeakExpr):r
(token('.') foreign(O2SqueakRecognizer. #squeakExpr):s [r, '. ', s]:r)*
-> [r]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 6/3/2009 15=
:15'!
hostLanguageTinyExpression =3D
foreign(O2SqueakRecognizer. #squeakExpr)! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/23/2009 2=
2:01'!
initialize
super initialize.
locals :=3D Set new.! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/30/2009 2=
2:05'!
keyword :xs =3D
spaces seq(xs) ~(letter | digit) -> [xs]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 6/3/2009 20=
:03'!
name =3D
spaces letter:x (letter | digit)*:xs -> [((String with: x), (String withAl=
l: xs)) asSymbol]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/23/2009 2=
2:04'!
number =3D
spaces digit+:ip
( $. digit+:fp -> [(String withAll: ip), '.', (String withAll: fp)]
| empty -> [String withAll: ip] )! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 6/3/2009 14=
:29'!
production =3D
&(name:name)
productionPart(name):x
(token(';') productionPart(name))*:xs
spaces end
-> [{#Rule. name asSymbol. locals asArray. (OrderedCollection with: #Or w=
ith: x) addAll: xs; yourself}]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/25/2009 1=
2:10'!
productionPart :requiredName =3D
name:name ?[name =3D requiredName]
expr4:body
( token('::=3D') expr:rhs -> [{#And. body. rhs}]
| empty -> [body] )! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/26/2009 2=
1:37'!
semanticAction
| e |
self ometaOr: {
[self apply: #token withArgs: {'=3D>'}].
[self apply: #token withArgs: {'!!'}].
}.
self apply: #token withArgs: {'['}.
e :=3D self apply: #hostLanguageExpression.
self apply: #exactly withArgs: {$]}.
^ {#Act. e}! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/23/2009 2=
2:06'!
semanticPredicate =3D
token('?[') hostLanguageExpression:r token(']') -> [{#pred. r}]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/23/2009 2=
2:18'!
string =3D
spaces $' (~$' char)*:s $' -> [(String withAll: s) printString]! !
!OMeta1RuleParser methodsFor: 'as yet unclassified' stamp: 'fbs 5/23/2009 2=
2:07'!
symbol =3D
spaces $#
( name
| string:s -> [s copyFrom: 2 to: s size - 1] ):n
-> [n asSymbol printString]! !
!OMeta1RuleParser class methodsFor: 'compiling' stamp: 'fbs 6/3/2009 14:45'!
rootRule
^ #production.! !
!OMeta2Compiler methodsFor: 'as yet unclassified' stamp: 'fbs 6/3/2009 14:4=
2'!
compile: origCode in: cls notifying: notify ifFail: failBlock
^ self compile: origCode in: cls notifying: notify ifFail: failBlock using=
: OMeta2RuleParser.! !
!OMeta2Compiler methodsFor: 'as yet unclassified' stamp: 'fbs 6/3/2009 14:4=
4'!
compile: origCode in: cls notifying: notify ifFail: failBlock using: rulePa=
rser
| origCodeStream parseTree structuredCode translatedCode |
origCodeStream :=3D origCode asString readStream.
self from: origCodeStream class: cls context: nil notifying: notify.
[
parseTree :=3D ruleParser matchStream: origCodeStream with: (ruleParser r=
ootRule) withArgs: #() withPlaybackDebugging: false.
parseTree :=3D OMeta2Optimizer match: parseTree with: #optimizeRule.
structuredCode :=3D OMeta2RuleTranslator match: parseTree with: #translat=
e withArgs: {cls}.
translatedCode :=3D OMeta2Flattener match: structuredCode with: #flatten
] on: OM2Fail do: [
self notify: '<-- parse error around here -->' at: origCodeStream positio=
n.
^ failBlock value
].
^ Compiler new
compile: translatedCode readStream
in: cls
notifying: notify
ifFail: failBlock! !
!OMeta2Compiler methodsFor: 'as yet unclassified' stamp: 'fbs 6/3/2009 14:4=
2'!
compileLegacy: origCode in: cls notifying: notify ifFail: failBlock
^ self compile: origCode in: cls notifying: notify ifFail: failBlock using=
: OMeta1RuleParser.! !
!OMeta2RuleParser class methodsFor: 'compiling' stamp: 'fbs 6/3/2009 14:46'!
rootRule
^ #rule.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 5/30/2009 21:19'!
testParseAnything
self assertParses: 'a' as: $a using: #anything.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 15:37'!
testParseApplication
self assertParses: '<token ''foo''>' as: {#App. #token. '''foo'''.} using:=
#application.
self assertParses: '<token foo>' as: {#App. #token. 'foo'.} using: #applic=
ation.
self assertParses: '<listOf #foo ''bar'' 2>' as: {#App. #listOf. '#foo'. '=
''bar'''. '2'} using: #application.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/2/2009 21:34'!
testParseCharacter
self assertParses: ' $c ' as: {#App. #exactly. 'c' first storeString.} usi=
ng: #character! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 15:50'!
testParseExpr1
self assertParses: '<token ''foo''>' as: {#App. #token. '''foo'''.} using:=
#expr1.
=
self assertParses: '=3D>[nil]' as: {#Act. 'nil'.} using: #expr1.
=
self assertParses: ' ?[3 + 4] ' as: {#pred. '3 + 4'.} using: #semanticPre=
dicate.
=
self assertParses: 'nil' as: {#App. #exactly. 'nil'} using: #expr1.
self assertParses: 'true' as: {#App. #exactly. 'true'} using: #expr1.
self assertParses: 'false' as: {#App. #exactly. 'false'} using: #expr1.
self assertParses: '123' as: {#App. #exactly. '123'} using: #expr1.
self assertParses: '''foo''' as: {#App. #exactly. '''foo'''} using: #expr1.
self assertParses: '$c' as: {#App. #exactly. {#App. #exactly. '$c'}} using=
: #expr1.
self assertParses: '#foo' as: {#App. #exactly. '#foo'} using: #expr1.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 15:50'!
testParseExpr1Braces
self assertParses: '{<rule>}'
as:
{#Form.
{#Or.
{#And.
{#App. #rule.}}}}
using: #expr1.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 09:41'!
testParseExpr1EmptyBraces
self assertParses: '{}'
as:
{#Form.
{#action. 'true'}}
using: #expr1.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 09:39'!
testParseExpr1Parens
self assertParses: '(#foo)'
as:
{#Or.
{#And.
{#App. #exactly. '#foo'}.}.}
using: #expr1.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 09:38'!
testParseExpr1String
self assertParses: '"foo"'
as:
{#And.
{#App. #exactly. '$f'}.
{#App. #exactly. '$o'}.
{#App. #exactly. '$o'}.
{#Action. '''foo'''}}
using: #expr1.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 09:29'!
testParseExpr2
self assertParses: '~$c' as: {#Not. {#App. #exactly. {#App. #exactly. '$c'=
}}} using: #expr2.
self assertParses: '&$c' as: {#Lookahead. {#App. #exactly. {#App. #exactly=
. '$c'}}} using: #expr2.
self assertParses: '$c' as: {#App. #exactly. {#App. #exactly. '$c'}} using=
: #expr2.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 20:03'!
testParseExpr3
self assertParses: '$c*' as: {#Many. {#App. #exactly. {#App. #exactly. '$c=
'}}} using: #expr3.
self assertParses: '$c+' as: {#Many1. {#App. #exactly. {#App. #exactly. '$=
c'}}} using: #expr3. =
self assertParses: '$c' as: {#App. #exactly. {#App. #exactly. '$c'}} using=
: #expr3.
=
self assertParses: '<foo>:n' as: {#Set. #n. {#App. #foo.}} using: #expr3.
=
self assertParses: ':n' as: {#Set. #n. {#App. #anything.}} using: #expr3.
! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 13:17'!
testParseExpr4
self assertParses: '' as: {#And.} using: #expr4.
self
assertParses: '$c* $c+'
as:
{#And.
{#Many. {#App. #exactly. {#App. #exactly. '$c'}}}.
{#Many1. {#App. #exactly. {#App. #exactly. '$c'}}}}
using: #expr4.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 14:10'!
testParseHostLanguageExpression
self assertParses: '3 + 4' as: '3 + 4' using: #hostLanguageExpression.
self assertParses: 'OrderedCollection new' as: 'OrderedCollection new' usi=
ng: #hostLanguageExpression.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 15:51'!
testParseHostLanguageTinyExpression
self assertParses: '(3 + 4)' as: '(3 + 4)' using: #hostLanguageTinyExpress=
ion.
self assertParses: '{3. 4.}' as: '{3. 4.}' using: #hostLanguageTinyExpress=
ion.
self assertParses: '[OrderedCollection new]' as: '[OrderedCollection new]'=
using: #hostLanguageTinyExpression.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 13:41'!
testParseKeyword
self assertParses: ' foo123 ' as: 'foo123' using: #keyword withArguments:=
{'foo123'}.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 20:03'!
testParseName
self assertParses: 'a' as: #a using: #name.
self assertParses: ' a ' as: #a using: #name.
self assertParses: 'foo1' as: #foo1 using: #name.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 5/30/2009 21:21'!
testParseNumber
self assertParses: ' 1' as: '1' using: #number.
self assertParses: '1234.5678' as: '1234.5678' using: #number.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 20:00'!
testParseProduction
self
assertParses: 'empty ::=3D =3D>[t]'
as: {#Rule. #empty. {}.
{#Or.
{#And.
{#And.}.
{#Or. {#And. {#Act. 't'}}}}}}
using: #production.
=
self
assertParses: 'name :a :b ::=3D <token a> <token b>'
as:
{#Rule. #name. #(#b #a).
{#Or.
{#And.
{#And.
{#Set. #a. {#App. #anything}}.
{#Set. #b. {#App. #anything}}}.
{#Or.
{#And.
{#App. #token. 'a'}.
{#App. #token. 'b'}}}}}}
using: #production.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 20:04'!
testParseProductionPart
self assertParses: 'name' as: {#And} using: #productionPart withArguments:=
{'name'}.
=
self
assertParses: 'name :a :b ::=3D <token a> <token b>'
as:
{#And.
{#And.
{#Set. #a. {#App. #anything}}.
{#Set. #b. {#App. #anything}}}.
{#Or.
{#And.
{#App. #token. 'a'}.
{#App. #token. 'b'}}}}
using: #productionPart withArguments: {'name'}.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 14:10'!
testParseSemanticAction
self assertParses: '=3D>[nil]' as: {#Act. 'nil'.} using: #semanticAction.
self assertParses: '!![nil]' as: {#Act. 'nil'.} using: #semanticAction.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 14:11'!
testParseSemanticPredicate
self assertParses: ' ?[3 + 4] ' as: {#pred. '3 + 4'} using: #semanticPred=
icate.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 5/30/2009 21:23'!
testParseString
self assertParses: ' ''foo'' ' as: '''foo''' using: #string.
self assertParses: '''I am a String''' as: '''I am a String''' using: #str=
ing.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 5/30/2009 21:25'!
testParseSymbol
self assertParses: ' #foo ' as: '#foo' using: #symbol.
self assertParses: '#foo123' as: '#foo123' using: #symbol.
self assertParses: '#''foo''' as: '#foo' using: #symbol.! !
!TestOMeta1RuleParser methodsFor: 'test-rules' stamp: 'fbs 6/3/2009 13:41'!
testParseToken
self assertParses: ' abc ' as: 'abc' using: #token withArguments: {'abc'}.=
! !
!TestOMeta1RuleParser methodsFor: 'private' stamp: 'fbs 5/28/2009 16:03'!
assertEquals: seqA collection: seqB
self assertEquals: seqA collection: seqB description: ''.! !
!TestOMeta1RuleParser methodsFor: 'private' stamp: 'fbs 6/3/2009 14:38'!
assertEquals: expected collection: received description: aString
"Assert that SequenceableCollections expected, receive each contain equal =
(not necessarily identical) data, recursively."
=
self assert: expected isCollection =3D received isCollection description: =
aString.
=
"Strings should not be equal to Symbols."
self assert: expected isSymbol =3D received isSymbol description: aString.
=
expected isCollection ifFalse:
[^ self assert: expected =3D received description: aString].
self assert: (expected size =3D received size) description: aString.
=
1 to: expected size do:
[:i | self
assertEquals: (expected at: i)
collection: (received at: i)
description: aString].! !
!TestOMeta1RuleParser methodsFor: 'private' stamp: 'fbs 5/30/2009 21:19'!
assertParses: inputString as: aCollection using: aRule
self
assertEquals: aCollection
collection: ((OMeta1RuleParser matcherOn: inputString readStream)
apply: aRule)
description: aRule asString.! !
!TestOMeta1RuleParser methodsFor: 'private' stamp: 'fbs 5/30/2009 21:26'!
assertParses: inputString as: aCollection using: aRule withArguments: args
self
assertEquals: aCollection
collection: ((OMeta1RuleParser matcherOn: inputString readStream)
apply: aRule withArgs: args)
description: aRule asString.! !
!TestOMeta1RuleParser methodsFor: 'test-self' stamp: 'fbs 6/3/2009 14:38'!
testAssertEqualsCollectionDescription
self assertEquals: #() collection: #() description: 'empty collection'.
self assertEquals: #() collection: OrderedCollection new description: 'emp=
ty collection, different species'.
self assertEquals: 'foo' collection: #($f $o $o) description: 'strings and=
collections'.
self
should: [self assertEquals: 'foo' collection: #foo description: 'Strings =
<> Symbols']
raise: TestResult failure
withExceptionDo: [:unused|].! !
TestOMeta1RuleParser removeSelector: #testAssertEqualsCollection!
TestOMeta1RuleParser removeSelector: #testCharacter!
!TestOMeta1RuleParser reorganize!
('test-rules' testParseAnything testParseApplication testParseCharacter tes=
tParseExpr1 testParseExpr1Braces testParseExpr1EmptyBraces testParseExpr1Pa=
rens testParseExpr1String testParseExpr2 testParseExpr3 testParseExpr4 test=
ParseHostLanguageExpression testParseHostLanguageTinyExpression testParseKe=
yword testParseName testParseNumber testParseProduction testParseProduction=
Part testParseSemanticAction testParseSemanticPredicate testParseString tes=
tParseSymbol testParseToken)
('private' assertEquals:collection: assertEquals:collection:description: as=
sertParses:as:using: assertParses:as:using:withArguments:)
('test-self' testAssertEqualsCollectionDescription)
!
!OMeta2RuleParser class reorganize!
('as yet unclassified' isOMeta2Rule:)
('compiling' rootRule)
!
OMeta2RuleParser removeSelector: #rootRule!
!OMeta2RuleParser reorganize!
('as yet unclassified' application args characterLiteral characters expr ex=
pr1 expr2 expr3 expr4 initialize keyword name nameFirst nameRest nsName num=
berLiteral optIter rule rulePart semanticAction semanticPredicate space squ=
eakExpression stringLiteral symbolLiteral tokenSugar)
!
!OMeta1RuleParser class reorganize!
('compiling' rootRule)
!
OMeta1RuleParser removeSelector: #rootRule!
OMeta1RuleParser removeSelector: #token!
!OMeta1RuleParser reorganize!
('as yet unclassified' application argument character expr expr1 expr2 expr=
3 expr4 hostLanguageExpression hostLanguageTinyExpression initialize keywor=
d name number production productionPart semanticAction semanticPredicate st=
ring symbol)
!
More information about the OMeta
mailing list